Quantcast
Channel: ASP.NET – Xlinesoft Blog
Viewing all 88 articles
Browse latest View live

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!

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 soon.
  • Business templates are not supported yet
  • Right-to-left languages are not supported 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!

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.

PHPRunner 9.6, ASPRunner.NET 9.6, ASPRunnerPro 9.6 released

$
0
0

PHPRunner 9.6, ASPRunnerPro 9.6, ASPRunner.NET 9.6 released (November 10th, 2016).

If you purchased or upgraded PHPRunner, ASPRunner.NET or ASPRunnerPro after November 10, 2015 this update is free of charge for you. Logon to control panel at https://xlinesoft.com/dss/support.asp and find download links and registration keys under 'My purchases'. If you do not have a help desk account yet create a new one using the same email address you have used to place the order.

Trial version download links

'Buy now' pages

PHPRunner

ASPRunner.NET

ASPRunnerPro

Upgrade pages

PHPRunner

ASPRunner.NET

ASPRunnerPro

Important: if you purchased PHPRunner, ASPRunnerPro or ASPRunner.NET before November 10th 2015 you have till November 20th, 2016 to use discounted upgrade option. After November 20th, 2016 you will have to purchase software at the full price. Do not miss your chance to upgrade for less.

'Update selected' feature

This is a long awaited feature that allows you quickly makes changes to multiple records. You can choose fields to appear on 'Update selected' dialog. Depending on Edit page settings 'Update selected' page can be shown either in popup or as a separate page.

Change fields order on the List page in run time

Drag-n-drop columns on the List page according to your needs. Your settings will be saved in the database and next time you open this page you will see the same layout.

Show/hide columns on the List page in runtime

Choose what columns to show on each page in run time. Settings are also saved in the database and preserved between sessions. Each user has its own set of settings.

New List page settings

You can now select the table to store application settings like order of columns, show/hide state, columns size and also saved searches. These settings are user specific.

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 = button.getCurrentRecord();
dynamic data = XVar.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;

What to expect in version 10.3

$
0
0

We expect a beta version of PHPRunner, ASPRunner.NET, ASPRunnerPro 10.3 in the second half of September. You can also expect one more update (v10.4) to be released before the end of the year. Here are new features that will appear in this update.

1. Separate permissions for additional pages

It will apply to both Static and Dynamic permissions


2. Filters - grouping

If you have a database of orders and need to group it by date intervals like months or years - this feature comes handy.

3. Login via Google

Same as login via Facebook.

4. PDF API

Here are some PDF API use cases.

  • Create PDF, ask for user's email, email PDF file to that email address
  • Create PDF, ask user for file name, save it on the hard drive under that name
  • Create PDF and save in the database
  • Email selected records as separate PDF files

5. Dialog API

This is a really small but useful extension we developed while working on PDF API. Let's say you quickly need to ask the user for some info. Now there is no need to create a separate page for this purpose.

Here is the code that you can use in any Javascript event or button:

return ctrl.dialog( {
	title: 'Enter email',
	fields: [{
		name: 'email',
		label: 'Email',
		value: 'user@server.com'
		},
		'text']
});

And this is how it looks in the generated application.

6. Web.config editing

ASPRunner.NET only. Users will be able to modify web.config from ASPRunner.NET adding all required sections and variables.

7. BeforeConnect event

ASPRunner.NET only. It allows implementing dynamic connection strings. Useful in multi-tenant web applications or to retrieve connection string from Azure Key-vault.

8. Edit HEAD section

A separate HEAD section per project. Add favicons, meta tags, links to CSS/JS files etc.


Tri-part events

$
0
0

Tri-part events are a special code snippets that provide a convenient way of programming interactions between the browser and the webserver.

Why three parts?

Any web application consists of two parts - server and client. The client part runs in the browser on the computer of the web site visitor. This part takes care of all user interface interactions, such as buttons, input controls, blocks of text and images. Client-side code is written in Javascript in most cases.

Server part - this code runs on the webserver itself and has direct access to the database, can send emails, read and write files to the disk. In PHPRunner-created applications server code language is PHP, ASPRunner.NET uses C# or VB.NET, ASPRunnerPro uses Classic ASP.

Most real-life tasks though require a joint action of both client and server parts. For example, the user presses a button and wants something changed in the database, or an email being sent.
Tri-part events provide a relatively easy way to create such integrated code snippets.

They consist of three parts running one after another:

  • Client Before - this Javascript code is run immediately after user's action such as pressing the button.
  • Server - this part runs on the server after the Client Before part has finished. You can only user server-side code here (PHP, C# or ASP).
  • Client After - back to the web browser, another Javascript code part.

Passing data between events

To create a snippet serving a meaningful purpose these event parts must be able to pass data to one another. For example, the Client Before part gets input from the user passes it to the Server event. The latter runs some database queries using the data and passes the results to Client after, which shows them to the user.

Two objects serve as links between the three parts of the event.

  • params object helps passing date from Client Before to Server event.
  • result object helps passing data from Server to Client After event.

Mind the syntax difference between Client and Server events. The Client code is Javascript, and the Server code is PHP (C# or ASP).
The key names, user and address are up to you. Choose any names here.
You can also pass arrays and objects this way:

ClientBefore:

params["data"] = {
	firstname: 'Luke', 
	lastname: 'Skywalker'
};

Server:

do_something( $params["data"]["firstname"] );

Where in the software you can use it?

There are three features in PHPRunner/ASPRunner.NET/ASPRunnerPro that utilize the Tri-part events.

  • Custom buttons
  • Field events
  • Grid click actions

All these events work the same way. The only difference between them is how the user initiates the event - by clicking the button, interacting with an input control, or by clicking somewhere in the data grid.

Control flow

If you don't need the Server part, just return false in the Client Before code:

...
return false;
// all done, skip Server and Client after parts.

Asynchronous tasks

Some tasks in Javascript code are asynchronous, they are not completed immediately, but at some indefinite time in the future. You may need to wait for their conclusion before running the Server part of the event. In this case, return false from the Client Before event and call submit() function on the task conclusion.

Example:

// run Server event after 5 seconds pause:
setTimeout( function() { 
	submit();
}, 5000 );
return false;

Event parameters

PHPRunner passes the following parameters to ClientBefore and ClientAfter events:

  • pageObj - RunnerPage object representing the current page
  • ajax - Ajax helper object - provides miscellaneous functions like showing dialogs or generating PDF files
  • row - GridRow object - object representing a row in the grid. Buttons receive this parameter when positioned in the data grid on the List page. Field events receive it in the Inline Add/Edit mode
  • ctrl - in the Field events this parameter represents the input control itself. RunnerControl object.

SaaS application design

$
0
0

SaaS applications, also known as multi-tenant applications, are very popular these days. A single instance of the software is available to multiple customers/tenants. Each tenant's data is isolated and remains invisible to other tenants. All tenant databases have the same structure. The data is different, of course.


There are multiple ways to design and implement this kind of application in PHPRunner or ASPRunner.NET. You can get away using a single data to host all customers' data but using a dedicated database for each customer is easier and more reliable. In this article, we'll show the easiest way to build such an app.

You can see that we have a common saasdb database here and multiple identical client databases. We only need to add to the project one of those client databases, database1 one in our example.

Common saasdb database only contains a single table named users.

We will select this table as a login table on the Security screen. Based on the username we redirect users to their database. We would only need to add two lines of code to make this happen. Things will be slightly different in PHPRunner and ASPRunner.NET.

PHPRunner

1. AfterSuccessfulLogin event

$_SESSION["dbname"] = $data["database"];

Based on who is logged in we save database name in the session variable.

2. Server Database Connection

Proceed to the Output Directory screen, add a new Server Database Connection and modify the line where we specify the database name. This example is for MySQL. The point is to use $_SESSION["dbname"] instead of the hardcoded database name.

$host="localhost";
$user="root";
$pwd="";
$port="";
$sys_dbname=$_SESSION["dbname"];

ASPRunner.NET

1. AfterSuccessfulLogin event

XSession.Session["database"]=data["database"];

2. BeforeConnect event

.NET applications are compiled before they can be deployed meaning that we cannot use any code in a new Server Database Connection. For this specific reason, we have added BeforeConnect event in ASPRunner.NET v10.3.

This is how default connection string looks (as shown in the event Description):

GlobalVars.ConnectionStrings["database1_at_localhost"] = "Server=localhost;Database=database1;User Id=root;Password=";

This is how we need to change it to grab database name from the session variable:

GlobalVars.ConnectionStrings["database1_at_localhost"] = 
String.Format("Server=localhost;Database={0};User Id=root;Password=", XSession.Session["database"]);

This is it. If you logon as user1 now you will see two records in the Customers table.

And if you logon as user2 you will see three records in the same table, because we are connected to database2 now.

Of course, creating a fully-featured SaaS application takes more than two lines of code. You need to take care of user registration, create new databases on the fly, upgrade all databases when database structure changes, maybe add the billing part etc. But this article should definitely help you get started.

Making new record visible on the List page

$
0
0

When we added a new record and returned back to the List page that new record may not be clearly visible on the page. Depending on what page we on, what sort or is applied, how many records are on the page - we may simply be on the wrong page. This can be useful when, for instance, you are transferring a large amount of data from paper to the database and this kind of visual indication can be helpful to keep track of the process.

In this article, we will show you a technique that does the following. After the new record is added we find out which page it belongs to, redirect the user to that page, highlight the record and also scroll the page if the record is below the fold.

Things to change in the code:
- products - the name of the table, we need it for the redirect
- ProductID - the name of the key column

1. Add page: AfterAdd event:

Saving new record ID in the session variable. Execute previously saved SQL query, loop through the recordset finding our new record. Redirect to the corresponding page.

$_SESSION["ProductID"]=$keys["ProductID"];

$i=0;
$rs = DB::Query($_SESSION["ProductsSQL"]);
 
while( $data = $rs->fetchAssoc() )
{
$i++;
if ($data["ProductID"]==$keys["ProductID"])
	break;
}

$page = floor($i / $_SESSION["pageSize"] )+1;

header("Location: products_list.php?goto=".$page);
exit();

2. List page: BeforeSQLQuery event

Saving the current SQL query and page size in session variables

$_SESSION["ProductsSQL"] = $strSQL;
$_SESSION["pageSize"] = $pageObject->pageSize;

3. List page: AferRecordProcessed event

Changing the background color of the new record.

if ($data["ProductID"] == $_SESSION["ProductID"] )  
$record["css"]="background:yellow;";

4. List page: BeforeDisplay event

Bullets 4 and 5 are only required if you display a lot of records on the list page and need to scroll the page so the new record is visible. This code most likely is not required if you only display twenty records per page.

$pageObject->setProxyValue("ProductID", $_SESSION["ProductID"]);

5. List page: Javascript OnLoad event

var allRecords = pageObj.getAllRecords();
allRecords.forEach(function(row){
if( row.getFieldText('ProductID')==proxy['ProductID']) {
$([document.documentElement, document.body]).animate({
        scrollTop: row.fieldCell('ProductID').offset().top
    }, 2000);
}
})

Enjoy!

Making search panel horizontal

$
0
0

The search panel is displayed vertically on the left side of the List page by default. Sometimes you need to make it more prominent and place it above the grid, just like on the screenshot below.


Doing so is fairly simple.

1. Changes in Page Designer

Proceed to the List page in Page Designer and drag the search panel block to the cell above the grid.

2. Custom CSS

Now we need to make sure that search controls appear horizontally and also that search panel takes the whole horizontal space. To do so we can use the following CSS code under Eidtor->Custom CSS. By default, all DIVs are displayed vertically, one under another so we needed to use float: left to make them align horizontally. More info about float property.

.form-group.srchPanelRow.rnr-basic-search-field, div.srchPanelRow.form-group {
float: left !important;
margin: 5px;
}

.searchOptions.panel.panel-primary.searchPanelContainer, div[data-cellid=above-grid_c1] {
width: 100% !important;
}

It is worth mentioning that the following two tutorials helped us build this CSS.
Introduction to Custom CSS
Choosing the correct CSS selector

Version 10.4

$
0
0

Version 10.4 beta is here!

Here are the links to download the beta version:

PHPRunner 10.4 beta

ASPRunner.NET 10.4 beta

Please note that this is the beta version and some things may not work as expected. We expect the final version to be ready in about two weeks. New download links and keys will appear under your control panel.

The two most important features in this update are our own REST API and also the consumption of data, provided by third-party APIs. Consumption of third party data turned out to be the most difficult task and took more time than we expected, hence the delay. On the plus side, we now able to work with any data, not just something that comes as a result of the SQL query. And this also helped us implement a few minor but frequently requested features like OR search or data filtering in charts.

REST API consumption

REST Views

Data received from REST API providers like Spotify or Football API. In this article we will show you how to display the list of English Premiere League matches using Footbal API.

1. Add REST API connection and REST View

Here is how REST API connection setup looks like.

Note: OAuth authorization doesn't work yet.

Both the base API URL and authorization method are provided by the REST API provider. What kinds of views to create - depends on the API itself. For instance, Football API offer the following "resources": competitions, matches, teams, standings, and players. It is natural to create the same REST Views in your project. In this specific project, we are creating Matches and Teams views.

2. Configure URL, execute the List request, add fields

REST View setup, List operation.

You can see that in this view we limit it to season 2019 of the Premier League. This is where we found some sample requests.

You enter the Request URL, it gets added to the base REST API URL and shows the actual request being performed. If your URL is correct, run the request and get the response back in JSON format. You can see it in the Response area. Retrieving and parsing the response may take a bit of time, depending on the API. You can also copy and paste the sample JSON response from the

Now there is time to add fields. The easiest option is to use a plus button in the Response area. Make sure you are finding the correct field in the response. For instance, in this response, there is also competition info, season info, venue, referees etc. Again, it all depends on the specific API. Once we found and added our fields we also need to assign meaningful names to them. There might be several fields named ID, Name. We'd rather deal with names like homeTeamName, awayTeamName etc.

Here is how the field settings are going to look at this moment. Your job is to assign it a meaningful name and make sure that the correct data type is selected.

* in Source(list) means a list of items, for instance, matches/*/score/fullTime/homeTeam means a list of matches where * will be replaced with real IDs like 1, 2, 3 etc. You don't need to worry about the syntax as our software handles this for you.

Now you can build your project, run it and you will see the list of English Premier Leagues from season 2019 on your screen. Isn't it cool? This is your first REST API View and it works!

Now you can choose what fields to show on the List page and in which order, choose what fields to make searchable, what 'View as' format to use etc. In other words - the rest of the software works exactly the same way as at did with data coming from the database.

3. Configure URL for the Single operation, execute, add fields

Next step - let's create a Single operation, which is basically a view page. Proceed to the Pages screen and choose id as a key column. Again, this name depends on the API you use and may be different in your case.

Go back to REST View setup screen and enable Single operation. From the REST API docs we can see that a single match request looks like this: https://api.football-data.org/v2/matches/xxxxxx where xxxxxx is our match id, received via List operation. So, into the Request URL field we enter matches/ and then use 'Insert variable' to select keys.id variable.

Now we can run this request. Since we use a parameter there the software will ask you to provide one. We can go back to the List operation screen and copy match id (264341) from there.

Now you do the same thing - add fields from the Single operation response. If REST API is well designed and fields named in a similar manner then it will simply populate Source (single) part of the field properties.

In some cases, the software won't recognize that the field was created already and will add a new one like id1, id2 etc. In this case, proceed to id1 field properties, copy Source (single) path and paste to id field Source (single) part. After that id1 field can be safely deleted.

Once you do this for all fields you need to appear on the Single (View) page, proceed to the 'Pages' screen, enable 'View' page and build your project. You can now access the View page from the List page.

4. Creating Teams view and linking it from Matches view

Let's create a similar REST View for the Teams. I will just show screenshots of how List and Single operations should look.

List operation:

Single operation:

You can add Team fields the same way as described in the Matches View section. Now let's go back to Matches List View and make sure that both Home Team ID and Away Team ID appear on the List view.

Proceed to homeTeamID 'View as' settings and configure it as a link that points to the corresponding Teams View page.

This is pretty much it for now. Now if you build your app and run it in the web browser you will see something like this.

5. Other REST View operations

Since this API only provides the read-only access to data we won't need insert, update or delete operations. We will come with examples of using these operations later. The count operation is only required when REST API provides pagination option.

SQL Views

The new functionality offers a lot more than just data received via REST API.

You can now use any non-standard SQL Query to retrieve data i.e. you can use MySQL variables in your SQL Query or use stored procedures to retrieve data. Let's see how to work with stored procedures. In this example, we will work with MySQL and Northwind database.

First, let's create a stored procedure:

DELIMITER //
CREATE PROCEDURE categories_search 
(IN a VARCHAR(50))
BEGIN
  SELECT * from categories
  where Description like concat('%', a, '%');
END //
DELIMITER ;

It doesn't do much but returns a list of categories that contain the word passed there as a parameter. We can test in in phpMyAdmin this way:

call categories_search('fish')

Once we made sure that stored procedure works we can create a SQL View based on this stored procedure call. We will use 'All fields search' variable as a parameter. Note that we added single quotes around the parameter since this is a text variable. And, of course, we didn't have to remember this variable name, we added it via Insert variable->All fields search.

Now we can run this procedure, get results back, add fields to the list and proceed to build the project.

Note: 'All fields search' parameter will be empty on the initial page load. You need to make sure that your stored procedure won't break if an empty parameter is passed there. This is, of course, not a problem, if your stored procedure doesn't take any parameters.

Any PHP or C# code

For instance, you can display a list of files from the file system. Handy, if you need to implement a file manager. PHPRunner and ASPRunner.NET will build the sample code for you and you can extend it any way you need it. Here is an example of just supplying some data as an array:

$data = array();
$data[] = array("id"=>1, "name"=>"aaa");
$data[] = array("id"=>2, "name"=>"bbb");
$data[] = array("id"=>3, "name"=>"ccc");
$result = new ArrayResult( $data );

if( !$result ) {
	$dataSource->setError( DB::LastError() );
	return false;
}
// filter results, apply search, security & other filters
$result = $dataSource->filterResult( $result, $command->filter );
// reorder results as requested
$dataSource->reorderResult( $command, $result );
return $result;

You will also need to define two fields, id (integer) and name (varchar). This is it, you can build your app and all functions like search, filters, pagination will work out of the box. If you ever needed to build pages that are not tied to any database table - your prayers were answered. You can use Code Views, for instance, to build a feedback form where data entered by the user is sent to your email.

REST API

As a first step enable REST for your project under Miscellaneous->REST API.

REST API Events

Since REST API interaction is fully UI-less, not all events make sense. Here is the list of events that will be executed for requests made via REST API:

  • AfterAppInitialized
  • AfterTableInitialized
  • BeforeAdd/AfterAdd
  • BeforeEdit/AfterEdit
  • BeforeDelete
  • After record deleted

REST API Authorization

Skip this section if your project doesn't have the login page enabled.

Beta version supports Basic HTTP Authorization only. In the final version we will also add API keys support.

REST API considerations

URLs

URLs and URL parameters should be URL encoded. For instance instead of "order details" you need to use "order%20details".

This is correct:

curl "http://localhost:8086/api/v1.php?table=order%20details&action=view&editid1=10248&editid2=42"

In this is not:

curl "http://localhost:8086/api/v1.php?table=order details&action=view&editid1=10248&editid2=42"

And response will be:

{
error: "Unknown table name",
success: false
}

List of fields

For now, all fields that appear in the SQL query will be returned in case of list/view or updated in case of update/insert. Later we will have an additional option to choose fields that are updatable or selectable via REST API.

Files upload

Not supported for now

Advanced Security

If Advanced Security mode like "Users can see and edit their own data only" is enabled in the wizard it will be also applied to the REST API requests.

Search and pagination

Support of search and results pagination will be added in the final version.

REST API Code Examples

list

Returns a list of records.

Sample request URL:

curl "http://localhost:8086/api/v1.php?table=customers&action=list"

Sample response:

{
  "data": [
    {
      "CustomerID": "ANATR",
      "CompanyName": "",
      "ContactName": "Morris H Deutsch",
      "ContactTitle": "",
      "Address": "8799 Knollwood dr",
      "City": "Eden Prairie",
      "Region": "MN",
      "PostalCode": "55347",
      "Country": "United States",
      "Phone": "2027280820",
      "Fax": "(5) 555-3745",
      "Lat": "44.8436452000",
      "Lng": "-93.4535225000"
    },
    {
      "CustomerID": "ANTON",
      "CompanyName": "Antonio Moreno Taqueria",
      "ContactName": "Antonio Moreno",
      "ContactTitle": "Owner",
      "Address": "Mataderos 2312",
      "City": "Mexico",
      "Region": "",
      "PostalCode": "33333",
      "Country": "Mexico",
      "Phone": "(5) 555-3932",
      "Fax": "",
      "Lat": "32.5053534000",
      "Lng": "-117.0668113000"
    }
],
  "success": true
}

view

Sample request URL:

curl "http://localhost:8086/api/v1.php?table=customers&action=view&editid1=WOLZA"

Sample request URL with multiple key columns:

curl "http://localhost:8086/api/v1.php?table=order%20details&action=view&editid1=10248&editid2=42"

Sample response:

{
data: {
CustomerID: "WOLZA",
CompanyName: "Wolski Zajazd",
ContactName: "Zbyszek Piestrzeniewicz",
ContactTitle: "Owner",
Address: "ul. Filtrowa 68",
City: "Warszawa",
Region: "",
PostalCode: "1",
Country: "Poland",
Phone: "(26) 642-7012",
Fax: "(26) 642-7012",
Lat: "52.2195630000",
Lng: "20.9858780000"
},
success: true
}

update

Data is passed in the form urlencoded format as fieldname=value&fieldname1=value1 list.

Example:
Update customer with CustomerID (key column) KOENE setting ContactName to be Bill Gates.

curl -X POST "http://localhost:8086/api/v1.php?table=customers&action=update" -d "editid1=KOENE&ContactName=Bill Gates" -H "Content-Type: application/x-www-form-urlencoded"

Sample success response:

{
  "success": true
}

h4>insert

Similar to update except you do not need to supply editid1 parameter.

Example:
Add a category named Beer with Description Beer and stuff.

curl -X POST "http://localhost:8086/api/v1.php?table=categories&action=insert" -d "CategoryName=Beer&Description=Beer and stuff" -H "Content-Type: application/x-www-form-urlencoded"

And response will contain the whole new record including the autoincrement column:

{
   "success":true,
   "data":{
      "CategoryName":"Beer",
      "Description":"Beer and stuff",
      "CategoryID":272
   }
}

delete

Sample request URL:

curl -X POST "http://localhost:8086/api/v1.php?table=customers&action=delete&editid1=WOLZA"

Sample request URL with multiple key columns:

curl "http://localhost:8086/api/v1.php?table=order%20details&action=delete&editid1=10248&editid2=42"

Grouping in Charts

Previously in charts, you had to use GROUP BY in SQL query to display aggregated data like the number of orders per month. Many people were struggling with SQL queries like this and also you could not search or filter that data because of the use of the GROUP BY.

Now we added a much easier option for this. You can just leave the default SELECT * FROM ... query and on chart setup screen choose the group field. Here is how it looks in the generated application:

There is an excellent video tutorial on YouTube that covers grouping in charts.

"OR" search

Also, a feature that was not available previously. Previously you had to add the same field twice or thrice to the search panel in order to implement the OR search. Now all you have to do is to make the search field a lookup wizard and enable multi-select on the search page. Now you can easily display orders that belong either to Customer 1 or to Customer 2 or to Customer 3. Configure CustomerID as a Lookup wizard, choose 'Different settings on each page' option, make it multi-select on the Search page, leave it Single select on Add/Edit pages.

Here is the video that explains how to configure OR search in version 10.4.

Viewing all 88 articles
Browse latest View live