Joomla! 1.0 component tutorial - part 2: back-end
Part 2 of the component tutorial explaines all files creating backend interface for my sample JPortfSimple component:
  1. admin.jportfsimple.php
  2. admin.jportfsimple.html.php
  3. toolbar.jportfsimple.php
  4. toolbar.jportfsimple.html.php
  5. install.jportfsimple.php
  6. uninstall.jportfsimple.php
  7. jportfsimple.xml
which are placed in administrator/components/com_jportfsimple folder during installation.
Well, .xml file was explained earlier (see p. 4).

7. Backend

Similarly to frontend, one file is responsible for "logic" of the component (it's backend interface) - admin.jportfsimple.php, and second file is displaying the information (admin.jportfsimple.html.php).

admin.jportfsimple.php
  1. // no direct access
  2. defined('_VALID_MOS') or die('Direct Access to this location is not allowed.');
  3.  
  4. // ensure user has access to this function
  5. if (!($acl->acl_check( 'administration', 'edit', 'users', $my->usertype, 'components', 'all' )
  6.   | $acl->acl_check( 'administration', 'edit', 'users', $my->usertype, 'components', 'com_jportfsimple' ))) {
  7.   mosRedirect( 'index2.php', _NOT_AUTH );
  8. }
  9. global $mosConfig_live_site, $mosConfig_absolute_path, $mosConfig_lang, $mosConfig_list_limit;
  10.  
  11. // load .class.php and .html.php files for the component
  12. require_once( $mainframe->getPath( 'admin_html' ) );
  13. require_once( $mainframe->getPath( 'class' ) );
  14.  
  15. // get task and act from request
  16. $task   = trim( mosGetParam( $_REQUEST, 'task', '' ));
  17. $act   = trim( mosGetParam( $_REQUEST, 'act', '' ));
  18.  
  19. // get selected objects
  20. $id = josGetArrayInts( 'cid' );
  21.  
  22. // load language file
  23. if (file_exists( $mosConfig_absolute_path.'/components/com_jportfsimple/lang/'.$mosConfig_lang.'.php'))
  24.       include_once( $mosConfig_absolute_path.'/components/com_jportfsimple/lang/'.$mosConfig_lang.'.php');
  25.    else
  26.    if (file_exists( $mosConfig_absolute_path.'/components/com_jportfsimple/lang/english.php'))
  27.       include_once( $mosConfig_absolute_path.'/components/com_jportfsimple/lang/english.php');
  28.  
  29.   // get configuration parameters
  30.   $database->setQuery('SELECT * FROM #__jportfsimple_conf'  );
  31.   $conf_rows = $database -> loadObjectList();
  32.   if ($database -> getErrorNum()) {
  33.     echo $database -> stderr();
  34.     return false;
  35.   }
  36.   if ($conf_rows) {
  37.     $jpConf = $conf_rows[0];
  38.     $base_path = $jpConf->base_path;
  39.   }

First part of frontend file does the following:

lines 12 to 16 - acl check - does logged in user has access to the component ?

19 to 21 - loads the required class and backend html (admin.jportfsimple.html.php) files

23 to 28 - gets variables defining what to do ($act, $task) and with which object ($id)

30 to 35 - loads language file, default is english.php in com_jportfsimple/lang folder

37 to 47 - reads data from database table containing configuration parameters. They are stored in $jpConf object, and location of images in $base_path


Main backend "logic":

admin.jportfsimple.php
  1. switch( $act )
  2. {
  3.   case 'configure':
  4.   switch($task) {
  5.     case 'save':
  6.     Conf_save( $option );
  7.     break;
  8.     default:
  9.     Conf_list( $option );
  10.     break;
  11.   }
  12.   break;
  13.  
  14.   case 'categories':
  15.   switch ($task) {
  16.     case 'save' :
  17.       Cat_save( $option );
  18.       break;
  19.     case 'cancel' :
  20.       Cat_cancel( $option, $act);
  21.       break;
  22.     case 'edit' :
  23.         Cat_edit( $option, $id );
  24.       break;
  25.     case 'new' :
  26.       $id = '';
  27.       Cat_edit( $option, $id );
  28.       break;
  29.     case 'delete' :
  30.       Cat_del( $option, $id );
  31.       break;
  32.     case 'publish' :
  33.       Cat_publish( $option, '1', $id );
  34.       break;
  35.         case 'unpublish' :
  36.       Cat_publish( $option, '0', $id );
  37.       break;
  38.     case 'orderup':
  39.       ordercat( intval( $id[0] ), -1, $option, $act );
  40.       break;
  41.     case 'orderdown':
  42.       ordercat( intval( $id[0] ), 1, $option, $act );
  43.       break;
  44.      
  45.     default:
  46.       Cat_list($option);
  47.       break;
  48.    
  49.     }
  50.   break;
  51.    
  52.   case 'projects':
  53.   switch ( $task ) {
  54.     case 'save' :
  55.       Proj_save( $option );
  56.       break;
  57.     case 'cancel' :
  58.       Proj_cancel( $option, $act);
  59.       break;
  60.     case 'edit' :
  61.       Proj_edit( $option, $id );
  62.       break;
  63.     case 'new' :
  64.       $id = '';
  65.       Proj_edit( $option, $id );
  66.       break;
  67.     case 'delete' :
  68.       Proj_del( $option, $id );
  69.       break;
  70.     case 'publish' :
  71.       Proj_publish( $option, '1', $id );
  72.       break;
  73.     case 'unpublish' :
  74.       Proj_publish( $option, '0', $id );
  75.       break;
  76.     case 'orderup':
  77.       orderproj( intval( $id[0] ), -1, $option, $act );
  78.       break;
  79.     case 'orderdown':
  80.       orderproj( intval( $id[0] ), 1, $option, $act );
  81.       break;
  82.      
  83.     default:
  84.       Proj_list( $option );
  85.       break;
  86.   }
  87.   break;
  88.  
  89.  
  90. case 'info':
  91.   HTML_jportfsimple::Info( $option );
  92.   break;
  93.  
  94. default:
  95.  
  96. break;
  97. }

Variable $act get it's value from chosen menu. You remember this part of xml file:

  • <menu>jportfsimple</menu>
  •   <submenu>
  •     <menu act="categories">Categories</menu>
  •     <menu act="projects">Projects</menu>
  •     <menu act="configure">Configuration</menu>
  •     <menu act="info">About</menu>
  •   </submenu>

So for example, if Categories was clicked, $act would be 'categories', and then variable $task will be checked (line 64). Initially $task is empty, so the default action is performed: function Cat_list in line 95. Other functions are called when you click toolbar buttons.

Configuration saving function:

admin.jportfsimple.php
  1. /**
  2. * Configuration saving
  3. */
  4. function Conf_save( $option )
  5. {
  6.   global $database,$mosConfig_absolute_path, $jfiles;
  7.  
  8.   // no . in path, and must end in slash
  9.   if (!(strpos($_POST['base_path'],'..')===FALSE)) { $err2=_COM_JP_ERROR; $_POST['base_path']='images/stories/'; }
  10.   if (substr($_POST['base_path'],-1,1)!=='/') $_POST['base_path']=$_POST['base_path'].'/';
  11.  
  12.   $row = new jportfsimpleConf($database);

  13.   // bind it to the table
  14.   if (!$row -> bind($_POST)) {
  15.     echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n";
  16.     exit();
  17.   }
  18.   // store it in the db
  19.   if (!$row -> store()) {
  20.     echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n";
  21.     exit()
  22.   }
  23.  
  24.   mosRedirect( 'index2.php?option='.$option.'&act=configure', 'Configuration Saved'.$err2 );
  25. }

Function called after editing configuration parameters, when "Save" is clicked.

First some specific for this component checks - if base_path variable containes two dots, then it's changed to default one; if it does not end with slash, one is added.

Lines 159 to 172 - whichever object in Joomla! is being saved (from those defined in .class file) similar actions are performed:

  • new object is created (line 159)
  • bind - copies values from $_POST to the above object - if for example you have a field in a form with a name which is not in object's class, then this part will throw an error (line 162)
  • storing to database - here also an error may show up, if for example you added field to .class file, but you didn't update database table
  • if no errors, then component redirects to the page depending on act variable in mosRedirect function, with nice message "Configuration Saved"
Configuration listing function:

admin.jportfsimple.php
  1. /**
  2. * Configuration listing
  3. */
  4. function Conf_list( $option )
  5. {
  6.  
  7.   HTML_jportfsimple::Conf_list( $option );
  8.   HTML_jportfsimple::bottom();
  9. }

It just calls display function, since configuration data is already in $jpConf variable (see line 45).

Function publishing a category:

admin.jportfimple.php
  1. /**
  2. * Category publishing
  3. */
  4. function Cat_publish( $option, $publish=1 ,$cid )
  5. {
  6.   global $database, $my;
  7.   if (!is_array( $cid ) || count( $cid ) < 1) {
  8.     $action = $publish ? 'publish' : 'unpublish';
  9.     echo "<script> alert('Select an item to $action'); window.history.go(-1);</script>\n";
  10.     exit;
  11.   }
  12.   $cids = implode( ',', $cid );
  13.   $database->setQuery( 'UPDATE #__jportfsimple_categories SET published='.$publish.' WHERE id IN ('.$cids.')');
  14.  
  15.  
  16.   if (!$database->query()) {
  17.     echo "<script> alert('".$database->getErrorMsg()."'); window.history.go(-1); </script>\n";
  18.     exit();
  19.   }
  20.   mosRedirect( 'index2.php?option='.$option.'&act=categories' );
  21. }

First it checks if at least one category is checked (line 192), if not - it displays popup window with error message (this is one exeption of the rule to not use output commands in this file). Then it runs a query to update 'published' column in jos_jportfsimple_categories table (line 198), and if no errors (line 201) it redirects back to categories page.

Category saving function:

admin.jportfsimple.php
  1. /**
  2. * Category saving
  3. */
  4. function Cat_save( $option )
  5. {
  6.   global $database;
  7.   $row = new jportfsimpleCategories($database);
  8.   // bind it to the table
  9.   if (!$row -> bind($_POST)) {
  10.     echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n";
  11.     exit();
  12.   }
  13.   // store it in the db
  14.   if (!$row -> store()) {
  15.     echo "<script> alert('".$row->getError()."'); window.history.go(-1); </script>\n";
  16.     exit()
  17.   }
  18.   $row->updateOrder();
  19.   mosRedirect( 'index2.php?option='.$option.'&act=categories', 'Saved' );
  20. }

As you see, it's almost the same as configuration saving function earlier, just one additional function updating order of all categories (line 225).

Category deleting function:

admin.jportfsimple.php
  1. /**
  2. * Category deleting
  3. */
  4. function Cat_del( $option, $cid )
  5. {
  6.   global $database;
  7.   if (!is_array($cid) || count($cid) < 1) {
  8.     echo "<script> alert('Select an item to delete'); window.history.go(-1);</script>\n";
  9.     exit();
  10.   }
  11.   if (count($cid))
  12.   {
  13.     $ids = implode(',', $cid);
  14.     $database->setQuery('DELETE FROM #__jportfsimple_categories WHERE id IN ('.$ids.')');
  15.   }
  16.   if (!$database->query()) {
  17.     echo "<script> alert('".$database->getErrorMsg()."'); window.history.go(-1); </script>\n";
  18.   }
  19.   mosRedirect( 'index2.php?option='.$option.'&act=categories' );
  20. }

Similar to category publishing function, just different query is used (line 242).