Wednesday, April 23, 2008

Xataface - How to unset tabs in the table view

Inside of a delegate class' fields.ini file, you can specify the notion of "tabs" and actually put separate fields into their tabs.

Sometimes you don't want a tab to appear in the view of a record. You can set the permissions of this like this:



function __tabs__(&$record){
$user =& getUser();

$t =& $record->_table;
$tabs = $t->_tabs;
if ( $record and !isAdmin() ){

if ( !$record->val('people_approval_status') )
unset($tabs['__people__']);
if ( !$record->val('researcher_approval_status') )
unset($tabs['__gcms__']);
}
return $tabs;
}


Here we call the __tabs__ function to actually process the tabs before they are run. And then we unset the tabs that we don't want the user to see.

Saturday, April 12, 2008

Creating multiple drupal sites on the local machine

This is supplementary information on the article listed at: http://drupal.org/node/120647

So there were a few things that I had trouble with:


  1. Copy the database to a new database.
    This step kept failing saying that I was failing a constraint test (it said I had a duplicate key). I don't really really see why it failing considering that my users table only had 2 users. The first user was actually a blank row, that had only the default values. This seemed to have been the initialize drupal installation.

    So to solve this, I went into the database and literally just deleted that row. And when I now tried to copy the database to a new database, it worked.

  2. Modifying the httpd-vhosts.conf and hosts file
    It says to comment out existing lines, but I was using the Apache 2.2.6 which had them uncommented anyways. This file is not used anyways unless you tell it to be used in the httpd.conf file which is configured in an early step.

    For each new site, you need to add this:

    <VirtualHost *:80>
    DocumentRoot /www/drupal/
    ServerName databasename
    </VirtualHost>


    What this actually does it set the location of the files to be accessed when someone tries to access the server name. This actually is in relationship with the hosts file in the /windows/system32/drivers/etc directory.

    I had this annoying problem with vista where I couldn't edit the file because I didn't have permissions. So I just copied and pasted the file on my desktop. Opened it, edited it and the pasting it back in the same directory effectively overwriting it


    If you look at this file you should see some lines like this:


    127.0.0.1 localhost
    ::1 localhost
    127.0.0.1 how_far_are_you
    127.0.0.1 drupal_test_database


    This maps IP address to host names (Servers). The number 127.0.0.1 means localhost, and then we say that this IP address can map to three servers: localhost, how_far_are_you, and drupal_test_database.

    Where are these locations/Servers? Well they are actually on your machine!!! These are specified in your httpd-vhosts.conf file above. Each server must be listed with:


    <VirtualHost *:80>
    DocumentRoot /www/drupal/
    ServerName databasename
    </VirtualHost>


    So for example:


    <VirtualHost *:80>
    DocumentRoot ""D:\web\webserver\htdocs\drupal-6.2"
    ServerName how_far_are_you
    </VirtualHost>


    This says that this server "how_far_are_you" is located at the file location specified under document root. For another drupal site, we do exactly the same thing, expect the servername is just different (it is the database name)

    So the interesting thing is that they both point to the same document root. In order to differentiate the two different sites, we use the database names.

    Actually now that I think about it. I think it just matches the name of the site folder (ie .drupal/sites/site_name). Because the settings.php file handles the database name connection. Haven't confirmed this yet though.


    One thing I want to mention is that because turn this virtual host thingy on, typing in http://localhost no longer works. You actually have to add the line:


    <VirtualHost *:80>
    DocumentRoot ""D:\web\webserver\htdocs"
    ServerName localhost
    </VirtualHost>


    For it to work. I think this was taking care of when the virtual host feature wasn't turned on.



This is a pretty interesting thing, and it also allows to just declare like domain names to point to different document roots on the local computer. This is essentially how virtual servers work (where one machine hosts multiple sites!)

One interesting thing I tried to do was skip the part about editing the host file, and see what happened if I just typed in http://name_of_servername. It didn't worked and it searched the web actually. I suspect that part is necessary, because typing in http://... causes it to first search your local machine for valid servername. So the host file is necessary

Friday, April 11, 2008

Drupal - Getting Started

Once I followed the video to install Drupal, I ran into this problem where when I tried to click on any link in the index.php file in the drupal directory it would lead me right back to a view of the directory.

I suspected this has something to do with the fact that I didn't my local installation of apache to serve the index.php file if it was present (similar to how index.html is the file to be served when the user first enters that directory.

So going into my httpd.conf file, I searched for the line "DirectoryIndex", and then modified it to include index.php:

DirectoryIndex index.html index.php


Note how index.html was already being served. And when I went back to my drupal directory and tried a link, it worked!

GD Library Not Found
Upon entering the administrator page, I found that I had some status errors right off the bat. Clicking on the link to see what status errors there were, I found one to say that my "GD Library" was not found. This is an image library that is required for drupal to do some image manipulation for some themes or maybe so content. Anyways, the version of PHP 5.2.5 that I am using actually has this library but it is turned off by default.

So go to the php.ini file, and search for the line "extension=php_gd2.dll" and then there should be a semicolon right before it. Just remove the semicolon and then try it again and that should fix the problem.

Wednesday, April 9, 2008

functions.inc.php

If you create this file in a folder called include, this file will be included in every page and the functions will be available. I've included like an example of this file with some functions that serve as an example of what it can be used for:


error_reporting(E_ALL);
ini_set('display_errors','On');
define('APPROVAL_LEVEL_APPROVED',2);
define('APPROVAL_LEVEL_CONDITIONAL',1);
define('APPROVAL_LEVEL_NOT_APPROVED',0);

define('APPROVAL_STATUS_APPROVED',2);
define('APPROVAL_STATUS_NOT_APPROVED',0);

function isLoggedIn(){
static $loggedIn = 0;
if ( $loggedIn === 0 ){
$user =& getUser();
if ( $user ) $loggedIn = true;
else $loggedIn = false;
}
return $loggedIn;
}

function &getUser(){
static $user = 0;
if ( $user === 0 ){
$auth =& Dataface_AuthenticationTool::getInstance();
$user = $auth->getLoggedInUser();
}
return $user;
}

// Checks whether the current user is an admin or not.
function isAdmin(){
$user =& getUser();
if ( !$user ) return false;
return ($user->val('role') == 'ADMIN');
}

function isBrokerCustomer(){
$user =& getUser();
if ( !$user ) return false;
return ($user->val('cookedRole') == 'BROKER CUSTOMER');
}

function getDealerID(){
static $dealer_id = -1;
$user =& getUser();
if ( !$user){
if ( $dealer_id == -1 ){
$res = mysql_query("select company_id from companies where host_name='".addslashes($_SERVER['HTTP_HOST'])."' limit 1", df_db());
if ( mysql_num_rows($res) == 0 ) $dealer_id = null;
else list($dealer_id) = mysql_fetch_row($res);
}
return $dealer_id;
}
return $user->val('dealerID');
}

function &getBrokerDealer(){
static $bd = 0;
if ( $bd === 0 ){
$id = getDealerID();
if ( $id ){
$bd = df_get_record('companies', array('company_id'=>$id));
}
}
return $bd;
}

function isBrokerDealer(){
$user =& getUser();
if ( !$user ) return false;
return ($user->val('cookedRole') == 'BROKER DEALER');
}

function isCompanyOwner(){
$user =& getUser();
if ( !$user ) return false;
return ($user->val('cookedRole') == 'COMPANY OWNER');
}

function isCompanyRep(){
$user =& getUser();
if ( !$user ) return false;
return ($user->val('cookedRole') == 'COMPANY REP');
}

function getCompanyID(){
$user =& getUser();
if ( !$user ) return null;
return $user->val('companyID');
}

function &getCompany(){
static $company = 0;
if ($company === 0 ){
$id = getCompanyID();
if ( $id ){
$company = df_get_record('companies', array('company_id'=>$id));
}
}
return $company;
}


function off($date){
$date = strtotime($date);
$offset = (strftime("%j")+strftime("%Y")*365)-
(strftime("%j",$date)+strftime("%Y",$date)*365);
if ($offset>7){
$offset = (strftime("%V")+strftime("%Y")*52)-
(strftime("%V",$date)+strftime("%Y",$date)*52);
$end=($offset!=0?($offset>1?$offset . " weeks ago":"a week ago"):"Today");
} else
$end=($offset!=0?($offset>1?"$offset days ago":"Yesterday"):"Today");
return strftime("%A, %B %e, %Y",$date)." - ". $end;
}


Here we have some common functions to help figure out what type of user is logged in.

Actions: Adding new actions at specific locations

One thing that seemed to escape me for the longest time was the fact that when I included an actions.ini, or like a permissions.ini file in my website's directory. I was actually overriding the actions and permissons.ini files in the main dataface directory.

My point of stating this was I wanted to figure out how to like add new actions to specific blocks/spaces in my website. Like for example, where the "list", "detail", "find" buttons are. In the actions.ini file, each action has an attribute called category and you can assign this a value. And then templates will uses these categories to figure out which actions to place where.

So my problem was trying to find out what categories were available. All this information is available in the dataface directory's actions.ini file.

Thursday, April 3, 2008

Permissions

So I've been doing a lot of research into the permission in dataface and I think I have a pretty good understanding.

There is a global permission setting you can set for all tables in the conf/ApplicationDelegate.php class. So you can grant a particular user to say all the tables, or block them from all tables in this class. This is accomplished through the function getPermission() in the ApplicationDelegate.php class.


function getPermissions(&$record){
$auth =& Dataface_AuthenticationTool::getInstance();
$user =& $auth->getLoggedInUser(); //grabs the logged in user
if ( !isset($user) ) return Dataface_PermissionsTool::NO_ACCESS(); //if there is no user logged in, then they should not have access to anything.
$role = $user->val('role'); //grab the role of the user. the val function just returns the value of the parameter
if ( !$role ) return Dataface_PermissionsTool::NO_ACCESS(); //if no role, then they should get no permissions
return Dataface_PermissionsTool::getRolePermissions($role); //return the permissions of that role.
}


Now if you want to block them for specific tables, these permission settings are to be set at the delegate class table level. For example,


$auth =& Dataface_AuthenticationTool::getInstance();

$user =& $auth->getLoggedInUser();

//if user is not set and (no record OR no memberID) and action is new
//give them person to edit. Enter information into the fields
if ( !$user and ( !$record || !$record->val('memberid') ) and @$_GET['-action'] == 'new' ){

return Dataface_PermissionsTool::getRolePermissions('EDIT');

//if there is just no user then they should have no acess
} else if (!$user ) {

return Dataface_PermissionsTool::NO_ACCESS();

//if there is a user, then return the role permissions of that user
} else {

return Dataface_PermissionsTool::getRolePermissions($user->val('role'));

//return PEAR::raiseError('Let the app delegate class handle it', DATAFACE_E_REQUEST_NOT_HANDLED);

}


And if we want even more fine grained, then we can do it was the field level. The field level is a little more tricky thou. To provide permissions for a given field, it must follow this syntax:

function FIELDNAME__permissions(&$record)


The record represents the given record object in question (not 100% how this works...but it could mean a row in the database...but if it is a submission form is the row empty?).

The function body could be:


$auth =& Dataface_AuthenticationTool::getInstance();

$user =& $auth->getLoggedInUser();

if ( !$user or $user->val('role') != 'ADMIN'){

return Dataface_PermissionsTool::NO_ACCESS();

}


Here we are getting the logged in user, and it doesn't exist, or if it does exist, but role is NOT admin, then they should have no access and thus it will not appear in the form.

Installation

I've decided to update this post (done on Nov 29th) to add all the problems I've had with the installation of dataface and following that "Getting Started" tutorial. So below is a list of them:

Nothing shows up in the dataface_info.php file
So I followed the instruction on the dataface site (the getting started article), but nothing even shows up! What the? Turns out the actual file relies on short tags (ie.

session_save_path is not set
So when first setting up Dataface, I always ran into this stupid problem that it says that my session_save_path is never set...
And then it refers you to line 531 of the Application class. Here you see the code:

$sessdir = ini_get('session.save_path');
if ( !$sessdir ) $sessdir = '/tmp';
$sessdir .= "/dataface";

So basically it tries to grab the ini config value session.save_path from the php.ini file...and if it isn't set then it sets it to /tmp/dataface.

Now the thing is on windows, the session.save_path value is not set by default, so it attempts to set this to /tmp/dataface...only we are using windows and not linux (windows has paritions where linux treats the /tmp to be the root directory)

So the solution is basically just make some folder location on your windows hard drive and then in the php.ini file look for the variable session.save_path and set it to that location.

This should solve the problem.

Unable to insert a new record
So after getting past the initial steps of installing and having dataface create the front end. You can then start adding some records. But when you try to add a new record into the course table, I ended up with this sql error saying that the PDF outline column has no default value. I went into phpmyadmin and looked up the column and sure enough the column was set to not allow NULL. I guess this might have been an intended feature, but in the form no red box is beside that field to even indicate that. Maybe a bug? In any case, the solution is to simply make that column NULLABLE.

Complaining about invalid character (eg. /) in a file when it doesn't exis
I was on the trigger part of the tutorial making a course.php delegate file. But when I made the file, I originally set it as a Rich Text Document. I didn't think this would be a problem as I just renamed it into a .php extension. So did all my work and then tried to reload my wwebapp. Bam! These errors about invalid characters in my course.php. Odd...I was looked inside to find nothing and I even got rid of everything and had the basic:

class tables_Course {
}
?>


And still errors!!! And then it occurred to me that maybe when I made it as a rich text document it added some additional crap in the file that I can see. So I deleted the entire file and remade it as a plain text document (.txt) and everything worked!

Permission Authetication

I was doing some development on my Dataface IERG project and I was trying to login into backend of the system. I noticed that the conf.ini file had this section:


auth_type = cas
url = "https://sson.ierg.net"
users_table = users
username_column = uid
password_column = ""


So the auth_type variable was something I've seen but never really understood. But I noticed that when I accessed the backend of the system it always led me to this url https://sson.ierg.net. I had no idea how to log in and stuff. I emailed Steve and he had this to say:

It needs the users table in order to work. You can just copy the users table from the ierg_net_-_main database into your hybrid one. That should do it.
Then you just log in with the same username and password you use for the timesheets app.


So I just did a quick copy of the table from the membership database to the hybrid database. One thing I noticed was that there was no password column which I thought was weird. So I tried to login using the username and password I've using all the time for my ierg development (ie. fcc, fcc*password), but to no avail nothing worked. I took a quick look at the conf.ini file and noticed that the password column is "". Confused...so I just made a new column password in the users table, and then added this column name to the variable in the conf.ini file.

Hoping things would work, I tried to login but still nothing. I then got frustrated and removed the CAS references and lo and behold everything worked. So I emailed Steve again mentioning the CAS stuff and he emailed back with this:

I'd prefer to keep it as CAS authentication because we'll have to set it back to that anyways when we move the site live again.

CAS just uses the same authentication I use for the timesheets app. I was able to log into your application fine until you turned off CAS.

The best steps from here:

1. Return the [_auth] section of the conf.ini file to the way it was with auth_type = cas
2. Remove the password column from the users table.
3. Try to log in using your timesheet application username and password (username fcc)

Troubleshooting

If you still cannot log in, please let me know. Also describe what happens when you try to log in.

Best regards

Steve


The one thing that stuck out to me was the timesheet application username and password. I suddenly realized that my username and password for the timesheet was different from the one that I used for the IERG development site. So I quickly undid all my changes and tried with the username and password from the timesheet and it worked! So in conclusion:


  1. I figure that the CAS authetication is some sort of system that stores the username and password in some separate place.

  2. To use the CAS service, you set in your conf.ini file:
    auth_type = cas
    url = "https://sson.ierg.net"
    users_table = users
    username_column = uid
    password_column = ""


  3. Create a user table with just a username and role if you want. I guess the username acts a foreign key to the CAS system which stores the username and password. This way we can keep this centralized location of users and if they need to access a particular dataface application they will have to first be listed in the users table of the application and then in the CAS system too.

Makesite problems

Ack...I spent so much time doing such a trivial task today. I was making a new dataface application and I had created all the tables in phpMyAdmin and then I wanted to run the dataface makesite script to quickly generate the outline of my site. Filling out the variables in the call of the makesite script, I made all the paths to be local to my machine. In other words, I had C:/web/htdocs...etc, etc. So that when the makesite script ran it included these values inside of the index.php.

The script ran with no problem, but when I tried to launch my application I noticed it had NO STYLE. I was so confused. I gather it was maybe having trouble accessing the templates folder of the dataface root directory because of the hardcoded path locations? So I then tried to change all the paths to be relative to my webserver...so I had http://localhost/ierg_news.....but then the makesite script started to complain about how it didn't have permission to create in this directory. So frustrating. So I just basically reverted back to the values that originally worked and then I manually changed the paths in the index.php to fit what I needed.

Creating approval levels for records through dynamic forms

This was done on the advisorpage website. I had so much trouble figuring how to do this, but I finally got it done. Before I show you how to do it, I want to give the situation this was applied. There a bunch of companies that needed to be approved. There were separate approval levels: approved, conditionally approved, and not approved. Rather than have the user have to go into the company detail page and select approve, disapprove, and stuff. The client wanted the ability to change the approval level in list view. The default list view of the companies was changed and the list of different approved options to change the approval level was displayed in the list view.

This was done through the actions.ini file and assigning actions to the category=record_actions. The list view displays any actions under this category in a specific location specified in the template. So there are several key things to note here:


  1. All the companies information was stored in a companies table

  2. The details of approval were stored in a separate table called dealers_companies. The reason this was done was because there were different dealers for the website, and each dealer wanted the ability to either approval or not approve the company. The net result is that each approval level is specific to a certain dealer. So that there "customers" will only see what companies THEIR dealer approved of and not others.
So here are the steps I followed to get it done:

This was done using actions. In the application's actions.ini file, there was three separate actions called bd_approve, bd_approve_record and bd_conditional_approve_record. Each of these actions corresponds to a separate thing that can be done to the company record. The content of one of these actions:


[bd_approve_record]
;; This action is available to Broker Dealers to change the approval level
;; of the current record to "Approved"
label="Approve"
category=record_actions
condition="$record and $record->_table->tablename == 'companies' and isBrokerDealer() and $record->val('approval_level') < url="">getId()}');"
url_condition="$record"
permission=bd_approve
icon="images/confirm_icon.gif"
description="Approve this company and all its news to be viewed by your agents."


  1. The label attribute was used to display the text of the action

  2. category was used to group this action under the record_actions category which is used by the template to show actions under this category at a location specified

  3. condition is when this action appears. Here we say that there must be a record, the tablename must be companies, the user logged in must a broker dealer and the approval level of the record must be less than 2.

    Note that the companies doesn't include an approval level, this is actually found in a separate table called called "dealer_companies" This issue will be addressed later.

  4. url_condition is like the condition the url has to satisfy in order for this action to be present. The reason why this is needed is because the condition attribute relies on like a record...so unless there is a record it won't be able to like apply the conditions. The url condition first checks to make sure that the url has a record...something along those lines.

  5. permission is taken from the permissions.ini file. Here the user who is currently logged on must have the bd_approve permission for this action to appear.

  6. icon is the image that appears beside the label

  7. description is the text that appears when the label is mouseovered..



Here the url attribute is assigned to a javascript function called WLS_ApproveCompany(). This function is found in the script.js file in the folder js and it was included into the website in the ApplicationDelegate class by the function block__custom_javascripts():


function block__custom_javascripts(){
echo '<script src="%27.DATAFACE_SITE_URL.%27/js/scripts.js" type="text/javascript" language="javascript"></script>';
echo '<script src="%27.DATAFACE_SITE_URL.%27/js/menu.js" type="text/javascript" language="javascript"></script>';
}


If you to the script.js and find that function:


function WLS_ApproveCompany(level, id){
var form = document.getElementById('companyApprovalForm');
form.elements['--selected-ids'].value = id;
form.elements['--level'].value = level;
form.submit();
}


Here we are grabbing the html form companyApprovalForm (more on this later) assigning the elements --selected-ids and --level values to the function inputs.

The --selected-ids variable has to exactly named this way. This is because we use a Dataface function called df_get_records() which looks specify for the --selected-ids variable.

And this submitting the form using javascript. The form is located in the templates folder called companyApprovalForm.html:



{foreach from=$ENV.QUERY key=name item=val}
{if $name != '-action' and $name != '-table' and $name != '--level' and '--selected-ids'}
<input name="{$name}" value="{$val|escape:'html'}" type="hidden">
{/if}
{/foreach}
<input name="-action" value="bd_approve" type="hidden">
<input name="-table" value="companies" type="hidden">
<input name="--level" value="" type="hidden">
<input name="-from" value="{$ENV.action}" type="hidden">
<input name="--selected-ids" value="" type="hidden">
</form>


Here we are just including all the values in the query string into this form so that when we submit it they are included in the post request and can be used again. Note the --level and --selected-id elements. We changed this in the javascript function. Notice how the action of the form is itself, but there is an additional element called -action and with a value called "bd_approve." This action is in the actions folder and called bd_approve.php. It is called when the page is get reloaded because the query string will have ?-action=bd_approve.php will tells it to run this.

The key thing to note is that if something isn't in the query string it won't be put into the form. You have to include the selected_ids and level in the $name not equal because it will continue to add these hidden fields to the form when you refresh it

So I was trying to extend this to include a separate form that handled trust values. The problem was I wasn't including the approval level in the query string and so when I tried to modify the database, I was just overriding the approval level (you will see why in a bit). We need to include this form on the page of our interest. So we can use the particular delegate class of our interest, and insert this code:


function block__before_header(){
df_display(array(), 'abstract_approval_form.html');


The bd_approve.php class

class actions_bd_approve {

function handle(&$params){
$app =& Dataface_Application::getInstance();
$query =& $app->getQuery();
$records = df_get_selected_records($query);
//echo count($records).' records selected.';


$dealerid = getDealerID();


if ( !isset($dealerid) ){
return PEAR::raiseError("No dealer id set", DATAFACE_E_ERROR);
}

if ( !isBrokerDealer() ){
return PEAR::raiseError("Sorry, you must be a BD Administrator to approve companies for your BD.", DATAFACE_E_ERROR);

}

if ( !isset($_REQUEST['--level']) ){
$level = 0;
} else {
$level = $_REQUEST['--level'];
}

$sql = array();
$sql[] = "replace into dealer_companies (company_id,dealer_id,approval_level) values ";
$vals = array();

foreach ($records as $rec){
if ( $rec->_table->tablename != 'companies' ){
// we only approve companies
continue;
}

$vals[] = "('".$rec->val('company_id')."','$dealerid', '$level')";

}

if (count($vals) == 0 ) return PEAR::raiseError("No records were selected to be approved", DATAFACE_E_ERROR);

$sql[] = implode(',', $vals);
$sql = implode(' ',$sql);
$res = mysql_query($sql, df_db());
if ( !$res ) return PEAR::raiseError("A mysql error occurred while trying to approve companies: ".mysql_error(df_db()), DATAFACE_E_ERROR);

if ( isset($query['-from']) ){
$query['-action'] = $query['-from'];
} else {
$query['-action'] = 'list';
}
$url = $app->url($query).'&--msg='.urlencode('The selected companies have been succesfully approved for viewing by your representatives. '.count($vals).' companies approved.');
header("Location: $url");
exit;


}
}


The important line is the one with the sql statement

"$sql[] = "replace into dealer_companies (company_id,dealer_id,approval_level) values ";"


And then we include the values. So we've now changed the approval level. It is important to notice that any columns in that record that don't have a value will be set to their default...meaning it will get overwritten. For example, say the dealer_companies had another column called trust_level. If I just included the replace statement without specifying the trust_level, it will always set that value to the default which could be like 0.

So you have to include the trust value even if you are only modifying the approval level. The way to make the changes are:


$sql[] = "replace into dealer_companies (company_id,dealer_id, approval_level) values ";

BECOMES

$sql[] = "replace into dealer_companies (company_id,dealer_id, approval_level, trust_level) values ";

Have to add this in the foreach loop to grab the trust level of the corresponding record:

$trust_level = $rec->val('trust_level');

And finally:

$vals[] = "('$company_id','$dealerid', '$level')";
BECOMES
$vals[] = "('$company_id','$dealerid', '$level')";




The next step is getting that to be reflected in the actions. Remember in our actions.ini file, for each action there was an attribute called condition which had this:

record->val('approval_level')

function init(&$table)
This is a function that is called each time the companies table is run. Note that the parameter $table refers to the dataface company table (like a table class that dataface represents). This allows us to create like an extended version of the companies table in the sense that we are adding new columns.

What is done here is:


$app =& Dataface_Application::getInstance();

$sql = array();
$sql[] = "create temporary table MyCompanyPermissions (
company_id int(11) not null,
role varchar(32) not null,
approval_level int(5) default 0,
trust_level int(1) default 0,
Primary Key (company_id))";


We create a temporary table which grabs columns from other tables. We need to write the sql code to do this like this:


$sql[] = "replace into MyCompanyPermissions (company_id,role,approval_level)
select c.company_id, '$defaultRole' as role, ct.approval_level from companies c inner join company_types ct on c.resource_type=ct.id where ct.approval_level > '".APPROVAL_LEVEL_NOT_APPROVED."'";


I am not going to show all the code, but the idea is to store the sql queries in an array of sql statements. And then:


foreach ($sql as $query){
$res = mysql_query($query, df_db());
if ( !$res ){
trigger_error(mysql_error(df_db()).' while executing SQL query "'.$query.'"', E_USER_ERROR);
}
}


This will execute the sql statements for use. And then the MyCompanyPermissions table will be available for use each time the companies table is executed.

__sql__ function
This function is the function that is called to override the usual sql statement which grabs rows from the companies table (or in delegate class). By default it will grab all rows using the statement "SELECT * FROM table_name". But we are overriding it here. We are still grabbing all rows, but now we are inner joining it with the MyCompanyPermission table that we just created:


function __sql__(){

return "select c.*, mcp.role as MyRole, mcp.approval_level, mcp.trust_level from companies c left join MyCompanyPermissions mcp on c.company_id=mcp.company_id";



It is important to understand that the __sql__ function must still grab all the rows of the original table. It can only add new columns.

Note the use of the left join here. We can't just normally do the SELECT column1, column2, FROM A, B WHERE (A.a = B.b). Because we need to grab all companies. Some companies might not actually have an approval level...so if we just did that they would not be returned. But if we did a left join, it will always return a row from the companies table (or which ever table was on the left of the join...hence the name LEFT JOIN) regardless of whether there was a row in the right table.

So our companies table is actually expanded by some additionally columns, and thus we have access to MyRole, approval_level, and trust_level columns. Note the trust level column was actually something I was adding on to the system.

Adding new sections in a table view

Inside of the actions.ini file, if you want to use a xataface function you have to surround it with semicolons. So for example, {getDealerID()} would return the dealer id.


So the first thing I wanted to talk about is how to add custom sections to the viewing of a table. By default, the view of a record in a table will display all the columns for that record. But what happens if you want to display so other information from another table that had a relationship with that table record?

For example, say I have a table of companies and this company had a relationship to a table called news. I could setup a relationship in the relationship.ini file, but this would just create a tab in the record view to go click. But what if I wanted to view it would having to go into a tab. Like this:




The way to do this is to first create a function in the table delegate class to that will retrieve that field. So basically we make a custom field:


function field__recent_news(&$record){
//first parameter is table,
//second is the where part of the query
return df_get_records_array('news', array('company_id'=>$record->val('company_id'), 'owner_type'=>'COMPANY','-sort'=>'published_date desc','-limit'=>10));
}


The df_get_records_array function uses the first parameter as the table we are interested in, and the second parameter is the part used to in the where part of the query. So in this case, we are using the news table, where we want the company_id of the row equal to the record company id, etc, etc. Note, we use the -sort here because that is like an action as opposed to a simple additional of a query variable.

The next part is to add the section function:


function section__recent_news(&$record){

return array(
'class'=>'left',
'records'=>$record->val('recent_news'), //there is a calculated field from the function field_section
'label'=>'Recent Updates',
'url'=>DATAFACE_SITE_HREF.'?-action=list&-table=news&company_id='.$record->val('company_id').'&owner_type=COMPANY'
);
}


The name of this function can actually be section__XXX, but we just give it a good name. Here the class attribute means what side it will be displayed in. The records are what we are grabbing. So remember that function we made above that makes a custom field, we are now grabbing this field. I think by default it will display the first column of the table (that is not the primary key or foreign key). Label is the label of the section, and url is the condition I think the url has to fulfill to make sure that this function gets run.