SlideShare une entreprise Scribd logo
1  sur  89
Télécharger pour lire hors ligne
Extending CMS Made Simple
              Presented by
    Jeff Bosch and Samuel Goldstein
Jeff Bosch
●   Core Development Team member
●   B.S. in Computer Science
●   Started A & J Progamming in 2007 for
    custom web programming and
    implementation
Samuel Goldstein
●   Core Development Team member since 2004
●   Principal at 1969 Communications
●   Background
    ●   Programming since 1980, starting with TRS-80
    ●   Aerospace in the '90s
    ●   Dot-com roller coaster
●   Hey! Buy my book!
    ●   CMS Made Simple Developer's Cookbook from Packt Publishing
OK, enough of that
●   Why extend CMS Made Simple?
    ●   Add functionality
    ●   Make things easier for your clients
    ●   More money for you
    ●   Fame, fortune, glory
Ways to Extend CMSMS
●   Core, core modules, and Smarty
●   User-Defined Tags
●   Tags (a.k.a. Plugins)
●   Modules
How do I decide on an
             approach?
●   There is not always an “absolute best”
    approach
●   Don't neglect non-technical
    considerations (e.g., your specific users)
●   The following chart may also help
Approaches


                                              Smarty   Tag   UDT   Module
No installation required                        √             √
Multiple actions                                             √*      √
Create databases / preferences                               √*      √
Act as Smarty modifier                                  √
Help displayed in Admin area                            √            √
Admin panel                                                          √
Post on Developer's Forge, share via Module                          √
Manager
Localization/Translation                                             √
Let's get started!
●   Build a Google Site Map using Core and
    Core Modules
    ●   We'll use Menu Manager in a hidden page
    ●   We'll create a custom Menu Manager
        template
    ●   And a mod_rewrite trick
Menu Manager Template


<?xml  version="1.0"  encoding="UTF-­‐8"?>
<urlset  xmlns="hRp://www.sitemaps.org/schemas/sitemap/0.9">
{if  $count  >  0}
{foreach  from=$nodelist  item=node}
  <url>
        <loc>{$node-­‐>url|escape:'html'}</loc>
        <lastmod>{$node-­‐>modified|date_format:'%Y-­‐%m-­‐%dT%T-­‐08:00'}</lastmod>
    </url>
{/foreach}
{/if}
</urlset>
Calling the Template
●   Create page named “sitemap”
●   Set alias to “sitemap” too
●   Uncheck “Show in Menu”
●   Insert tag into your page content:
{menu  template='sitemap'  show_all='1'  collapse='0'}
Add an .htaccess file


<IfModule  mod_rewrite.c>
RewriteEngine  on
#Sub-­‐dir  e.g:  /cmsms
RewriteBase  /
RewriteCond  %{REQUEST_FILENAME}  !-­‐f  [NC]
RewriteCond  %{REQUEST_FILENAME}  !-­‐d  [NC]
RewriteRule  ^sitemap.xml$  index.php?page=sitemap&showtemplate=false  [L]
</IfModule>
Test it!
●   Go to http://yoursite.com/sitemap.xml
<?xml  version="1.0"  encoding="UTF-­‐8"?>
<urlset  xmlns="hRp://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
        <loc>hRp://cms_book.viajante/</loc>
        <lastmod>2009-­‐05-­‐13T10:12:18-­‐08:00</lastmod>
    </url>
    <url>
        <loc>hRp://cms_book.viajante/index.php?page=how-­‐cmsms-­‐works</loc>
        <lastmod>2009-­‐05-­‐12T20:11:52-­‐08:00</lastmod>
    </url>
  …
</urlset>
OK, how about a Tag?
●   Only tags can serve as Smarty modifiers
●   Here's a tag that vigorously defends our
    intellectual property!
●   Appends a ® symbol to every instance of
    “CMS Made Simple” in page content.
postfilter.registeredtrademarker.php

<?php
funcQon  smarty_cms_posPilter_registeredtrademarker($tpl_output,  &$smarty)
{
    global  $gCms;
    $result  =  explode(':',  $smarty-­‐>_current_file);

     if  (count($result)  >  0)
     {
            if  ($result[0]  ==  'content')
            {
            $tpl_output  =  str_replace('CMS  Made  Simple','CMS  Made  Simple®',  $tpl_output);
            }
     }
     return  $tpl_output;
}
?>
Using the Filter
●   Save your filter in the plugins directory
●   In the Admin, be sure to clear your
    cache!
●   View your site.
You® filte®ed ®esults
OK, how about a UDT?
●   UDTs can do all sorts of things:
    ●   Talk to the database
    ●   Talk to modules
    ●   Deal with events
    ●   Interact with Smarty
●   Let's try a few of those things!
UDT “pagecounter”
global  $gCms;

$db  =  $gCms-­‐>GetDb();

$count  =  $db-­‐>GetOne('select  count(*)  from  '.cms_db_prefi x().'content  where  acQve=1');

$smarty  =  $gCms-­‐>GetSmarty();
$smarty-­‐>assign('page_count','Total  pages:  '.$count);
Then, add a content page
●   Set the title and menu text
●   In the content, call the UDT with
{pagecounter}

●   And output the Smarty variable with
{$page_count}
Viewing your page
Exercise I
●   What approach would you use to implement each
    of the following?
    ●   Convert your page content into Pirate Speak
    ●   A form for users to submit movie reviews, with an
        Admin panel allowing the site maintainer to approve
        or reject reviews for display
    ●   Putting the title of the most-recently added page into
        a Smarty variable
Exercise I, cont.
●   Display a special image file only on the site's
    home page
●   Display the Admin account email address on a
    page (but obscured to prevent spammers
    from harvesting it)
●   Display a poll, and show the results after the
    user votes
●   Display links to all pages that are below the
    current page in the site hierarchy
Exercise II
●   Create a UDT for changing site layout
    seasonally.
    ●   Set a variable which can be used to change
        the stylesheet classes of the site.
    ●   For simplicity, call January-March “winter,”
        April-June “spring,” July-September
        “summer,” and October-December
        “autumn.”
Exercise II, cont. UDT
global  $gCms;

$month  =  date('n');  //  returns  month  as  number  between  1  and  12,  inclusive
$smarty  =  $gCms-­‐>GetSmarty();

if  ($month  <  4)
       $season  =  'winter';
else  if  ($month  <  7)
       $season  =  'spring';
else  if  ($month  <  10)
       $season  =  'summer';
else
       $season  =  'autumn';

$smarty-­‐>assign('season',  $season);
Exercise II, cont. Template.
{process_pagedata}
<!DOCTYPE  html  PUBLIC  "-­‐//W3C//DTD  XHTML  1.0  TransiQ onal//EN"  
"hRp://www.w3.org/TR/xhtml1/DTD/xhtml1-­‐transiQ onal.dtd">
<html  xmlns="hRp://www.w3.org/1999/xhtml"  xml:lang="en"  >
<head>
<Qtle>{sitename}  -­‐  {Qtle}</Qtle>
{metadata}
{cms_stylesheet}
{season}
</head>
<body>
<div  id="header"><h1  class=”{$season}”>{sitename}</h1></div>
<div  id="menu">{menu}</div>
<div  id="content"  class=”{$season}”>
    <h1>{Qtle}</h1>
    {content}
</div>
</body>
</html>
Exercise II, cont. CSS
.winter  {background-­‐color:  #bfc4ff;}
.spring  {background-­‐color:  #bfffce;}
.summer  {background-­‐color:  #fffabf;}
.autumn  {background-­‐color:  #ffcabf;}

#header  h1.summer  {background:  url(/uploads/images/beach.jpg);}
Hour II
How's my timing?
Getting Started with Modules
●   For the 1.x series, several ways to get
    started
    ●   From scratch
    ●   Skeleton module
    ●   Module Maker
    ●   MCFactory
    ●   etc
Skeleton Module
●   Download from Forge or Module
    Manager
●   Has simple functionality
    ●   install/upgrade
    ●   admin panel
    ●   database routines
    ●   form API calls
Skeleton Module, cont.
●   Once you've installed, edit the files to
    adapt to your own purposes
●   Well commented
●   Somewhat dated: last updated for CMS
    MS 1.6.5
●   Still has correct structure, but may lack
    more recent improvements and features
Structure of a 1.x module
●   Modules extend the CMSModule class
●   Magic of OO:
    ●   Automatically inherits a lot of functionality
        (e.g., Database and Form APIs, Smarty,
        Translation, etc)
    ●   Most methods already inherit acceptable
        defaults
Minimal 1.x Module
●   File lives in
    $CMS_ROOT/modules/module_name
●   Filename is module_name.module.php
●   Module class must match file name
●   Need to implement 3 methods:
    ●   GetName
    ●   DoAction
    ●   IsPluginModule
Minimal 1.x module, cont.
●   GetName simply returns module name;
    must match class and filename
●   IsPluginModule returns boolean.
●   DoAction is, unsurprisingly, where the
    real action takes place
DoAction
●   Called with a few parameters:
    ●   $action, “reason for calling.” Two built-in:
        “default” and “defaultadmin”
    ●   $id, a prefix for use in forms
    ●   $params, a hash of parameters
    ●   $returnid, reference to the page containing
        the tag
Other important methods
●   Install, upgrade, uninstall. These methods
    called as expected, and allow for
    housekeeping
●   SetParameters. Sets up parameter
    filtering, URL routes,
●   Lang. Translates strings.
Minimal 1.x module, cont.
●   For performance reasons, methods are
    split out into multiple files
●   String translations are stored in external
    files
●   Templates may be stored as files or in the
    database
Layout on filesystem
Enough Abstraction!
●   We will create a basic module that
    demonstrates core APIs
●   “Quotations Module” requirements:
    ●   Presents form to user, handles input safely
    ●   Displays random quotation to user
    ●   Allows admin to delete records
Step 1. Preparation
●   Create directories
    ●   $CMS_ROOT/modules/Quotations
    ●   Quotations/templates
    ●   Quotations/lang
●   Create base files:
    ●   Quotations.module.php
    ●   lang/en_US.php
Quotations.module.php
<?php
class  QuotaQons  extends  CMSModule
       {
       funcQon  GetName()
            {
            return  'QuotaQons';
            }
       funcQon  GetFriendlyName()
            {
            return  $this-­‐>Lang('friendlyname');
            }

      funcQon  IsPluginModule()
           {
           return  true;
           }

      funcQon  GetVersion()
           {
           return  '0.1';
           }
      }
?>
lang/en_US.php
<?php
$lang['friendlyname']='QuotaQon  Module';
?>
Database Schema
●   We'll probably need
    ●   Unique ID
    ●   Quotation text
    ●   Quotation author
    ●   Submission date
method.install.php
<?php
if  (!isset($gCms))  exit;

$db  =  $this-­‐>GetDb();
$dict  =  NewDataDicQonary(  $db  );

$fields="
    quotaQon_id  I  KEY,
    quotaQon  X,
    author  C(80),
    submit_date  DT
";

$sqlarray  =  $dict-­‐>CreateTableSQL(  cms_db_prefix()."module_quotaQons",$fields);
$dict-­‐>ExecuteSQLArray($sqlarray);

$db-­‐>CreateSequence(cms_db_prefix()."module_quotaQon_seq");
?>
Install it
Consider parameters
●   Form will submit quotation and author
●   Viewing will require quotation_id
●   Module will also be a plugin module, so
    we want to give it its own tag {quotation}
●   Let's create SetParameters method
SetParameters()
funcQon  SetParameters()
    {
    $this-­‐>RegisterModulePlugin();
    $this-­‐>RestrictUnknownParams();
    $this-­‐>CreateParameter('quotaQon_id','null',$this-­‐>Lang('help_quotaQ on_id'));
    $this-­‐>SetParameterType('quotaQon_id',  CLEAN_INT);
    $this-­‐>CreateParameter('author','anonymous',$this-­‐>Lang('help_author'));
    $this-­‐>SetParameterType('author',CLEAN_STRING);
    $this-­‐>CreateParameter('quotaQon','',$this-­‐>Lang('help_quotaQon'));
    $this-­‐>SetParameterType('quotaQon',CLEAN_STRING);
    $this-­‐>CreateParameter('submit');
    $this-­‐>SetParameterType('submit',CLEAN_STRING);
    }

funcQon  GetHelp()
    {
    return  $this-­‐>Lang('help');
    }
Viewing Module Help
OK, let's get serious.
●   Need to create the form
●   Need to handle the inputs
●   This will be the module's default action
●   Create action.default.php
action.default.php
<?php
if  (!isset($gCms))  exit;

$smarty-­‐>assign('form_start',$this-­‐>CreateFormStart($id,  'default',  $returnid));

$smarty-­‐>assign('input_author',$this-­‐>CreateInputTextWithLabel($id,  'author',
    isset($params['author'])?$params['author']:'',  10,  80,  '',
    $this-­‐>Lang('Qtle_author')));

$smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon'));

$smarty-­‐>assign('input_quotaQon',$this-­‐>CreateTextArea(false,  $id,
     isset($params['quotaQon'])?html_enQty_decode($params['quotaQon'],ENT_QUOTES):'',  
'quotaQon'));

$smarty-­‐>assign('submit',$this-­‐>CreateInputSubmit($id,  'submit',  $this-­‐>Lang('submit')));

echo  $this-­‐>ProcessTemplate('user_form.tpl');
?>
user_form.tpl
<div  class="quotaQon_form">
      {$form_start}
      <div>{$input_author}</div>
      <div>{$Qtle_quotaQon}<br  />{$input_quotaQon}</div>
      <div>{$submit}</div>
      </form>
</div>
Add the tag to a page
View the page
Update our lang file
<?php
$lang['friendlyname']='QuotaQon  Module';

$lang['Qtle_author']='QuotaQon  Author';
$lang['Qtle_quotaQon']='QuotaQon';
$lang['submit']='Submit';

$lang['help_quotaQon_id']='ID  for  a  specific  quotaQon';
$lang['help_author']='QuotaQon  author';
$lang['help_quotaQon']='QuotaQon  text';
$lang['help']='This  module  is  for  the  presentaQon  of  quotaQons.';
?>
And check again
Input validation
Added to top of action.default.php
if  (isset($params['submit']))
        {
        if  (empty($params['author'])  ||  empty($params['quotaQ on']))
               {
               $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds'));
               }
        }


Added to user_form.tpl
     {if  isset($message)  &&  $message!=''}<div  class="error">{$message}</div>{/if}
Try an empty submit
Store to database
if  (isset($params['submit']))
        {
        if  (empty($params['author'])  ||  empty($params['quotaQ on']))
               {
               $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds'));
               }
        else
               {
               $db  =  $this-­‐>GetDb();
               $quotaQon_id  =  $db-­‐>GenID(cms_db_prefix().  'module_quotaQon_seq');
               $res  =  $db-­‐>Execute('insert  into  '.cms_db_prefi x().
                       'module_quotaQons  (quotaQon_id,author,quotaQon,submiRed_date)  values  (?,?,?,NOW())',
                       array($quotaQon_id,$params['author'],$params['quotaQon']));
               if  ($res  ===  false)
                       {
                       $smarty-­‐>assign('message',$this-­‐>Lang('db_error',$db-­‐>ErrorMsg()));
                       }
               else
                       {
                       $smarty-­‐>assign('message',$this-­‐>Lang('added'));
                       }
               }
        }
Test it – add a quotation
action.display.php
<?php
if  (!isset($gCms))  exit;

$db=$this-­‐>GetDb();
$row  =  array();
$res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefix().
      'module_quotaQons  order  by  rand()  limit  1');

if  ($res  &&  $row=$res-­‐>FetchRow())
       {
       $smarty-­‐>assign('quotaQon',$row);
       }
echo  $this-­‐>ProcessTemplate('display_quotaQ on.tpl');
?>
display_quotation.tpl
<dl>
     <dd>{$quotaQon.quotaQon}</dd>
     <dt>{$quotaQon.author}</dt>
</dl>
Add the tag to page template
●   Find a convenient spot
●   Add the tag:
{QuotaQons  acQon='display'}
Try it!
2.0 Module API
2.0  Module  Extensions:

Database:
    create_table($table,  $fields)
    create_index($table,  $name,  $field)
    drop_table($table)

Preference:  
     get($preference_name,  $default_value  =  '')
     set($preference_name,  $value)
     remove($preference_name  =  '')

Template:  
    get_list($template_type  =  '')
    get($template_type,  $template_name)
    get_from_file($template_name)
    set($template_type,  $template_name,  $content,  $default  =  null)
    delete($template_type  =  '',  $template_name  =  '')
    process_from_data(  $data  )
    process_from_database($template_type,  $template_name  =  '',  $id  =  '',  $return_id  =  '',  
$designaQon  =  '',  $cache_id  =  '')
    process($template_name,  $id  =  '',  $return_id  =  '',  $designaQ on  =  '',  $cache_id  =  '')
method.install.php
<?php
if  (!isset($gCms))  exit;

$this-­‐>Database-­‐>create_table('module_quotaQ ons',  "
     quotaQon_id  I  KEY  AUTO,
     language  C(8),
     quotaQon  X,
     author  C(80),
     submiRer  C(80),
     submit_date  DT
");

$this-­‐>Database-­‐>create_index('module_quotaQ ons','Author',  'author');

$this-­‐>Template-­‐>set('summary',  'Default  Template',  $this-­‐>Template-­‐
>get_from_file('orig_default_summary'));

$this-­‐>Preference-­‐>set('sort_by','author');

$this-­‐>Permission-­‐>create('Manage  QuotaQons'  );

?>
2.0 Module API
2.0 Module Extensions (Group 2)

Permission:
   create($permission_name, $extra_attr = '', $hierarchical = false, $table = '')
   check($permission_name, $extra_attr = '', $hierarchical = false, $table = '')
   remove($permission_name, $extra_attr = '')

Form:
   form_start($params = array(), $check_keys = false)
   form_end input_text input_hidden input_checkbox input_submit
   input_select           input_options input_textarea

Redirect:
     module_url
Url:
     link   content_linkreturn_link
action.default.php
<?php
if  (!isset($gCms))  exit;

$smarty-­‐>assign('form_start',$this-­‐>Form-­‐
>form_start(array('acQon'=>'default','return_id'=>$returnid))  );

$smarty-­‐>assign('input_author',$this-­‐>Form-­‐>input_text(array('id'=>$id,'name'=>  
'author','value'=>
      isset($params['author'])?$params['author']:'',  'size'=>10,'maxlength=>80,  
'lable'=>'Qtle_author'))  );

$smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon'));

$smarty-­‐>assign('submit',$this-­‐>Form-­‐>input_submit('name'=>  'submit',  'value'=>'submit')));

echo  $this-­‐>Template-­‐> process_from_database ('summary');
?>
2.0 Smarty Tags
{has_permission  perm="Modify  Templates"}
{/has_permission}

{tabs}
    {tab_content  name='users'}
        {tab_header  name='users'}
        {/tab_header}
    {/tab_content}
{/tabs}

{tr}users{/tr}
Blocks:  2.0 Module Smarty Tags
    {mod_form}{/mod_form}
    {mod_formrow}{/mod_formrow}
    {mod_label}{/mod_label}
    {mod_select}{/mod_select}

FuncQons:
    {mod_checkbox}
    {mod_dropdown}
    {mod_helptext}
    {mod_hidden}
    {mod_lang}
    {mod_link}
    {mod_opQons}
    {mod_password}
    {mod_submit}
    {mod_template}
    {mod_textarea}
    {mod_textbox}
    {mod_validaQon_errors}
2.0 Module Form
{mod_validaQon_errors  for=$blog_post}

{mod_form  acQon=$form_acQon}
         {mod_label  name="blog_post[Qtle]"}Title{/mod_label}:<br  />
         {mod_textbox  name="blog_post[Qtle]"  value=$blog_post-­‐>Qtle  size="40"}
         {mod_label  name="blog_post[content]"}Post{/mod_label}:<br  />
         {mod_textarea  name="blog_post[content]"  value=$blog_post-­‐>content  cols="40"  
rows="10"  wysiwyg=true}
              <legend>{tr}Categories{/tr}</legend>
              {foreach  from=$categories  item='one_category'}
                   {assign  var=category_id  value=$one_category-­‐>id}
                   {assign  var=category_name  value=$one_category-­‐>name}
                   {mod_checkbox  name="blog_post[category][$category_id]"  
selected=$blog_post-­‐>in_category($category_id)}  {$category_name}<br  />
              {/foreach}
         {mod_label  name="blog_post[status]"}{tr}Status{/tr}{/mod_label}:<br  />
         {mod_dropdown  name="blog_post[status]"  items=$cms_mapi_module-­‐>get_statuses()  
selected_value=$blog_post-­‐>status}
2.0 Module Form (Part 2)
        {mod_label  name='post_date_Month'}{tr}Post  Date{/tr}{/mod_label}:<br  />
        {html_select_date  prefix=$post_date_prefix  Qme=$blog_post-­‐>post_date-­‐
>Qmestamp()  start_year=2000  end_year=2020}  {html_select_Q me  prefix=$post_date_prefix  
Qme=$blog_post-­‐>post_date-­‐>Qmestamp()}
        {mod_hidden  name="blog_post[author_id]"  value=$blog_post-­‐>author_id}
        {mod_submit  name="submitpost"  value='Submit'  translate=true}  
        {if  $blog_post-­‐>id  gt  0}
               {mod_hidden  name="blog_post_id"  value=$blog_post-­‐>id}
               {mod_submit  name="cancelpost"  value='Cancel'  translate=true}  
        {/if}
        {mod_submit  name="submitpublish"  value='Publish'  translate=true}

{/mod_form}
OK, let's add an admin panel
●   First, we need to add a permission to
    control access
●   This should be done in the installer
●   Steps:
    ●   Change installer
    ●   Bump the module version number
    ●   Create the upgrade method
method.install.php
<?php
if  (!isset($gCms))  exit;
$db  =  $this-­‐>GetDb();
$dict  =  NewDataDicQonary(  $db  );

$fields="
    quotaQon_id  I  KEY,
    language  C(8),
    quotaQon  X,
    author  C(80),
    submiRer  C(80),
    submit_date  DT
";

$sqlarray  =  $dict-­‐>CreateTableSQL(  cms_db_prefix()."module_quotaQons",$fields);
$dict-­‐>ExecuteSQLArray($sqlarray);

$db-­‐>CreateSequence(cms_db_prefix()."module_quotaQon_seq");
$this-­‐>CreatePermission('Manage  QuotaQons',  'Manage  QuotaQons');
?>
method.upgrade.php
<?php
if  (!isset($gCms))  exit;

switch($old_version)
     {
     case  "0.1":
           $this-­‐>CreatePermission('Manage  QuotaQons',  'Manage  QuotaQons');
     }

$this-­‐>Audit(  0,  $this-­‐>Lang('friendlyname'),  $this-­‐>Lang('upgraded',$this-­‐>GetVersion()));
?>

Added to lang file:
$lang['upgraded']='Upgraded  to  version  %s';
Perform the upgrade
Now implement admin panel
              1.x
●   Add method to main module:
     function HasAdmin()
         {
         return ($this->CheckPermission('Manage Quotations'));
         }
action.defaultadmin.php
<?php
if  (!isset($gCms))  exit;
if  (!$this-­‐>CheckPermission('Manage  QuotaQons'))  exit;

$db=$this-­‐>GetDb();
$rows  =  array();
$res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefix().'module_quotaQons  order  by  submit_date  
desc');
while  ($res  &&  $row=$res-­‐>FetchRow())
      {
      $row['edit']  =  $this-­‐>CreateLink($id,  'admin_edit',  '',
            $this-­‐>Lang('edit'),  array('quotaQon_id'=>$row['quotaQon_id']));
      $row['delete']  =  $this-­‐>CreateLink($id,  'admin_delete',  '',
            $this-­‐>Lang('delete'),  array('quotaQon_id'=>$row['quotaQon_id']));
      array_push($rows,$row);
      }
$smarty-­‐>assign('quotaQons',$rows);
echo  $this-­‐>ProcessTemplate('admin_display_list.tpl');
?>
admin_display_list.tpl
{if  isset($message)  &&  $message!=''}<div  class="pagewarning">{$message}</div>{/if}
<table>
{foreach  from=$quotaQons  item=q}
        <tr>
             <td>{$q.quotaQon_id}</td>
             <td>{$q.quotaQon|truncate:80}</td>
             <td>{$q.author}</td>
             <td>{$q.edit}</td>
             <td>{$q.delete}</td>
        </tr>
{/foreach}
</table>
Admin View
action.admin_delete.php
<?php
if  (!isset($gCms))  exit;
if  (!$this-­‐>CheckPermission('Manage  QuotaQons'))  exit;

if  (isset($params['quotaQon_id']))
        {
        $db  =  $this-­‐>GetDb();
        $res  =  $db-­‐>Execute('delete  from  '.cms_db_prefi x().
              'module_quotaQons  where  quotaQon_id=?',
              array($params['quotaQon_id']));
        $smarty-­‐>assign('message',$this-­‐>Lang('quotaQ on_deleted'));
        }
echo  $this-­‐>DoAcQon('defaultadmin',  $id,  $params,  $returnid);
?>
Delete Quotation
Be kind to users


●   Add some safety – modify default admin
    link to delete
$row['delete']  =  $this-­‐>CreateLink($id,  'admin_delete',  '',
    $this-­‐>Lang('delete'),  array('quotaQon_id'=>$row['quotaQon_id']),
    $this-­‐>Lang('really_delete',$row['author']));



●   And add to language file
$lang['really_delete']='Really  delete  this  quotaQon  by  %s?';
That's better
Let admin edit quotations
●   We can re-use the form template
●   Just need to create the admin-side logic
●   Note that the logic is not identical, since
    we'll be updating the database rather than
    adding a new record
action.admin_edit.php
<?php
if  (!isset($gCms))  exit;
if  (!$this-­‐>CheckPermission('Manage  QuotaQ ons'))  exit;
$db  =  $this-­‐>GetDb();

if  (isset($params['submit']))
         {
         if  (empty($params['author'])  ||  empty($params['quotaQ on']))
                {
                $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds'));
                }
         else
                {
                $res  =  $db-­‐>Execute('update  '.cms_db_prefi x().'module_quotaQons  set  author=?,quotaQon=?  where  quotaQon_id=?',
                        array($params['author'],$params['quotaQ on'],$params['quotaQon_id']));
                if  ($res  ===  false)
                        {
                        $smarty-­‐>assign('message',$this-­‐>Lang('db_error',$db-­‐>ErrorMsg()));
                        }
                else
                        {
                        $smarty-­‐>assign('message',$this-­‐>Lang('quotaQ on_edited'));
                        }
                return  $this-­‐>DoAcQon('defaultadmin',  $id,  $params,  $returnid);
                }
         }
else
action.admin_edit.php, cont
     {
     $res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefi x().'module_quotaQons  where  quotaQon_id=?',
              array($params['quotaQon_id']));
     if  ($res  &&  $row=$res-­‐>FetchRow())
              {
              $params['author']=$row['author'];
              $params['quotaQon']=$row['quotaQon'];
              }
     }
$smarty-­‐>assign('form_start',$this-­‐>CreateFormStart($id,  'admin_edit',  $returnid));

$smarty-­‐>assign('input_author',$this-­‐>CreateInputTextWithLabel($id,  'author',
     isset($params['author'])?$params['author']:'',  10,  80,  '',
     $this-­‐>Lang('Qtle_author')));

$smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon'));

$smarty-­‐>assign('input_quotaQ on',$this-­‐>CreateTextArea(false,  $id,
     isset($params['quotaQon'])?html_enQty_decode($params['quotaQ on'],ENT_QUOTES):'',  'quotaQ on'));

$smarty-­‐>assign('submit', $this-­‐>CreateInputHidden($id,'quotaQ on_id',$params['quotaQ on_id']).
     $this-­‐>CreateInputSubmit($id,  'submit',  $this-­‐>Lang('submit')));

echo  $this-­‐>ProcessTemplate('user_form.tpl');
?>
Editing
And edited
action.defaultadmin.php 2.0
if  (!isset($gCms))  die("Can't  call  acQons  directly!");

if  (  !$this-­‐>Permission-­‐>check('Modify  Site  Preferences')  )
                    CmsResponse::redirect('index.php?'.CMS_SECURE_PARAM_NAME.'='.
$_SESSION[CMS_USER_KEY]);

if  (isset($params['submitprefs'])  )
{
        if(  !CmsAcl::check_core_permission('Modify  Site  Preferences',  $user)  )
                   die('permission  denied');

     $password_minlength  =  (int)coalesce_key($params,'password_minlength',6);
     $this-­‐>Preference-­‐>set('password_minlength',$password_minlength);
}
$smarty-­‐>assign('groups',cms_orm('CmsGroup')-­‐>fi nd_all());
$smarty-­‐>assign('users',cms_orm('CmsUser')-­‐>fi nd_all());

$smarty-­‐>assign('acQve_tab_for_modules',                  coalesce_key($params,'selected_tab','users'));
$smarty-­‐>assign('form_acQon','defaultadmin');

echo  $this-­‐>Template-­‐>process('defaultadmin.tpl',$id,$return_id);
defaultadmin.tpl 2.0
{has_permission  perm="Modify  Templates"}
     <div  style="text-­‐align:  right;  width:  80%;"><a  href="listmodtemplates.php"  
Qtle="{tr}modify_templates{/tr}">{tr}modify_templates{/tr}</a></div><br/>
{/has_permission}

{tabs}
     {has_permission  perm='Modify  Users'}
          {tab_content  name='users'}
               {tab_header  name='users'}{tr}users{/tr}{/tab_header}
               {mod_template  template='users_tab.tpl'}
          {/tab_content}
     {/has_permission}

     {has_permission  perm='Modify  Site  Preferences'}
          {tab_content  name='prefs'}
               {tab_header  name='prefs'}{tr}preferences{/tr}{/tab_header}
               {mod_template  template='prefs_tab.tpl'}
          {/tab_content}
     {/has_permission}
{/tabs}

Contenu connexe

Similaire à Extending CMS Made Simple

Drupalcamp performance
Drupalcamp performanceDrupalcamp performance
Drupalcamp performanceFrontkom
 
HTML, CSS & Javascript Architecture (extended version) - Jan Kraus
HTML, CSS & Javascript Architecture (extended version) - Jan KrausHTML, CSS & Javascript Architecture (extended version) - Jan Kraus
HTML, CSS & Javascript Architecture (extended version) - Jan KrausWomen in Technology Poland
 
Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Drupalcon Paris
 
Becoming A Drupal Master Builder
Becoming A Drupal Master BuilderBecoming A Drupal Master Builder
Becoming A Drupal Master BuilderPhilip Norton
 
Building and Maintaining a Distribution in Drupal 7 with Features
Building and Maintaining a  Distribution in Drupal 7 with FeaturesBuilding and Maintaining a  Distribution in Drupal 7 with Features
Building and Maintaining a Distribution in Drupal 7 with FeaturesNuvole
 
Cross-Platform Mobile Development with Ionic Framework and Angular
Cross-Platform Mobile Development with Ionic Framework and AngularCross-Platform Mobile Development with Ionic Framework and Angular
Cross-Platform Mobile Development with Ionic Framework and AngularMovel
 
Decoupling Drupal mit dem Lupus Nuxt.js Drupal Stack
Decoupling Drupal mit dem Lupus Nuxt.js Drupal StackDecoupling Drupal mit dem Lupus Nuxt.js Drupal Stack
Decoupling Drupal mit dem Lupus Nuxt.js Drupal Stacknuppla
 
Dolibarr - information for developers and partners - devcamp lyon 2019
Dolibarr - information for developers and partners - devcamp lyon 2019Dolibarr - information for developers and partners - devcamp lyon 2019
Dolibarr - information for developers and partners - devcamp lyon 2019Laurent Destailleur
 
Thinking in Components
Thinking in ComponentsThinking in Components
Thinking in ComponentsAnton Ivanov
 
GDayX - Advanced Angular.JS
GDayX - Advanced Angular.JSGDayX - Advanced Angular.JS
GDayX - Advanced Angular.JSNicolas Embleton
 
Getting started with the Lupus Nuxt.js Drupal Stack
Getting started with the Lupus Nuxt.js Drupal StackGetting started with the Lupus Nuxt.js Drupal Stack
Getting started with the Lupus Nuxt.js Drupal Stacknuppla
 
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...Alessandro Molina
 
Drupal 8 - Core and API Changes
Drupal 8 - Core and API ChangesDrupal 8 - Core and API Changes
Drupal 8 - Core and API ChangesShabir Ahmad
 
Architektura html, css i javascript - Jan Kraus
Architektura html, css i javascript - Jan KrausArchitektura html, css i javascript - Jan Kraus
Architektura html, css i javascript - Jan KrausWomen in Technology Poland
 
Implementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyImplementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyMarcos Labad
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - TryoutMatthias Noback
 
WordPress Plugin Development 201
WordPress Plugin Development 201WordPress Plugin Development 201
WordPress Plugin Development 201ylefebvre
 
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsEP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsAlessandro Molina
 
Using WordPress for Rapid Prototyping
Using WordPress for Rapid PrototypingUsing WordPress for Rapid Prototyping
Using WordPress for Rapid PrototypingDrew Morris
 

Similaire à Extending CMS Made Simple (20)

Drupalcamp performance
Drupalcamp performanceDrupalcamp performance
Drupalcamp performance
 
HTML, CSS & Javascript Architecture (extended version) - Jan Kraus
HTML, CSS & Javascript Architecture (extended version) - Jan KrausHTML, CSS & Javascript Architecture (extended version) - Jan Kraus
HTML, CSS & Javascript Architecture (extended version) - Jan Kraus
 
Drupal Best Practices
Drupal Best PracticesDrupal Best Practices
Drupal Best Practices
 
Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3Staging Drupal 8 31 09 1 3
Staging Drupal 8 31 09 1 3
 
Becoming A Drupal Master Builder
Becoming A Drupal Master BuilderBecoming A Drupal Master Builder
Becoming A Drupal Master Builder
 
Building and Maintaining a Distribution in Drupal 7 with Features
Building and Maintaining a  Distribution in Drupal 7 with FeaturesBuilding and Maintaining a  Distribution in Drupal 7 with Features
Building and Maintaining a Distribution in Drupal 7 with Features
 
Cross-Platform Mobile Development with Ionic Framework and Angular
Cross-Platform Mobile Development with Ionic Framework and AngularCross-Platform Mobile Development with Ionic Framework and Angular
Cross-Platform Mobile Development with Ionic Framework and Angular
 
Decoupling Drupal mit dem Lupus Nuxt.js Drupal Stack
Decoupling Drupal mit dem Lupus Nuxt.js Drupal StackDecoupling Drupal mit dem Lupus Nuxt.js Drupal Stack
Decoupling Drupal mit dem Lupus Nuxt.js Drupal Stack
 
Dolibarr - information for developers and partners - devcamp lyon 2019
Dolibarr - information for developers and partners - devcamp lyon 2019Dolibarr - information for developers and partners - devcamp lyon 2019
Dolibarr - information for developers and partners - devcamp lyon 2019
 
Thinking in Components
Thinking in ComponentsThinking in Components
Thinking in Components
 
GDayX - Advanced Angular.JS
GDayX - Advanced Angular.JSGDayX - Advanced Angular.JS
GDayX - Advanced Angular.JS
 
Getting started with the Lupus Nuxt.js Drupal Stack
Getting started with the Lupus Nuxt.js Drupal StackGetting started with the Lupus Nuxt.js Drupal Stack
Getting started with the Lupus Nuxt.js Drupal Stack
 
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
PyconIE 2016 - Kajiki, the fast and validated template engine your were looki...
 
Drupal 8 - Core and API Changes
Drupal 8 - Core and API ChangesDrupal 8 - Core and API Changes
Drupal 8 - Core and API Changes
 
Architektura html, css i javascript - Jan Kraus
Architektura html, css i javascript - Jan KrausArchitektura html, css i javascript - Jan Kraus
Architektura html, css i javascript - Jan Kraus
 
Implementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing CompanyImplementing a Symfony Based CMS in a Publishing Company
Implementing a Symfony Based CMS in a Publishing Company
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - Tryout
 
WordPress Plugin Development 201
WordPress Plugin Development 201WordPress Plugin Development 201
WordPress Plugin Development 201
 
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For AssetsEP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
EP2016 - Moving Away From Nodejs To A Pure Python Solution For Assets
 
Using WordPress for Rapid Prototyping
Using WordPress for Rapid PrototypingUsing WordPress for Rapid Prototyping
Using WordPress for Rapid Prototyping
 

Dernier

What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfMounikaPolabathina
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 

Dernier (20)

What is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdfWhat is DBT - The Ultimate Data Build Tool.pdf
What is DBT - The Ultimate Data Build Tool.pdf
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 

Extending CMS Made Simple

  • 1. Extending CMS Made Simple Presented by Jeff Bosch and Samuel Goldstein
  • 2. Jeff Bosch ● Core Development Team member ● B.S. in Computer Science ● Started A & J Progamming in 2007 for custom web programming and implementation
  • 3. Samuel Goldstein ● Core Development Team member since 2004 ● Principal at 1969 Communications ● Background ● Programming since 1980, starting with TRS-80 ● Aerospace in the '90s ● Dot-com roller coaster ● Hey! Buy my book! ● CMS Made Simple Developer's Cookbook from Packt Publishing
  • 4. OK, enough of that ● Why extend CMS Made Simple? ● Add functionality ● Make things easier for your clients ● More money for you ● Fame, fortune, glory
  • 5. Ways to Extend CMSMS ● Core, core modules, and Smarty ● User-Defined Tags ● Tags (a.k.a. Plugins) ● Modules
  • 6. How do I decide on an approach? ● There is not always an “absolute best” approach ● Don't neglect non-technical considerations (e.g., your specific users) ● The following chart may also help
  • 7. Approaches Smarty Tag UDT Module No installation required √ √ Multiple actions √* √ Create databases / preferences √* √ Act as Smarty modifier √ Help displayed in Admin area √ √ Admin panel √ Post on Developer's Forge, share via Module √ Manager Localization/Translation √
  • 8. Let's get started! ● Build a Google Site Map using Core and Core Modules ● We'll use Menu Manager in a hidden page ● We'll create a custom Menu Manager template ● And a mod_rewrite trick
  • 9. Menu Manager Template <?xml  version="1.0"  encoding="UTF-­‐8"?> <urlset  xmlns="hRp://www.sitemaps.org/schemas/sitemap/0.9"> {if  $count  >  0} {foreach  from=$nodelist  item=node}  <url>        <loc>{$node-­‐>url|escape:'html'}</loc>        <lastmod>{$node-­‐>modified|date_format:'%Y-­‐%m-­‐%dT%T-­‐08:00'}</lastmod>    </url> {/foreach} {/if} </urlset>
  • 10. Calling the Template ● Create page named “sitemap” ● Set alias to “sitemap” too ● Uncheck “Show in Menu” ● Insert tag into your page content: {menu  template='sitemap'  show_all='1'  collapse='0'}
  • 11. Add an .htaccess file <IfModule  mod_rewrite.c> RewriteEngine  on #Sub-­‐dir  e.g:  /cmsms RewriteBase  / RewriteCond  %{REQUEST_FILENAME}  !-­‐f  [NC] RewriteCond  %{REQUEST_FILENAME}  !-­‐d  [NC] RewriteRule  ^sitemap.xml$  index.php?page=sitemap&showtemplate=false  [L] </IfModule>
  • 12. Test it! ● Go to http://yoursite.com/sitemap.xml <?xml  version="1.0"  encoding="UTF-­‐8"?> <urlset  xmlns="hRp://www.sitemaps.org/schemas/sitemap/0.9">    <url>        <loc>hRp://cms_book.viajante/</loc>        <lastmod>2009-­‐05-­‐13T10:12:18-­‐08:00</lastmod>    </url>    <url>        <loc>hRp://cms_book.viajante/index.php?page=how-­‐cmsms-­‐works</loc>        <lastmod>2009-­‐05-­‐12T20:11:52-­‐08:00</lastmod>    </url>  … </urlset>
  • 13. OK, how about a Tag? ● Only tags can serve as Smarty modifiers ● Here's a tag that vigorously defends our intellectual property! ● Appends a ® symbol to every instance of “CMS Made Simple” in page content.
  • 14. postfilter.registeredtrademarker.php <?php funcQon  smarty_cms_posPilter_registeredtrademarker($tpl_output,  &$smarty) { global  $gCms; $result  =  explode(':',  $smarty-­‐>_current_file); if  (count($result)  >  0) { if  ($result[0]  ==  'content') { $tpl_output  =  str_replace('CMS  Made  Simple','CMS  Made  Simple®',  $tpl_output); } } return  $tpl_output; } ?>
  • 15. Using the Filter ● Save your filter in the plugins directory ● In the Admin, be sure to clear your cache! ● View your site.
  • 17. OK, how about a UDT? ● UDTs can do all sorts of things: ● Talk to the database ● Talk to modules ● Deal with events ● Interact with Smarty ● Let's try a few of those things!
  • 18. UDT “pagecounter” global  $gCms; $db  =  $gCms-­‐>GetDb(); $count  =  $db-­‐>GetOne('select  count(*)  from  '.cms_db_prefi x().'content  where  acQve=1'); $smarty  =  $gCms-­‐>GetSmarty(); $smarty-­‐>assign('page_count','Total  pages:  '.$count);
  • 19. Then, add a content page ● Set the title and menu text ● In the content, call the UDT with {pagecounter} ● And output the Smarty variable with {$page_count}
  • 21. Exercise I ● What approach would you use to implement each of the following? ● Convert your page content into Pirate Speak ● A form for users to submit movie reviews, with an Admin panel allowing the site maintainer to approve or reject reviews for display ● Putting the title of the most-recently added page into a Smarty variable
  • 22. Exercise I, cont. ● Display a special image file only on the site's home page ● Display the Admin account email address on a page (but obscured to prevent spammers from harvesting it) ● Display a poll, and show the results after the user votes ● Display links to all pages that are below the current page in the site hierarchy
  • 23. Exercise II ● Create a UDT for changing site layout seasonally. ● Set a variable which can be used to change the stylesheet classes of the site. ● For simplicity, call January-March “winter,” April-June “spring,” July-September “summer,” and October-December “autumn.”
  • 24. Exercise II, cont. UDT global  $gCms; $month  =  date('n');  //  returns  month  as  number  between  1  and  12,  inclusive $smarty  =  $gCms-­‐>GetSmarty(); if  ($month  <  4) $season  =  'winter'; else  if  ($month  <  7) $season  =  'spring'; else  if  ($month  <  10) $season  =  'summer'; else $season  =  'autumn'; $smarty-­‐>assign('season',  $season);
  • 25. Exercise II, cont. Template. {process_pagedata} <!DOCTYPE  html  PUBLIC  "-­‐//W3C//DTD  XHTML  1.0  TransiQ onal//EN"   "hRp://www.w3.org/TR/xhtml1/DTD/xhtml1-­‐transiQ onal.dtd"> <html  xmlns="hRp://www.w3.org/1999/xhtml"  xml:lang="en"  > <head> <Qtle>{sitename}  -­‐  {Qtle}</Qtle> {metadata} {cms_stylesheet} {season} </head> <body> <div  id="header"><h1  class=”{$season}”>{sitename}</h1></div> <div  id="menu">{menu}</div> <div  id="content"  class=”{$season}”>    <h1>{Qtle}</h1>    {content} </div> </body> </html>
  • 26. Exercise II, cont. CSS .winter  {background-­‐color:  #bfc4ff;} .spring  {background-­‐color:  #bfffce;} .summer  {background-­‐color:  #fffabf;} .autumn  {background-­‐color:  #ffcabf;} #header  h1.summer  {background:  url(/uploads/images/beach.jpg);}
  • 27. Hour II How's my timing?
  • 28. Getting Started with Modules ● For the 1.x series, several ways to get started ● From scratch ● Skeleton module ● Module Maker ● MCFactory ● etc
  • 29. Skeleton Module ● Download from Forge or Module Manager ● Has simple functionality ● install/upgrade ● admin panel ● database routines ● form API calls
  • 30. Skeleton Module, cont. ● Once you've installed, edit the files to adapt to your own purposes ● Well commented ● Somewhat dated: last updated for CMS MS 1.6.5 ● Still has correct structure, but may lack more recent improvements and features
  • 31. Structure of a 1.x module ● Modules extend the CMSModule class ● Magic of OO: ● Automatically inherits a lot of functionality (e.g., Database and Form APIs, Smarty, Translation, etc) ● Most methods already inherit acceptable defaults
  • 32. Minimal 1.x Module ● File lives in $CMS_ROOT/modules/module_name ● Filename is module_name.module.php ● Module class must match file name ● Need to implement 3 methods: ● GetName ● DoAction ● IsPluginModule
  • 33. Minimal 1.x module, cont. ● GetName simply returns module name; must match class and filename ● IsPluginModule returns boolean. ● DoAction is, unsurprisingly, where the real action takes place
  • 34. DoAction ● Called with a few parameters: ● $action, “reason for calling.” Two built-in: “default” and “defaultadmin” ● $id, a prefix for use in forms ● $params, a hash of parameters ● $returnid, reference to the page containing the tag
  • 35. Other important methods ● Install, upgrade, uninstall. These methods called as expected, and allow for housekeeping ● SetParameters. Sets up parameter filtering, URL routes, ● Lang. Translates strings.
  • 36. Minimal 1.x module, cont. ● For performance reasons, methods are split out into multiple files ● String translations are stored in external files ● Templates may be stored as files or in the database
  • 38. Enough Abstraction! ● We will create a basic module that demonstrates core APIs ● “Quotations Module” requirements: ● Presents form to user, handles input safely ● Displays random quotation to user ● Allows admin to delete records
  • 39. Step 1. Preparation ● Create directories ● $CMS_ROOT/modules/Quotations ● Quotations/templates ● Quotations/lang ● Create base files: ● Quotations.module.php ● lang/en_US.php
  • 40. Quotations.module.php <?php class  QuotaQons  extends  CMSModule { funcQon  GetName() { return  'QuotaQons'; } funcQon  GetFriendlyName() { return  $this-­‐>Lang('friendlyname'); } funcQon  IsPluginModule() { return  true; } funcQon  GetVersion() { return  '0.1'; } } ?>
  • 42. Database Schema ● We'll probably need ● Unique ID ● Quotation text ● Quotation author ● Submission date
  • 43. method.install.php <?php if  (!isset($gCms))  exit; $db  =  $this-­‐>GetDb(); $dict  =  NewDataDicQonary(  $db  ); $fields=" quotaQon_id  I  KEY, quotaQon  X, author  C(80), submit_date  DT "; $sqlarray  =  $dict-­‐>CreateTableSQL(  cms_db_prefix()."module_quotaQons",$fields); $dict-­‐>ExecuteSQLArray($sqlarray); $db-­‐>CreateSequence(cms_db_prefix()."module_quotaQon_seq"); ?>
  • 45. Consider parameters ● Form will submit quotation and author ● Viewing will require quotation_id ● Module will also be a plugin module, so we want to give it its own tag {quotation} ● Let's create SetParameters method
  • 46. SetParameters() funcQon  SetParameters() { $this-­‐>RegisterModulePlugin(); $this-­‐>RestrictUnknownParams(); $this-­‐>CreateParameter('quotaQon_id','null',$this-­‐>Lang('help_quotaQ on_id')); $this-­‐>SetParameterType('quotaQon_id',  CLEAN_INT); $this-­‐>CreateParameter('author','anonymous',$this-­‐>Lang('help_author')); $this-­‐>SetParameterType('author',CLEAN_STRING); $this-­‐>CreateParameter('quotaQon','',$this-­‐>Lang('help_quotaQon')); $this-­‐>SetParameterType('quotaQon',CLEAN_STRING); $this-­‐>CreateParameter('submit'); $this-­‐>SetParameterType('submit',CLEAN_STRING); } funcQon  GetHelp() { return  $this-­‐>Lang('help'); }
  • 48. OK, let's get serious. ● Need to create the form ● Need to handle the inputs ● This will be the module's default action ● Create action.default.php
  • 49. action.default.php <?php if  (!isset($gCms))  exit; $smarty-­‐>assign('form_start',$this-­‐>CreateFormStart($id,  'default',  $returnid)); $smarty-­‐>assign('input_author',$this-­‐>CreateInputTextWithLabel($id,  'author', isset($params['author'])?$params['author']:'',  10,  80,  '', $this-­‐>Lang('Qtle_author'))); $smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon')); $smarty-­‐>assign('input_quotaQon',$this-­‐>CreateTextArea(false,  $id, isset($params['quotaQon'])?html_enQty_decode($params['quotaQon'],ENT_QUOTES):'',   'quotaQon')); $smarty-­‐>assign('submit',$this-­‐>CreateInputSubmit($id,  'submit',  $this-­‐>Lang('submit'))); echo  $this-­‐>ProcessTemplate('user_form.tpl'); ?>
  • 50. user_form.tpl <div  class="quotaQon_form"> {$form_start} <div>{$input_author}</div> <div>{$Qtle_quotaQon}<br  />{$input_quotaQon}</div> <div>{$submit}</div> </form> </div>
  • 51. Add the tag to a page
  • 53. Update our lang file <?php $lang['friendlyname']='QuotaQon  Module'; $lang['Qtle_author']='QuotaQon  Author'; $lang['Qtle_quotaQon']='QuotaQon'; $lang['submit']='Submit'; $lang['help_quotaQon_id']='ID  for  a  specific  quotaQon'; $lang['help_author']='QuotaQon  author'; $lang['help_quotaQon']='QuotaQon  text'; $lang['help']='This  module  is  for  the  presentaQon  of  quotaQons.'; ?>
  • 55. Input validation Added to top of action.default.php if  (isset($params['submit'])) { if  (empty($params['author'])  ||  empty($params['quotaQ on'])) { $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds')); } } Added to user_form.tpl {if  isset($message)  &&  $message!=''}<div  class="error">{$message}</div>{/if}
  • 56. Try an empty submit
  • 57. Store to database if  (isset($params['submit'])) { if  (empty($params['author'])  ||  empty($params['quotaQ on'])) { $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds')); } else { $db  =  $this-­‐>GetDb(); $quotaQon_id  =  $db-­‐>GenID(cms_db_prefix().  'module_quotaQon_seq'); $res  =  $db-­‐>Execute('insert  into  '.cms_db_prefi x(). 'module_quotaQons  (quotaQon_id,author,quotaQon,submiRed_date)  values  (?,?,?,NOW())', array($quotaQon_id,$params['author'],$params['quotaQon'])); if  ($res  ===  false) { $smarty-­‐>assign('message',$this-­‐>Lang('db_error',$db-­‐>ErrorMsg())); } else { $smarty-­‐>assign('message',$this-­‐>Lang('added')); } } }
  • 58. Test it – add a quotation
  • 59. action.display.php <?php if  (!isset($gCms))  exit; $db=$this-­‐>GetDb(); $row  =  array(); $res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefix(). 'module_quotaQons  order  by  rand()  limit  1'); if  ($res  &&  $row=$res-­‐>FetchRow()) { $smarty-­‐>assign('quotaQon',$row); } echo  $this-­‐>ProcessTemplate('display_quotaQ on.tpl'); ?>
  • 60. display_quotation.tpl <dl> <dd>{$quotaQon.quotaQon}</dd> <dt>{$quotaQon.author}</dt> </dl>
  • 61. Add the tag to page template ● Find a convenient spot ● Add the tag: {QuotaQons  acQon='display'}
  • 63. 2.0 Module API 2.0  Module  Extensions: Database: create_table($table,  $fields) create_index($table,  $name,  $field) drop_table($table) Preference:   get($preference_name,  $default_value  =  '') set($preference_name,  $value) remove($preference_name  =  '') Template:   get_list($template_type  =  '') get($template_type,  $template_name) get_from_file($template_name) set($template_type,  $template_name,  $content,  $default  =  null) delete($template_type  =  '',  $template_name  =  '') process_from_data(  $data  ) process_from_database($template_type,  $template_name  =  '',  $id  =  '',  $return_id  =  '',   $designaQon  =  '',  $cache_id  =  '') process($template_name,  $id  =  '',  $return_id  =  '',  $designaQ on  =  '',  $cache_id  =  '')
  • 64. method.install.php <?php if  (!isset($gCms))  exit; $this-­‐>Database-­‐>create_table('module_quotaQ ons',  " quotaQon_id  I  KEY  AUTO, language  C(8), quotaQon  X, author  C(80), submiRer  C(80), submit_date  DT "); $this-­‐>Database-­‐>create_index('module_quotaQ ons','Author',  'author'); $this-­‐>Template-­‐>set('summary',  'Default  Template',  $this-­‐>Template-­‐ >get_from_file('orig_default_summary')); $this-­‐>Preference-­‐>set('sort_by','author'); $this-­‐>Permission-­‐>create('Manage  QuotaQons'  ); ?>
  • 65. 2.0 Module API 2.0 Module Extensions (Group 2) Permission: create($permission_name, $extra_attr = '', $hierarchical = false, $table = '') check($permission_name, $extra_attr = '', $hierarchical = false, $table = '') remove($permission_name, $extra_attr = '') Form: form_start($params = array(), $check_keys = false) form_end input_text input_hidden input_checkbox input_submit input_select input_options input_textarea Redirect: module_url Url: link content_linkreturn_link
  • 66. action.default.php <?php if  (!isset($gCms))  exit; $smarty-­‐>assign('form_start',$this-­‐>Form-­‐ >form_start(array('acQon'=>'default','return_id'=>$returnid))  ); $smarty-­‐>assign('input_author',$this-­‐>Form-­‐>input_text(array('id'=>$id,'name'=>   'author','value'=> isset($params['author'])?$params['author']:'',  'size'=>10,'maxlength=>80,   'lable'=>'Qtle_author'))  ); $smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon')); $smarty-­‐>assign('submit',$this-­‐>Form-­‐>input_submit('name'=>  'submit',  'value'=>'submit'))); echo  $this-­‐>Template-­‐> process_from_database ('summary'); ?>
  • 67. 2.0 Smarty Tags {has_permission  perm="Modify  Templates"} {/has_permission} {tabs} {tab_content  name='users'} {tab_header  name='users'} {/tab_header} {/tab_content} {/tabs} {tr}users{/tr}
  • 68. Blocks: 2.0 Module Smarty Tags {mod_form}{/mod_form} {mod_formrow}{/mod_formrow} {mod_label}{/mod_label} {mod_select}{/mod_select} FuncQons: {mod_checkbox} {mod_dropdown} {mod_helptext} {mod_hidden} {mod_lang} {mod_link} {mod_opQons} {mod_password} {mod_submit} {mod_template} {mod_textarea} {mod_textbox} {mod_validaQon_errors}
  • 69. 2.0 Module Form {mod_validaQon_errors  for=$blog_post} {mod_form  acQon=$form_acQon} {mod_label  name="blog_post[Qtle]"}Title{/mod_label}:<br  /> {mod_textbox  name="blog_post[Qtle]"  value=$blog_post-­‐>Qtle  size="40"} {mod_label  name="blog_post[content]"}Post{/mod_label}:<br  /> {mod_textarea  name="blog_post[content]"  value=$blog_post-­‐>content  cols="40"   rows="10"  wysiwyg=true} <legend>{tr}Categories{/tr}</legend> {foreach  from=$categories  item='one_category'} {assign  var=category_id  value=$one_category-­‐>id} {assign  var=category_name  value=$one_category-­‐>name} {mod_checkbox  name="blog_post[category][$category_id]"   selected=$blog_post-­‐>in_category($category_id)}  {$category_name}<br  /> {/foreach} {mod_label  name="blog_post[status]"}{tr}Status{/tr}{/mod_label}:<br  /> {mod_dropdown  name="blog_post[status]"  items=$cms_mapi_module-­‐>get_statuses()   selected_value=$blog_post-­‐>status}
  • 70. 2.0 Module Form (Part 2) {mod_label  name='post_date_Month'}{tr}Post  Date{/tr}{/mod_label}:<br  /> {html_select_date  prefix=$post_date_prefix  Qme=$blog_post-­‐>post_date-­‐ >Qmestamp()  start_year=2000  end_year=2020}  {html_select_Q me  prefix=$post_date_prefix   Qme=$blog_post-­‐>post_date-­‐>Qmestamp()} {mod_hidden  name="blog_post[author_id]"  value=$blog_post-­‐>author_id} {mod_submit  name="submitpost"  value='Submit'  translate=true}   {if  $blog_post-­‐>id  gt  0} {mod_hidden  name="blog_post_id"  value=$blog_post-­‐>id} {mod_submit  name="cancelpost"  value='Cancel'  translate=true}   {/if} {mod_submit  name="submitpublish"  value='Publish'  translate=true} {/mod_form}
  • 71. OK, let's add an admin panel ● First, we need to add a permission to control access ● This should be done in the installer ● Steps: ● Change installer ● Bump the module version number ● Create the upgrade method
  • 72. method.install.php <?php if  (!isset($gCms))  exit; $db  =  $this-­‐>GetDb(); $dict  =  NewDataDicQonary(  $db  ); $fields=" quotaQon_id  I  KEY, language  C(8), quotaQon  X, author  C(80), submiRer  C(80), submit_date  DT "; $sqlarray  =  $dict-­‐>CreateTableSQL(  cms_db_prefix()."module_quotaQons",$fields); $dict-­‐>ExecuteSQLArray($sqlarray); $db-­‐>CreateSequence(cms_db_prefix()."module_quotaQon_seq"); $this-­‐>CreatePermission('Manage  QuotaQons',  'Manage  QuotaQons'); ?>
  • 73. method.upgrade.php <?php if  (!isset($gCms))  exit; switch($old_version) { case  "0.1": $this-­‐>CreatePermission('Manage  QuotaQons',  'Manage  QuotaQons'); } $this-­‐>Audit(  0,  $this-­‐>Lang('friendlyname'),  $this-­‐>Lang('upgraded',$this-­‐>GetVersion())); ?> Added to lang file: $lang['upgraded']='Upgraded  to  version  %s';
  • 75. Now implement admin panel 1.x ● Add method to main module: function HasAdmin() { return ($this->CheckPermission('Manage Quotations')); }
  • 76. action.defaultadmin.php <?php if  (!isset($gCms))  exit; if  (!$this-­‐>CheckPermission('Manage  QuotaQons'))  exit; $db=$this-­‐>GetDb(); $rows  =  array(); $res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefix().'module_quotaQons  order  by  submit_date   desc'); while  ($res  &&  $row=$res-­‐>FetchRow()) { $row['edit']  =  $this-­‐>CreateLink($id,  'admin_edit',  '', $this-­‐>Lang('edit'),  array('quotaQon_id'=>$row['quotaQon_id'])); $row['delete']  =  $this-­‐>CreateLink($id,  'admin_delete',  '', $this-­‐>Lang('delete'),  array('quotaQon_id'=>$row['quotaQon_id'])); array_push($rows,$row); } $smarty-­‐>assign('quotaQons',$rows); echo  $this-­‐>ProcessTemplate('admin_display_list.tpl'); ?>
  • 77. admin_display_list.tpl {if  isset($message)  &&  $message!=''}<div  class="pagewarning">{$message}</div>{/if} <table> {foreach  from=$quotaQons  item=q} <tr> <td>{$q.quotaQon_id}</td> <td>{$q.quotaQon|truncate:80}</td> <td>{$q.author}</td> <td>{$q.edit}</td> <td>{$q.delete}</td> </tr> {/foreach} </table>
  • 79. action.admin_delete.php <?php if  (!isset($gCms))  exit; if  (!$this-­‐>CheckPermission('Manage  QuotaQons'))  exit; if  (isset($params['quotaQon_id'])) { $db  =  $this-­‐>GetDb(); $res  =  $db-­‐>Execute('delete  from  '.cms_db_prefi x(). 'module_quotaQons  where  quotaQon_id=?', array($params['quotaQon_id'])); $smarty-­‐>assign('message',$this-­‐>Lang('quotaQ on_deleted')); } echo  $this-­‐>DoAcQon('defaultadmin',  $id,  $params,  $returnid); ?>
  • 81. Be kind to users ● Add some safety – modify default admin link to delete $row['delete']  =  $this-­‐>CreateLink($id,  'admin_delete',  '', $this-­‐>Lang('delete'),  array('quotaQon_id'=>$row['quotaQon_id']), $this-­‐>Lang('really_delete',$row['author'])); ● And add to language file $lang['really_delete']='Really  delete  this  quotaQon  by  %s?';
  • 83. Let admin edit quotations ● We can re-use the form template ● Just need to create the admin-side logic ● Note that the logic is not identical, since we'll be updating the database rather than adding a new record
  • 84. action.admin_edit.php <?php if  (!isset($gCms))  exit; if  (!$this-­‐>CheckPermission('Manage  QuotaQ ons'))  exit; $db  =  $this-­‐>GetDb(); if  (isset($params['submit'])) { if  (empty($params['author'])  ||  empty($params['quotaQ on'])) { $smarty-­‐>assign('message',$this-­‐>Lang('error_empty_fi elds')); } else { $res  =  $db-­‐>Execute('update  '.cms_db_prefi x().'module_quotaQons  set  author=?,quotaQon=?  where  quotaQon_id=?', array($params['author'],$params['quotaQ on'],$params['quotaQon_id'])); if  ($res  ===  false) { $smarty-­‐>assign('message',$this-­‐>Lang('db_error',$db-­‐>ErrorMsg())); } else { $smarty-­‐>assign('message',$this-­‐>Lang('quotaQ on_edited')); } return  $this-­‐>DoAcQon('defaultadmin',  $id,  $params,  $returnid); } } else
  • 85. action.admin_edit.php, cont { $res  =  $db-­‐>Execute('select  *  from  '.cms_db_prefi x().'module_quotaQons  where  quotaQon_id=?', array($params['quotaQon_id'])); if  ($res  &&  $row=$res-­‐>FetchRow()) { $params['author']=$row['author']; $params['quotaQon']=$row['quotaQon']; } } $smarty-­‐>assign('form_start',$this-­‐>CreateFormStart($id,  'admin_edit',  $returnid)); $smarty-­‐>assign('input_author',$this-­‐>CreateInputTextWithLabel($id,  'author', isset($params['author'])?$params['author']:'',  10,  80,  '', $this-­‐>Lang('Qtle_author'))); $smarty-­‐>assign('Qtle_quotaQon',$this-­‐>Lang('Qtle_quotaQon')); $smarty-­‐>assign('input_quotaQ on',$this-­‐>CreateTextArea(false,  $id, isset($params['quotaQon'])?html_enQty_decode($params['quotaQ on'],ENT_QUOTES):'',  'quotaQ on')); $smarty-­‐>assign('submit', $this-­‐>CreateInputHidden($id,'quotaQ on_id',$params['quotaQ on_id']). $this-­‐>CreateInputSubmit($id,  'submit',  $this-­‐>Lang('submit'))); echo  $this-­‐>ProcessTemplate('user_form.tpl'); ?>
  • 88. action.defaultadmin.php 2.0 if  (!isset($gCms))  die("Can't  call  acQons  directly!"); if  (  !$this-­‐>Permission-­‐>check('Modify  Site  Preferences')  ) CmsResponse::redirect('index.php?'.CMS_SECURE_PARAM_NAME.'='. $_SESSION[CMS_USER_KEY]); if  (isset($params['submitprefs'])  ) { if(  !CmsAcl::check_core_permission('Modify  Site  Preferences',  $user)  ) die('permission  denied'); $password_minlength  =  (int)coalesce_key($params,'password_minlength',6); $this-­‐>Preference-­‐>set('password_minlength',$password_minlength); } $smarty-­‐>assign('groups',cms_orm('CmsGroup')-­‐>fi nd_all()); $smarty-­‐>assign('users',cms_orm('CmsUser')-­‐>fi nd_all()); $smarty-­‐>assign('acQve_tab_for_modules',    coalesce_key($params,'selected_tab','users')); $smarty-­‐>assign('form_acQon','defaultadmin'); echo  $this-­‐>Template-­‐>process('defaultadmin.tpl',$id,$return_id);
  • 89. defaultadmin.tpl 2.0 {has_permission  perm="Modify  Templates"} <div  style="text-­‐align:  right;  width:  80%;"><a  href="listmodtemplates.php"   Qtle="{tr}modify_templates{/tr}">{tr}modify_templates{/tr}</a></div><br/> {/has_permission} {tabs} {has_permission  perm='Modify  Users'} {tab_content  name='users'} {tab_header  name='users'}{tr}users{/tr}{/tab_header} {mod_template  template='users_tab.tpl'} {/tab_content} {/has_permission} {has_permission  perm='Modify  Site  Preferences'} {tab_content  name='prefs'} {tab_header  name='prefs'}{tr}preferences{/tr}{/tab_header} {mod_template  template='prefs_tab.tpl'} {/tab_content} {/has_permission} {/tabs}