Quantcast
Channel: PHP – Xlinesoft Blog
Viewing all 97 articles
Browse latest View live

Using field events

$
0
0

We have two tables: categories and sub_categories. CategoryID is a two-digit category code.



In sub_categories table SubID consists of two-digit category code and and two-digit subcategory code.

What we need to do is to simplify adding a large number of subcategories. When user types in first two digit (category) we want to show category name underneath and also calculate the next subcategory number. We will do that using field events.

This is how it works in generated application:

Code

First we need to create Field Event for SubID field. We will use 'editing' event here which will be called every time the content of field is changed.

ClientBefore:

var val = this.getValue();
if (val.length==2)
  params["val"]=val;
else
 return false;

In ClientBefore code we check the length of entered code and only send it to server side when code is two digits long.

Server:

$result["catName"] = DBLookup("select CategoryName from apps_categories where CategoryID='".$params["val"]."'");
$sub = DBLookup("select max(substring(SubID,3,2))+1 from apps_sub_categories where left(SubID,2)='".$params["val"]."'");
$result["newCat"] = $params["val"].str_pad($sub,2,"0",STR_PAD_LEFT);

On Server side we pull CategoryName from categories table and also calculate next subcategory ID. We pass both category name and new subcategory code back to ClientAfter event.

ClientAfter:

$("#sub_tip").remove();
$("input[id=value_SubID_1]").after("
"+result["catName"]+"
"); ctrl.setValue(result["newCat"]);

In this event we remove previous tooltip with category name and add a new one. We also make it appear in blue color. Then we set the value of SubID with new subcategory code we received from the Server event.


Version 10. Page designer

$
0
0

It is about time we talk about version 10 of PHPRunner, ASPRunner.NET and ASPRunnerPro. We expect beta version to be available in November and stable version will be released before the end of the year.

Make sure that your software maintenance is current. Otherwise you will have to purchase version 10 at the full price.

The most interesting new feature in this update is Page Designer which will complement and eventually will replace the Visual Editor. This is quite a big change and before we talk about new functionality I need to explain why this change was required.


Visual Editor for many years was a primary way to customize the appearance of pages but we were hitting its limitations more and more often lately. We tried to introduce workarounds like Layout Editor and Free-Form mode but those were half measures.

  1. HTML is not a suitable medium for this job. When you mix HTML with your own template tags things tend to break.
  2. Visual Editor is powered by Internet Explorer editing engine which has minds on its own and doesn't always cooperate with us changing HTML behind the scene, shuffling HTML attributes etc.
  3. After switching to Bootstrap things like drag-n-drop in Visual Editor became even more difficult and we were often had to suggest make certain changes right in HTML code which sort of defeats the purpose of WYSIWYG.

Page Designer is a grid-based design tool that is supposed to take care of above issues plus adds more functionality.

  1. Multiple pages of the same type i.e. two or three list pages based on the same table. Previously you had to create a custom view or two that were more difficult to manage and also created unnecessary pages.

    This will be useful in many scenarios. For instance you need to display a button on the list page but don't need to see it when the same list page is shown in dashboard. Or you need to design different list pages with different set of columns for different users. Now you can do this.

  2. Page drafts. You can consider them working page copies. If page is marked as a draft it won't appear in generated application. Using drafts you can quickly switch between two versions of the page.

    On a screenshot below you can see the default list page template (list), an additional list page layout (list1) and a draft (list2). It also shows you how easy you can add a field to grid and to search or to filter panel. No need to switch to another screen in the software to choose what fields will appear on each page.

  3. Cells and object properties. Made much easier in Page Designer to change visual properties of any cell, field or any other object.

  4. Grid types. You can switch between grid types keeping the rest of page intact.

  5. Controlling where buttons appear. Buttons can appear standalone or in dropdown. Here an example of moving 'Inline Add' and 'Delete' buttons to 'More' dropdown.

  6. Customizing mobile version behaviour. For any button, search or filter panel you can choose if will be shown in mobile mode or hidden in mobile mode or only visible when mobile menu is open. There also will be an option to create a separate page layout for mobile mode.

Version 10. What takes so long?

$
0
0

First of all - we are still working on version 10 and getting closer every day. We are now close enough to publish a date that is more or less accurate. We expect beta version to be available March 10-15, 2018.

What takes so long? Apparently we underestimated the scope of work required. Also we have seen lots of potential in new functionality we adding and it didn't make sense to stop halfway. We originally planned to have Page Designer as an addition that will help design forms like Add, Edit and View. Then we quickly realized that it should be applied to all other pages. Then it become obvious that it should completely replace Visual Editor (we'll keep it available for old projects). More than that, some screens in wizard like 'Fields order and totals' also become obsolete and will be eliminated. We are moving towards a real IDE.

So I want to thank you everyone for being patient! I hope it worth the wait.

A little bit of history. We made lots of mistakes on the way. To be honest I'm surprised we made it that far. Our first product was ASPRunnerPro, then we developed ASP.NET version. PHP was seen as a language that mostly appeals to Linux enthusiasts and there is no money there. PHPRunner was the last one to be released and it quickly became our bestseller.

First versions of Runner family were quite simplistic and didn't offer much options to modify the visual appearance. The first attempt to change that was template editor. You could modify source files to change the appearance. That wasn't very useful. If you modify edit.php file, for instance, those changes to be applied to all Edit pages in your app. If you upgrade to a newer version you have to discard all changes made in previous version.

The next step was the separation of server side code and visual templates. This allowed us to introduce a Visual Editor that was powered by Internet Explorer editing engine. It worked fairly well but there issues that were not possible to fix. We tried band-aid solutions like Free-form mode and they brought as many new issues as they fixed.

We certainly hope that Page Designer is a bold step in the right direction. We no longer mess with HTML. Page Designer offers Excel-like grid to position and move page elements. HTML templates are generated on the fly when you build your project. No HTML in Visual Editor - impossible to break the template. Win-win.

In software development sometimes you cannot just keep piling new features on the top of outdated base. Once in a while you need to stop and lay a new foundation. It takes time but benefits all of us on the long run.

Joel Spolsky once said that Good software takes ten years to build. Well, it is easy for a smart guy like Joel. We already spent almost fifteen years building our software and just getting closer. We would not make it that far without your help. Thank you!

Troubleshooting web applications

$
0
0

This article will teach you how to troubleshoot your web applications like a pro. Most of these tips are specific to PHPRunner, ASPRunner.NET and ASPRunnerPro but some of them apply to all web applications.

General troubleshooting tips

This is where you start when something doesn't work as expected.

  • Make sure that you run the latest build of the software
  • Synchronize database and project
  • Perform a full project build
  • If you test your project on remote web server make sure you upload all generated files
  • Clear browser's cache. In Chrome or Firefox press Ctrl+Shift+Del to open "Clear browsing data" dialog. Choose to delete cached images and files.

  • If above doesn't help test your app on an alternative web server. For instance, if it doesn't work on your web server you can try Demo Account or a built-in web server that comes with the software. This can help you figure out if something is wrong with the project itself or with web server configuration.
  • Just in case test it using another web browser. Normally clearing browser cache helps to fix issues like this but sometimes your issue can be browser-specific. Also some browser plugins may interfere with your web application functionality. Switching to another browser can help eliminate this possibility.
  • If you use CDN or a cloud-based service like Azure you may want to create a new project there to quickly test it. Cloud services add their own caching layer so you maybe still looking at an old copy of files even after uploading a changed project.

You done all this and nothing helps. Time to dig a bit deeper.

There is an error message

You see an error message on the page. This is good, it gives us something to work with. As a first step Google the error message. You will be surprised how often searching for an error message points you in the right direction.

Here is an example:

Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLICIT) for operation '='

Searching for this error message points us to this article on Stackoverflow. The best answer offers a few options to fix this issue, but option four is the easiest one. Altering all database tables to use the same collation is an easy fix and requires no changes in the code.

Another error message you nay encounter.

Fatal error: Uncaught Error: Class 'ZipArchive' not found in /home/groupe/public_html/dynaco/plugins/PHPExcel/Writer/Excel2007.php:226 
Stack trace: 
#0 /home/groupe/public_html/dynaco/include/export_functions_excel.php(164): PHPExcel_Writer_Excel2007->save('/home/groupe/pu...') 
#1 /home/groupe/public_html/dynaco/include/export_functions.php(125): ExportExcelSave('Localisations.x...', 'Excel2007', Object(PHPExcel)) 
#2 /home/groupe/public_html/dynaco/classes/exportpage.php(206): ExportToExcel(Object(mysqli_result), 0, Object(eventsBase), Object(RunnerCipherer), Object(ExportPage)) 
#3 /home/groupe/public_html/dynaco/classes/exportpage.php(188): ExportPage->exportTo('excel2007', Object(mysqli_result), 0) 
#4 /home/groupe/public_html/dynaco/classes/exportpage.php(65): ExportPage->exportByType() 
#5 /home/groupe/public_html/dynaco/Localisations_export.php(87): ExportPage->process() 
#6 {main} 
thrown in /home/groupe/public_html/dynaco/plugins/PHPExcel/Writer/Excel2007.php on line 226

We do not need to search for the whole error message as it refers to specific files and line numbers. Just copy the generic part of this error message.

Fatal error: Uncaught Error: Class 'ZipArchive' not found in

The very first result points to Stackoverflow article that provides an answer:

For the ZipArchive class to be present, PHP needs to have the zip extension installed.
See this page for installation instructions (both Linux and Windows).

There is no error message

This one is going to be more complicated. As a first step check if there are any Javascript errors behind the scene. This article can help.

Login and security errors

You enter correct username and password but getting 'Invalid login' message. A few things may go wrong.

  • If email account activation is turned on make sure account is activated. Usually there is a column named 'active' in login table that holds value of 1 for active users.
  • If password encryption is turned on make sure that password is encrypted. When you turn on this option existing passwords stay in plain text and this is why login may not work.
  • You use Advanced Security option 'Users can see and edit their own data only' but users can see all data. Make sure that user doesn't belong to an admin group.

Welcome/menu page issues

Login works but menu is missing some tables or is completely empty.

  • Make sure that current user has permissions assigned. If you added new table to the project make sure you assigned permissions for this table to all user groups.
  • check events like ModifyMenuItem or AfterSuccessfulLogin. Remove code from those events temporarily to see if this fixes the issue.
  • Reset welcome page in Visual Editor. This is unlikely to fix anything but we need to eliminate all possibilities

If all of above doesn't help it is time to contact support.

Using multi-column design in version 10

$
0
0

Version 10 makes creating multi-column forms easier. In previous versions you needed to select layout on Style Editor page. In version 10 you can do this right in Page Designer and you can change it at any time without resetting the page. Changes you made to your page will no be lost.

In this article we use Customers table from Northwind database. In Page Designer select Edit page. Here is how it looks by default (all images are clickable):


Now we click Form Layout button and choose two- or three-column layout.

So far so good, we have a three-column form. Now lets add a tab control to this page and drag some fields there.

Select first tab by clicking on its label. Click Form Layout again and choose two-column layout. Select second tab, click Form Layout and choose three-column layout. Rearrange fields by dragging them to different cells if required.

This is pretty much it. Now it's time to build your project and see how this form looks in web browser.

I would also like to show an alternative method of building multicolumn forms. Add a new Edit page. Put cursor into container with form fields so it becomes selected. Click 'Split vertically' button on the right, drag some fields to the new cell.

You can split one of cells to achieve three-column layout or do the same thing with tabs or section. Just make sure you are selecting a cell where database fields reside before splitting it.

Now it can be a good time to change visual appearance of edit form a bit. You can select different cells and change fonts, colors, alignments and more. Just pay attention to what is currently selected in Page Designer. Options on the right will be different depending on selection. Here is just an example of what you can do.

PHPRunner 10 and ASPRunner.NET 10 are here!

$
0
0

The wait is over. PHPRunner 10 and ASPRunner.NET 10 are here! ASPRunnerPro will be ready in 2-3 weeks.

If you purchased or upgraded PHPRunner, ASPRunnerPro or ASPRunner.NET less than one year ago logon to control panel at https://xlinesoft.com/dss/support.asp and find download links and registration keys under 'My purchases'. There should be 'Reg info' link next to your latest upgrade purchase.

Note: Software maintenance coverage needs to be continuous. If your last purchase or upgrade were more than one year ago you have till July 31, 2018 to renew your maintenance. After that date you will have to purchase PHPRunner, ASPRunner.NET or ASPRunnerPro at the full price.

New customer purchase links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

Upgrade/maintenance renewal links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

Trial version download links:
http://xlinesoft.com/download

Current limitations of version 10:

  • ASPRunnerPro will be ready in 2-3 weeks. It is fully functional, we just need to improve the performance of generated app
  • Business templates are not supported yet
  • Right-to-left languages are not supported yet
  • Dashboard search pages not implemented yet

This all will be implemented very soon.

Here is the list of new features in version 10.

1. Page designer

Easy upgrade of pages modified in previous versions of the software. No longer use HTML as a page source in designer, no merges, nothing to break. Form building made easy, changes made in Page Designer will not conflict with project settings.

2. Multiple pages of the same type

You can have multiple Add or List pages that belong to the same table. A typical use case scenario: create two Add pages, insert a copy of Add button, in second Add button properties make it point to the second Add page. Then you can use code in events to hide one of buttons

3. Easily change properties of any page element

Change properties of page elements like buttons or links i.e.- color, font, label, what page to open, open in popup or as a standalone page. Choose when to show element: always, in desktop mode, in mobile mode, in expanded mobile view only.

4. Use regular or condensed Bootstrap schemes

Condensed schemes are great when you need to fit lots of info on the page.

Enjoy!

Building a hotel reservation system

$
0
0

Lets say you run a mini-hotel and need to build a very simple reservation system. In this article we will show how avoid double-booking only showing the rooms that are available for selected date range. Similar approach can be applied to any other reservation system i.e. if you need to build conference rooms reservation app.

In our database we need two tables, Rooms and Reservations.

Rooms table simply stores a list of rooms.

Each reservation is a record in Reservations table.

We can see that if someone comes and tries to reserve a room for one night, September 14-15, they should only see rooms 17 and 33. Rooms 23 and 27 are already booked for these days.

If we were to write this kind of select query manually this is how it supposed to look:

The only problem is that this query needs to be dynamic and needs to change based on DATEFROM and DATETO fields. SQL variables come to the rescue. In RoomID Lookup wizard we can use SQL variables in WHERE clause. This is how it is going to look:

And as a text that you can copy and paste to your project:

id not in ( select roomid from reservations where not
	 (':dateto' < datefrom OR ':datefrom' > dateto) )

This is all the code you need. And it works the same way in PHPRunner, ASPRunner.NET and ASPRunnerPro.

Here is how it looks in generated application:

More about SQL variables:
PHPRunner manual
ASPRunner.NET manual
ASPRunnerPro manual

And here is the live demo project. Go to Add Reservation page and play with dates to see how it works.

Using third party components

$
0
0

People ask us fairly often - how to incorporate some third party library or component into PHPRunner or ASPRunner.NET application keeping overall look and feel. Here is an example of integrating Tabulator component in version 10 of PHPRunner or ASPRunner.NET.

  1. Download the latest version of Tabulator. Unzip js and css folder to 'source' folder of your PHPRunner or ASPRunner.NET project.
  2. Proceed to List page of any table in Page Designer and removed page elements you don't need. Insert a code snippet into any container.  Here is an example of how it is going to look:
  3. Snippet PHP code (PHPRunner):
    echo'<link href="css/tabulator.min.css" rel="stylesheet">
    <script type="text/javascript" src="js/tabulator.min.js"></script>
    <div id="example-table"></div>';

    Snippet C# code (ASPRunner.NET):

    MVCFunctions.Echo(@"<link href='css/tabulator.min.css' rel='stylesheet'>
    <script type='text/javascript' src='js/tabulator.min.js'></script>
    <div id='example-table'></div>");
  4. BeforeDsiplay event code. Here we are retrieving data from Categories table in Northwind database.PHP code:
    $rs = DB::Query("select CategoryName, UnitsInStock, ProductName, UnitPrice, QuantityPerUnit from products p 
    inner join Categories c on c.CategoryID = p.CategoryID");
    $rows = array();
    while( $data = $rs->fetchAssoc() )
    {
    $rows[]=$data;
    }
    $pageObject->setProxyValue("tabledata", $rows);
    

    C# code:

    dynamic data;
    dynamic rs = DB.Query("select CategoryName, UnitsInStock, ProductName, UnitPrice, QuantityPerUnit from products p 
    inner join Categories c on c.CategoryID = p.CategoryID");
    dynamic rows = new List<dynamic>();
    while(data = rs.fetchAssoc())
    {
     rows.Add(data);
    }
    pageObject.setProxyValue("tabledata", rows);

    Make sure to update SQL query according to your database structure. SQL query can be as simple as select * from mytable

  5. Javascript OnLoad event of the same List page:
    var tabledata = proxy["tabledata"];var tabledata = proxy["tabledata"];
    var table = new Tabulator("#example-table", {    
    height:"600px",    
    layout:"fitColumns",    
    groupStartOpen: function(value, count, data, group){ return value=='Dairy Products'; },    
    movableRows:true, 
    data: tabledata,    
    groupBy:"CategoryName",    
    columns:[        
    {title:"Product", field:"ProductName", width:200},        
    {title:"Price", field:"UnitPrice", align:"right", width: 150},        
    {title:"In Stock", field:"UnitsInStock", align:"right", width:150},        
    {title:"QuantityPerUnit", field:"QuantityPerUnit", width:200},    
    ],
    });

    Make sure to change titles and field names accordingly.

 

And you can see how it works in this live demo.

 


Customizing project logo

$
0
0

Project logo (by default a project name) appears in the top left corner of the menu and points to the welcome page. In this article we'll show you ways to customize this project logo.

By default project logo is nothing but project name. Kind of looks boring.


1. Change project name

In version 10 proceed to "Editor" screen, double-click on Project Logo and edit it. Looks a little better.

2. Add an image

Double-click Project Logo again and insert image HTML code.

And here is how it looks in generated application:

HTML code for this:

<img src='https://yourwebsite.com/images/balloons.png'>Arizona Dreams

3. Changes via code

Project logo doesn't need to be static, you can display bit of useful info from the database there. For this purpose we can use Labels API. Code can be added to AfterAppInit event. This code shows today's sales.

PHPRunner:

$sales=DBLookup("select sum(UnitPrice*Quantity) from `Order details` od  
inner join Orders o where o.OrderID=od.OrderID and DATE(OrderDate) = CURDATE()");
Labels::setProjectLogo("Sales today: $".number_format($sales, 2, ".", ","));

ASPRunner.NET:

dynamic sales = tDAL.DBLookup(@"select sum(UnitPrice*Quantity) from `Order details` od 
inner join Orders o where o.OrderID=od.OrderID and DATE(OrderDate) = CURDATE()");
Labels.setProjectLogo(String.Format("Sales today: {0:c}", sales));

Generated application:

More info on setProjectLogo() function:

PHPRunner

ASPRunner.NET

ASPRunnerPro

Black Friday – Cyber Monday sale

$
0
0

Special offer: Buy one get one or more templates free

Time Remaining



Here is the deal:

  1. Renew PHPRunner, ASPRunnerPro or ASPRunner.NET maintenance - get one free template
  2. Purchase Professional Edition of PHPRunner, ASPRunnerPro or ASPRunner.NET maintenance - get two free templates
  3. Purchase Enterprise Edition of PHPRunner, ASPRunnerPro or ASPRunner.NET maintenance - get three free templates

Eligible templates to choose from:

To take advantage of this offer place an order for any eligible product and contact support team to claim your gift.

Creating Grid Tabs dynamically

$
0
0

Version 9.8 added an option to use Grid Tabs. Besides static tabs we also developed an API that allows to create and manage tabs from your code.

In this example we will use the typical Orders table to demonstrate how this API works. We will create a tab for each employee that lists her own orders only. This is how it is going to look in generated application.



This code needs to be added to List page: BeforeProcess event.

PHP

$sql = "select * from employees";
$rs = CustomQuery($sql);
while ($data = db_fetch_array($rs)) {

	$pageObject->addTab("EmployeeID='".$data["EmployeeID"]."'", 
			$data["FirstName"], $data["EmployeeID"]);
}

C#

string sql = "select * from employees";
XVar rs = tDAL.CustomQuery(sql);
XVar data;
while (data = CommonFunctions.db_fetch_array(rs)) {
	pageObject.addTab("EmployeeID='"+data["EmployeeID"].ToString()+"'",
		data["FirstName"], data["EmployeeID"].ToString());
}

ASP

sql = "select * from employees"
set rs = CustomQuery(sql)
do while bValue(DoAssignment(data,db_fetch_array(rs)))
   pageObject.addTab "EmployeeID='" & data("EmployeeID") & "'", _ 
			data("FirstName"), data("EmployeeID")
loop

Single use access codes

$
0
0

Lets say you need to build an application that provides one time access to some information. It can be some files you let your users download, or some game they want to play, or some video to watch. Instead of distributing some sort of usernames and passwords you want to make this experience as easy as possible and provide access based on a simple numeric code. Such code can sent via email or text messages or handed as scratch card similar to one below.



1. We are going to need a simple database table to store those codes. Besides the code code itself we also need to store the status. Status field is empty if code never been used and contains 'used' otherwise.

2. Add this table to project, enable Add page and proceed to Page Designer. This is the only page we going to need for now. Remove all elements that you don't need to see. We want to make this page as light as possible, we only need a title, an input box and a submit button. Make sure to change labels accordingly.

3. Proceed to Editor, right click on this Add page and make sure it is set as a Landing page.

4. Add the following code to Custom Add event (PHPRunner).

$select = array();
$select["code"] = $values["code"];

$rs = DB::Select("codes", $select);
if ($data = $rs->fetchAssoc() ) {
     if ($data["status"]=="used") {
       $error="Code was used already";
	 } else {
       DB::Exec("update codes set status='used' where id=".$data["id"]);
       $error="Code is valid. Ready to redirect";
       // login and or redirect code goes here 
       }
}
else {
	$error="Invalid code";
}
return false;

5. In this code we use Database API to find if such code exists. If it doesn't - we display the error message. If it exists but was used already - we will display another message. If code exists and wasn't used - we should allow user access to some sort of information. For now we are just saying that code is correct.

6. Next steps. For clarify purposes we don't do anything after code was verified. Here is a sample code that will logon user to your application and redirect them to some other page.

Security::loginAs("user");
header("Location: some_other_page.php");
exit();  

Note: if you add login to your project make sure you also enable Guest login and provide Guest users access to this Add page.

More info:
Database API
Security API

Using Invoice template as a checkout solution

$
0
0

If you purchased or upgraded PHPRunner, ASPRunner.NET or ASPRunnerPro recently you may have noticed that we are using a new checkout solution based on Invoice template. In this article we will show you steps required to add this kind of functionality to your project.

To see how it works live proceed to PHPRunner purchase page, select Edition, enter your email address, add more options on the next screen and click Continue.


Sample code is PHP only. We will add C# code soon too.

1. SMTP settings

Make sure you specified correct SMTP settings in PHPRunner/ASPRunner.NET. We will need this to send email receipts.

2. Change admin password

Build application, logon as admin/admin and change admin password.

3. Change Paypal/Stripe settings

Go to Settings in web application and provide Paypal and Stripe settings in order to use both payment methods.

4. Sending email receipts

Now there is something interesting. Proceed to Editor, create folder 'email' under 'Custom files', create subfolder 'english' there (of whatever language you use in your project). Create file named orderemail.txt there.

Here is the sample email text you can use. Note the use of %field_name% placeholders.

Xlinesoft order %invoice_number%: %item%

           Date: %date%
   Order Number: %invoice_number%
 Product Number: 32557-inv
  Product Title: %item%
       Quantity: %quantity%
     Unit Price: %item% US$ %price%
          Total: US$ %total%

          First: %buyer_info%
           Last: 
   Company Name: 
  Email Address: %buyer_email%
      Address 1: 
      Address 2: 
           City: 
 State/Province: 
Zip/Postal Code: 
        Country: 
          Phone: 

5. Custom invoice number generation

Many applications require to generate a custom invoice/transaction instead of plain numbers like 1,2,3 etc. You may notice that in our checkout solution Invoice numbers look like INV-X0000365. Here is how it works.

There is an extra table named invnumber that holds the latest order number. We get that number, increment it by one, format it the way we need it and update invoice_number table. Pretty straightforward.

This code can be found under Invoices->Add page->Process record values event.

$rs = CustomQuery("select invnumber from invoice_number");
$data = db_fetch_array($rs);
$values['invoice_number'] = "INV-X".str_pad($data["invnumber"]+1, 7, "0",STR_PAD_LEFT);
CustomQuery("update invoice_number set invnumber=invnumber+1");

6. Sending payment receipt (afterpay.php)

This is a part of the template and can be edited under Custom Files. Function named runAfterPay will be executed after a successful payment.

Here we update invoice as paid, set invoice payment date and also send the email receipt.

<?php
function runAfterPay($hash, $payid){
	// $hash - record id for update status field
	// $payid - Payment id from paypal or stripe
	if($hash && $payid){

			// mark invoice as paid
			$sql = "update invoices set status='paid', transaction_id = '".$payid."', paydate='".date("Y-m-d")."' where hash='".$hash."'";
			DB::Exec($sql);

			$email="support@xlinesoft.com";
				
			// get invoice id based on hash
			$params = array();
			$rs = DB::Query("select * from invoices where hash='".$hash."'");
			if ($data = $rs->fetchAssoc()) {

				$rs2 = DB::Query("select * from invoicedetails where id_invoice=".$data["id"]);
				while( $data2 = $rs2->fetchAssoc() ) 	{
						// send order email, one per item
						$data["price"]=$data2["price"];
						$data["quantity"]=$data2["quantity"];
						$data["item"]=$data2["item"];
						$data["total"]=$data2["total"];
					  RunnerPage::sendEmailByTemplate($email, "orderemail", $data);
				}
			}
			echo "ok";
		}
}
?>

Note the use of RunnerPage::sendEmailByTemplate() function. This function accepts the following parameters:

$email - email address to send email to.
"orderemail" - name of email template. Remember orderemail.txt file created on step 4? 
$data - array with data to replace placeholders in email template with the actual values.

7. API integration

Now we need to integrate it into our existing website. We provide REST API for this purpose. There are two files that come with Invoice template, apitest.php and apitest.cs that provide sample API usage code.

You will need to change $url variable - URL of Invoice add page on your website. Also you need to specify "your Authenticate key". This can be any unique text string that you can specify in Invoice project under Settings->API Authorization key.

<?php

//This API allows to create an invoice sending buyer and seller data and also a list of items, prices, taxes etc. 
//If invoice is created successfully it returns URL of the View page of this invoice.

require_once("include/dbcommon.php");
$url = "https://yourwebsite.com/invoice/invoices_add.php";
$res["company_name"]="Company Name";
$res["seller_info"]="Seller Info";
$res["buyer_info"] = "buyer name";
$res["buyer_address"] = "buyer address";
$res["buyer_email"] = "buyer@email.com";
$res["tax"]="10";
$res["terms"]="terms";
$res["invoice_name"]="Invoice Name";
$res["details"] = array();
for($i=1;$i<3;$i++){
	$details = array();
	$details["item"] = "item_".$i;
	$details["price"] = $i;
	$details["quantity"] = $i;
	$res["details"][] = $details;
}
$str = json_encode($res);
$parameters["invoice"] = $str;
$headers["WWW-Authenticate"] = "your Authenticate key";
$res = runner_post_request($url, $parameters, $headers);
if($res["error"])
	echo $res["error"];
else {
	if(strpos($res["content"],"http")>-1)
		echo "Invoice view";
	else
		echo $res["content"];
}
?>

PHPRunner 10.1 and ASPRunner.NET 10.1 are here!

$
0
0

If you purchased or upgraded PHPRunner, or ASPRunner.NET less than one year ago logon to control panel at https://xlinesoft.com/dss/support.asp and find download links and registration keys under 'My purchases'. There should be 'Reg info' link next to your latest upgrade purchase.

Note: Software maintenance coverage needs to be continuous. If your last purchase or upgrade were more than one year ago you have till February 28, 2019 to renew your maintenance. After that date you will have to purchase PHPRunner, ASPRunner.NET or ASPRunnerPro at the full price.

New customer purchase links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

Upgrade/maintenance renewal links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

What's new in version 10.1

1. New PDF export engine and options

We have switched to a new PDF engine powered by PDFMake. It is about 10 times faster and also has a smaller footprint (2Mb instead of 50Mb). So we are retiring DOMPDF and instead of converting HTML to PDF we create it from scratch using page structure supplied by Page Designer.

Now you can create PDF right from the List page, can have multiple PDF buttons creating different PDF layouts. You can export master and details to PDF together, you can export to PDF selected records and much more.

You can add "Create PDF" button to any page now. Do Insert->Standard button->Create PDF. Customize PDF options like orientation, scale, PDF file name etc. See all options on this screenshot. This is a really big day for all PDF lovers.

2. EU cookie consent banner

We all seen it a hundred of times. Now you can add it to your projects as well.

3. Minor fixes

  • fast style switching on Editor screen
  • fixed maps in PHP 7.x
  • Crosstab reports print page redesign
  • fixed Admin area pages appearance in dark styles like superhero
  • fixed Register page issues when changing form there
  • fixed inline add/edit default control sizes

4. Next steps

Right now we are working on adding RTL languages support and on releasing ASPRunnerPro 10.1. Also we are working on releasing versions of business templates that are compatible with version 10.x. Currently the following templates are compatible.

Survey template is the next one in the queue. Once all this done we will publish a more detailed roadmap.

Page Designer: Frequently Asked Questions Answered

$
0
0

Page Designer is one of the most amazing new features in version 10 but as any new functionality it also introduces a learning curve. In this article we are answering the most common questions people ask us about Page Designer usage.

If you have Page Designer questions that are not answered in this article feel free to ask here in comments or via helpdesk.

How to work with additional pages?

In version 10 you can add extra pages of the same type. For instance, besides the main edit page you can have extra edit pages named edit1, edit2 etc. The big question is how to use these additional pages in your application.

Changing button properties

Since we created multiple edit pages lets see how we can use those. On the List page select Edit button. On the right side using 'Page' dropdown select the page this button will open.


Using URL of extra pages in events

You may also need to know how to address extra pages manually i.e. in your event code. To access extra List page named list1 use the following in PHPRunner:

tablename_list.php?page=list1

And the same in ASPRunner.NET

/tablename/list?page=list1

What is not implemented yet

Shortly we will provide an option to choose extra pages in Menu Builder and also in Dashboards.

How to set Edit control width on Add/Edit page?

As a first step - proceed to that Add/Edit page in Page Designer and under Form Layout choose "separate labels and controls". Then you can modify settings of control and its label separately. Make edit control selected in Page Designer and set its width in pixels on the right side.

How to change List page column width?

As a first step - switch to Advanced Grid.

In Advanced Grid mode you can set width of individual grid columns. Select a column and set width in pixels using width setting on the right.

How to switch back to basic grid?

If you no longer need Advanced Grid you can switch back to Basic Grid under Grid Type.

How to insert a button into grid?

Switch to Advanced Grid first. Then do Insert->Custom Button->New Button. Then drag-n-drop you button to the grid or anywhere where it needs to appear.

How to hide a custom button?

Select the button you just added and check Item ID section on the right. It will show you code examples of how you can hide button conditionally both on server side and on client side in Javascript. Note that code examples already use correct Item ID and you can just copy and paste it to your code.

How to set width of inline edit control?

In Advanced Grid mode select the field itself and set its width in pixels on the right. This will applied to both field width in List mode and to Inline Edit control width.

What if I removed something and need add it back?

What if you accidentally removed the menu from your List page? You have two options here. Option 1 - create a new page of the same type and make it a default page. It works similar to 'Reset' function in older versions and gives you a brand new page. Not ideal though if you already applied a bunch of customizations to this page. In this case click outside of any page elements and see 'Re-insert deleted elements' button appear. By clicking this button you restore "essential" page elements like menu or breadcrumbs keeping other customizations intact.


Changing default tab order

$
0
0

Normally tab order is determined by the web browser based on your Add/Edit page structure. If you have a single row two-column form browser will tab through first cell edit controls first before switching to second cell.

If you place each control into its own cell it will tab through first row, then second, then third etc.

The point is that you want to be in control and you need to specify tab order that fits your application logic. Luckily it is easy to achieve placing the following code to Add/Edit page Javascript OnLoad event:

$("input[id^='value_CustomerID']").attr('tabindex', 1);
$("input[id^='value_ContactName']").attr('tabindex', 2);
$("textarea[id^='value_Address']").attr('tabindex', 3);
$("input[id^='value_Country']").attr('tabindex', 4);
$("input[id^='value_CompanyName']").attr('tabindex', 5);
$("select[id^='value_City']").attr('tabindex', 6);
$("input[id^='value_PostalCode']").attr('tabindex', 7);
$("input[id^='value_Phone']").attr('tabindex', 8);

Note that you need to account for edit control type here. For regular text edits use input, for dropdown boxes use select and for textareas use textarea. This code should work the same way in versions 8.x-10.x of all PHPRunner, ASPRunner.NET and ASPRunnerPro.

And here is the end result produced by this code.

Happy coding!

How to add a “Delete” button to each row in grid

$
0
0

By default PHPRunner/ASPRunner.NET provide "Delete selected" functionality. In some cases you need to be able to delete records individually. In this tutorial we will show how to add a delete button to each record. We will also show how to make it look as a regular edit/copy/view buttons.

1. Proceed to the List page in Page Designer and insert a button. Move it to one of grid columns. Set button style to "link-button" and choose icon "glyphicon-remove" as appears on a screenshot below.


2. Now lets edit button's code. We want to ask for a deletion confirmation, delete record and reload the page.

ClientBefore event:

if (confirm("Do you want to delete this record?"))
return true;
else 
return false;

In Server event you will need to specify correct table and key column name. In our case table name is categories and key column name is CategoryID.

Server event PHP code:

$record = $button->getCurrentRecord();
$data = array();
$data["CategoryID"] = $record["CategoryID"];
DB::Delete("categories", $data );

Server event C# code:

XVar record = pageObject.getCurrentRecord();
dynamic data = XVar.Array();
data = array();
data["CategoryID"] = record["CategoryID"];
DB.Delete("categories", data );

Client After event:

location.reload();

This is it. Enjoy!

Displaying IP addresses on the map

$
0
0

This is a weekend project, not much practical use but it was fun to create. Hopefully you find techniques mentioned here useful.

Some our employees need to connect to our FTP server while travelling. As an extra protection measure we maintain a whitelist of IP addresses where connection is allowed from. We thought it would be nice to convert IP addresses to geographical coordinates and display them on a map. It worth saying that IP to latitude/longitude conversion is not 100% accurate and IP addresses get re-assinged all the time. However for our task it is not really important.



And check this live demo.

There are several IP2location services available on the Internet, both free and paid. we are going to use a free database provided by ip2location.com. After a free registration you can download their database as CSV file. In this example we are going to use MySQL database. This database contains about 3 million records that allows to map IP address ranges to lat/lng pairs.

1. Create ip2location_db5 table and import data.

Create table

CREATE DATABASE ip2location;
USE ip2location;
CREATE TABLE `ip2location_db5`(
	`ip_from` INT(10) UNSIGNED,
	`ip_to` INT(10) UNSIGNED,
	`country_code` CHAR(2),
	`country_name` VARCHAR(64),
	`region_name` VARCHAR(128),
	`city_name` VARCHAR(128),
	`latitude` DOUBLE,
	`longitude` DOUBLE,
	INDEX `idx_ip_from` (`ip_from`),
	INDEX `idx_ip_to` (`ip_to`),
	INDEX `idx_ip_from_to` (`ip_from`, `ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

Import database (adjust path to CSV file)

LOAD DATA LOCAL
	INFILE 'IP2LOCATION-LITE-DB5.CSV'
INTO TABLE
	`ip2location_db5`
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 0 LINES;

2. Create table locations and import data

Skip this step if you already have your IP addresses somewhere in the database. In our scenario IP addresses are stored in a text file named ip.txt, one IP address per line.

Create table

CREATE TABLE IF NOT EXISTS `locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  `lat` decimal(10,2) DEFAULT NULL,
  `lng` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Import data

LOAD DATA LOCAL
	INFILE 'c:\\tmp\\ip.txt'
INTO TABLE
	`locations` 
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 0 LINES
(ip)
;

3. Populate lat/lng fields in locations table.

This query may need a few minutes to run, the database is pretty big.

UPDATE locations l, ip2location_db5 d
SET l.lat = d.latitude , l.lng = d.longitude
WHERE INET_ATON(l.ip) between d.ip_from and d.ip_to

4. Create a new project in PHPRunner or ASPRunner.NET.

Add locations table. On 'Choose Pages' screen uncheck all pages but List.

In Page Designer insert a map on the top of the grid. Choose "lat" and "lng" as latitude/longitude fields. Choose "ip" as "addressField".
Remove visual elements you don't need like breadcrumbs or search. Do not forget t specify your Google Maps API key.

$mapSettings["addressField"] = "ip";

// name of field in table that used as latitude for map
$mapSettings["latField"] = "lat";

// name of field in table that used as longitude for map
$mapSettings["lngField"] = "lng";

And using our famous PHP to C# converter we get similar code for C#:

dynamic mapSettings = XVar.Array();
mapSettings.InitAndSetArrayItem("ip", "addressField");
mapSettings.InitAndSetArrayItem("lat", "latField");
mapSettings.InitAndSetArrayItem("lng", "lngField");

5. Next steps

While this is a simple project that only took two hours to implement it shows some ideas that you can use in real-life projects.

What is more interesting - what kind of mapping projects would you be interested to see? I was thinking to create a project where you can upload a bunch of pictures, extract geotag info, and shows those pictures on the map, attaching it to locations where they were taken.

If you have similar ideas feel free to share in comments.

Version 10.2 of PHPRunner, ASPRunner.NET and ASPRunnerPro is here

$
0
0

If you purchased or upgraded PHPRunner, or ASPRunner.NET less than one year ago logon to control panel and find download links and registration keys under 'My purchases'. There should be 'Reg info' link next to your latest upgrade purchase.

Note: Software maintenance coverage needs to be continuous. If your last purchase or upgrade were more than one year ago you have till June 20, 2019 to renew your maintenance. After that date you will have to purchase PHPRunner, ASPRunner.NET or ASPRunnerPro at the full price.

New customer purchase links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

Upgrade/maintenance renewal links:
PHPRunner
ASPRunnerPro
ASPRunner.NET

What's new in version 10.2

Collapsible menu bar

Hide/collapse menu bar on the left. This feature is inspired by AdminLTE template.


Smart caching of JS, CSS files

Why this is important? CSS and JS files will be cached by your user's web browsers. When you make changes to CSS or Javascript your users won't see those changes till they clear the browser's cache. Now this will be taken care of automatically.

New images display options

A whole bunch of new options here. We like masonry view the best.

More options related to additional pages

- option to choose which of additional pages to show in the dashboard
- option to choose which of additional pages to show in Lookup - List with search and Add on the fly

New build notifications

Our software will tell you when a new build is available. Should have done that a long time ago

DATE control new options and API

We added an option to disable certain days of the week, only allow to choose future or past dates, etc. There is also an API with options like prohibiting all past or future dates and more.

Minor improvements

- option to insert NULL into the database instead of empty string
- HTML head section customization. For instance, in PHPRunner 10.2 you can edit C:\Program Files (x86)\PHPRunner10.2\templates\pd\headers.htm file and these changes will be applied to all pages in the project. Later we will add a more convenient way to modify headers.

To be added in maintenance release soon

- option "Hide column on the List page if the column is empty"
- PDF API. Create PDF and save it to a hard drive. Create PDF and send it via email etc.

SEO-friendly pages in your web application

$
0
0

There was an article posted back in 2007 in our forums that provided a solid start for many SEO-friendly URLs applications. It is about time to talk about how you can implement it in version 10.x of PHPRunner, ASPRunnerPro, and ASPRunner.NET.

We have helped one of our clients to implement this solution in their project. They run a website that lists public voters' information in certain Florida counties. The objective is to make these pages rank high when someone searches for county, city or neighborhood name in Google. To do so we need to make sure that relevant keywords appear in the URL.

So, for instance, instead of this View page URL:

https://www.keyword-rich-website-name.com/tablename_view.php?editid1=125745441-Dean-Abbondanza-Venice-FL

We want to see this:

https://www.keyword-rich-website-name.com/voters-125745441-Dean-Abbondanza-Venice-FL

And we would want to prettify List page URLs as well. For instance, instead of

https://www.keyword-rich-website-name.com/tablename_list.php?goto=3

We want to see this:

https://www.keyword-rich-website-name.com/voters-list-3

This is a two-step process. We need to create redirect rules on the web server and also make changes to the project itself to use new-style links.

1. Redirect rules

These rules are for .htaccess file meaning that you need Linux-based web server and Apache. We will provide IIS instructions later. These are rules we have used in this project:

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^voters-([0-9][A-Za-z0-9-]+)$ vsar0604_view.php?editid1=$1
RewriteRule ^voters-list-([A-Za-z0-9-]+)$ vsar0604_list.php?goto=$1

vsar0604 here is a table name. There are only two rules, one per page type and they are easy to understand. ([A-Za-z0-9-]+)$ is a Regex that defines a pattern to match and then in the second part of the rule we define a page to show where $1 is our match from the first part of the rule.

This may sound complicated if you never worked with regular expressions before. I found this .htaccess tester tool very valuable. You enter your rules and the URL to test against and it tells you what rules will match.

If you want to learn more about URL Rewrite rules I recommend this excellent article.

2. Project changes

We would need to make changes to the way links to view page and pagination links are displayed.

List Page: After Record Processed event

PHP code:

$link = $record["viewlink_attrs"];
$p1 = strpos($link, "href='");
$p2 = strpos($link, "editid1=");
$url = substr($link,$p2+8,-1);
$link = substr($link,0,$p1)." href='voters-".$url."'";
$record["viewlink_attrs"] = $link;

C# code:

dynamic link = null, p1 = null, p2 = null, url = null;
link = XVar.Clone(record["viewlink_attrs"]);
p1 = XVar.Clone(CommonFunctions.strpos((XVar)(link), new XVar("href='")));
p2 = XVar.Clone(CommonFunctions.strpos((XVar)(link), new XVar("editid1=")));
url = XVar.Clone(CommonFunctions.substr((XVar)(link), (XVar)(p2 + 8), new XVar(-1)));
link = XVar.Clone(MVCFunctions.Concat(CommonFunctions.substr((XVar)(link), new XVar(0), (XVar)(p1)), " href='voters-", url, "'"));
record.InitAndSetArrayItem(link, "viewlink_attrs");

List Page: Before Display event

PHP code:

function my_getPaginationLink($pageNum, $linkText, $active = false)
{
   return '<li class="' . ( $active ? "active" : "" ) . '"><a href="voters-list-'.$pageNum.'">'.$linkText.'</a></li>';
}

$rs = CustomQuery("select count(*) as cnt from (".$pageObject->querySQL.") as a");
$data = db_fetch_array($rs);
$numRowsFromSQL = $data["cnt"];

if(!$_SESSION[$pageObject->sessionPrefix."_mypagesize"])
	$_SESSION[$pageObject->sessionPrefix."_mypagesize"] = $pageObject->gPageSize;
if(postvalue("pagesize"))	
	$_SESSION[$pageObject->sessionPrefix."_mypagesize"] = postvalue("pagesize");

$gPageSize=$_SESSION[$pageObject->sessionPrefix."_mypagesize"];

$separator = "";
$advSeparator = "";
$myPages = postvalue("goto");
if(!$myPages)
	$myPages=1;

if($gPageSize && $gPageSize!=-1)
	$maxPages = ceil($numRowsFromSQL / $gPageSize);
if($myPages > $maxPages)
	$myPages = $maxPages;
if($myPages < 1)
	$myPages = 1;
$recordsOnPage = $numRowsFromSQL -($myPages - 1) * $gPageSize;
if($recordsOnPage > $gPageSize && $gPageSize!=-1)
	$recordsOnPage = $gPageSize;

$pageObject->jsSettings["tableSettings"][$pageObject->tName]['maxPages'] = $maxPages;

$firstDisplayed = ( $myPages - 1 )	 * $gPageSize + 1;
$lastDisplayed = ( $myPages ) * $gPageSize;
if( $gPageSize < 0 || $lastrecord > $numRowsFromSQL )
	$lastDisplayed = $numRowsFromSQL;

$pageObject->prepareRecordsIndicator( $firstDisplayed, $lastDisplayed, $numRowsFromSQL );
$xt->assign("pagination_block", false);
$limit=10;
if($maxPages > 1) 
{
	$xt->assign("pagination_block", true);
	$pagination = '';
	$counterstart = $myPages - ($limit-1);

	if($myPages % $limit != 0)
		$counterstart = $myPages -($myPages % $limit) + 1;
	$counterend = $counterstart + $limit-1;
	if($counterend > $maxPages)
		$counterend = $maxPages;
	if($counterstart != 1) 
	{
		$pagination.= my_getPaginationLink(1,"First") . $advSeparator;
		$pagination.= my_getPaginationLink($counterstart - 1,"Prev").$separator;
	}
	$pageLinks = "";
	for($counter = $counterstart; $counter <= $counterend; $counter ++) 
	{
		$pageLinks .= $separator . my_getPaginationLink($counter,$counter, $counter == $myPages );
	}
	
	$pagination .= $pageLinks;
	if($counterend < $maxPages) 
	{
		$pagination.= $separator . my_getPaginationLink($counterend + 1,"Next") . $advSeparator;
		$pagination.= my_getPaginationLink($maxPages,"Last");
	}
	$pagination = '';
}

$xt->assign("pagination",$pagination);

C# code:

string my_getPaginationLink(dynamic pageNum, dynamic linkText, dynamic active = null) {
return MVCFunctions.Concat("<li class=\"", (XVar.Pack(active) ? XVar.Pack("active") : XVar.Pack("")), "\"><a href=\"voters-list-", pageNum, "\">", linkText, "</a></li>");
}

dynamic advSeparator = null, firstDisplayed = null, gPageSize = null, lastDisplayed = null, lastrecord = null, limit = null, maxPages = null, myPages = null, numRowsFromSQL = null, pagination = null, recordsOnPage = null, separator = null;
rs = XVar.Clone(CommonFunctions.db_query(MVCFunctions.Concat("select count(*) as cnt from (", pageObject.querySQL, ") as a"), GlobalVars.conn));
data = XVar.Clone(CommonFunctions.db_fetch_array((XVar)(rs)));
numRowsFromSQL = XVar.Clone(data["cnt"]);
if(XVar.Pack(!(XVar)(XSession.Session[MVCFunctions.Concat(pageObject.sessionPrefix, "_mypagesize")])))
{
	XSession.Session[MVCFunctions.Concat(pageObject.sessionPrefix, "_mypagesize")] = pageObject.gPageSize;
}
if(XVar.Pack(MVCFunctions.postvalue(new XVar("pagesize"))))
{
	XSession.Session[MVCFunctions.Concat(pageObject.sessionPrefix, "_mypagesize")] = MVCFunctions.postvalue(new XVar("pagesize"));
}
gPageSize = XVar.Clone(XSession.Session[MVCFunctions.Concat(pageObject.sessionPrefix, "_mypagesize")]);
separator = new XVar("");
advSeparator = new XVar("");
myPages = XVar.Clone(MVCFunctions.postvalue(new XVar("goto")));
if(XVar.Pack(!(XVar)(myPages)))
{
	myPages = new XVar(1);
}
if((XVar)(gPageSize)  && (XVar)(gPageSize != -1))
{
	maxPages = XVar.Clone((XVar)Math.Ceiling((double)(numRowsFromSQL / gPageSize)));
}
if(maxPages < myPages)
{
	myPages = XVar.Clone(maxPages);
}
if(myPages < 1)
{
	myPages = new XVar(1);
}
recordsOnPage = XVar.Clone(numRowsFromSQL - (myPages - 1) * gPageSize);
if((XVar)(gPageSize < recordsOnPage)  && (XVar)(gPageSize != -1))
{
	recordsOnPage = XVar.Clone(gPageSize);
}
pageObject.jsSettings.InitAndSetArrayItem(maxPages, "tableSettings", pageObject.tName, "maxPages");
firstDisplayed = XVar.Clone((myPages - 1) * gPageSize + 1);
lastDisplayed = XVar.Clone(myPages * gPageSize);
if((XVar)(gPageSize < XVar.Pack(0))  || (XVar)(numRowsFromSQL < lastrecord))
{
	lastDisplayed = XVar.Clone(numRowsFromSQL);
}
pageObject.prepareRecordsIndicator((XVar)(firstDisplayed), (XVar)(lastDisplayed), (XVar)(numRowsFromSQL));
xt.assign(new XVar("pagination_block"), new XVar(false));
limit = new XVar(10);
if(1 < maxPages)
{
	dynamic counter = null, counterend = null, counterstart = null, pageLinks = null;
	xt.assign(new XVar("pagination_block"), new XVar(true));
	pagination = new XVar("");
	counterstart = XVar.Clone(myPages - (limit - 1));
	if(myPages  %  limit != 0)
	{
		counterstart = XVar.Clone((myPages - myPages  %  limit) + 1);
	}
	counterend = XVar.Clone((counterstart + limit) - 1);
	if(maxPages < counterend)
	{
		counterend = XVar.Clone(maxPages);
	}
	if(counterstart != 1)
	{
		pagination = MVCFunctions.Concat(pagination, CommonFunctions.my_getPaginationLink(new XVar(1), new XVar("First")), advSeparator);
		pagination = MVCFunctions.Concat(pagination, CommonFunctions.my_getPaginationLink((XVar)(counterstart - 1), new XVar("Prev")), separator);
	}
	pageLinks = new XVar("");
	counter = XVar.Clone(counterstart);
	for(;counter <= counterend; counter++)
	{
		pageLinks = MVCFunctions.Concat(pageLinks, separator, CommonFunctions.my_getPaginationLink((XVar)(counter), (XVar)(counter), (XVar)(counter == myPages)));
	}
	pagination = MVCFunctions.Concat(pagination, pageLinks);
	if(counterend < maxPages)
	{
		pagination = MVCFunctions.Concat(pagination, separator, CommonFunctions.my_getPaginationLink((XVar)(counterend + 1), new XVar("Next")), advSeparator);
		pagination = MVCFunctions.Concat(pagination, CommonFunctions.my_getPaginationLink((XVar)(maxPages), new XVar("Last")));
	}
	pagination = XVar.Clone(MVCFunctions.Concat(""));
}
xt.assign(new XVar("pagination"), (XVar)(pagination));
return null;
Viewing all 97 articles
Browse latest View live