Geek Moot '09 -- Introduction to CMS Module Development

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    2 Favorites

    Geek Moot '09 -- Introduction to CMS Module Development - Presentation Transcript

    1. Good Morning
    2. An Introduction to Module Development
    3. An Introduction to Module Development Or: How To Create Modules Without Becoming a Bitter, Angry Little Man
    4. What is a module? • adds functions or capabilities to CMSMS • allows more complexity than a plug-in or user-defined tag (UDT)
    5. Why a module instead of a plug-in or UDT? • multi-language • install / uninstall / upgrade • trigger / handle events • provide infrastructure and functions for other modules
    6. What else does the module API offer? • database abstraction (ADODB) • templating system (Smarty) • user-input sanitizing • persistent preferences • access to CMS configurations • dependencies management
    7. Module API, cont. • admin-side permission system • admin-side tools for tabbed interfaces • form utilities • ... and more ...
    8. Types of modules • plug-ins (e.g., News, Search, FormBuilder) • content-type modules (e.g., Cataloger) • infrastructure module support (e.g., CMSMailer, nuSOAP) • administrative (e.g., ModuleManager) • event handlers (e.g., UserWarning*) • filters (e.g., RegTM*)
    9. Types of modules • plug-ins (e.g., News, Search, FormBuilder) • content-type modules (e.g., Cataloger) • infrastructure module support (e.g., CMSMailer, nuSOAP) • administrative (e.g., ModuleManager) • event handlers (e.g., UserWarning*) • filters (e.g., RegTM*) * modules written for this presentation, source in appendix
    10. Ready? Let’s go! • “Hello World” module • create the directory: $CMSROOT/modules/HelloWorld • create the initial module file: HelloWorld.module.php
    11. HelloWorld.module.php <?php class HelloWorld extends CMSModule { function GetName() { return 'HelloWorld'; } function IsPluginModule() { return true; } function DoAction($action, $id, $params, $returnid=-1) { echo 'Hello World!'; } } ?>
    12. Congratulations! • you’re done! • next steps: • publish Hello World on the Developer’s Forge • check in to svn or git • mention the new module in the Forum • wait for fame and fortune to roll in
    13. Uh-oh! • our first Bitter Angry Little Man alert! • 3 people filed bug reports / feature requests: • “why English only?” • “Capitalizing ‘World’ is grammatically incorrect.” • “I want to use this module in German!”
    14. Solving problems • reject issue about `World’ capitalization (“works for me!”) • solve other problem by using language files: • create directory HelloWorld/lang • create initial language file: en_US.php
    15. modules/HelloWorld/lang/en_US.php <?php /* CMSROOT/modules/HelloWorld/lang/en_US.php */ $lang['hello_world_string'] = 'Hello World!'; ?>
    16. HelloWorld.module.php <?php class HelloWorld extends CMSModule { function GetName() { return 'HelloWorld'; } function IsPluginModule() { return true; } function DoAction($action, $id, $params, $returnid=-1) { echo $this->Lang('hello_world_string'); } } ?>
    17. Translation • by adding module to Translation Center, it can be translated into any language • add by setting up externals in svn • instructions at http://forum.cmsmadesimple.org/index.php/topic,2639.0.html • results in lang/ext directory containing translation files
    18. HelloWorld/lang/ext/de_DE.php <?php $lang['hello_world_string'] = 'Hallo Welt! Grüße!'; ?>
    19. Success! • localized version of the module is available • checked into the repository and published on the Forge • downloaded all over the world • surely, fame and fortune must follow
    20. Uh-Oh! • another Bitter Angry Little Man alert! • more bug reports / feature requests: • “I want the message in <h2> tags” • “text should be in <p> tags!” • “I also want ‘goodbye world’ as an option!”
    21. Solutions • output should use a template to allow different formatting • module should use the DoAction method to provide alternate functionality
    22. Templates • create CMSROOT/modules/HelloWorld/templates directory • create your template: hello.tpl • templates use the Smarty markup language • you will learn to read and love the Smarty manual at http://www.smarty.net/manual/en/
    23. HelloWorld/templates/hello.tpl {* smarty template. CMSROOT/modules/HelloWorld/templates/hello.tpl *} <h2>{$mod->Lang('hello_world_string')}</h2>
    24. Changes to HelloWorld.module.php function DoAction($action, $id, $params, $returnid=-1) { $this->smarty->assign_by_ref('mod',$this); $this->ProcessTemplate('hello.tpl'); }
    25. Where we’re at • output is now templated. Site developer could edit the hello.tpl to make it output the message in any way they wanted • we still have only one action - the module will only display the “hello world” string
    26. Multiple actions • build out the DoAction method • initially, we’ll implement it in-line in the module
    27. DoAction in HelloWorld.module.php function DoAction($action, $id, $params, $returnid=-1) { switch ($action) { case 'default': case 'hello': { $this->_do_hello($id, $params, $returnid); break; } case 'goodbye': { $this->_do_goodbye($id, $params, $returnid); break; } } }
    28. DoAction • special action: default • we’ll see another special action later • other parameters will be explained later
    29. Adds to HelloWorld.module.php function _do_hello($id, $params, $returnid) { $this->smarty->assign_by_ref('mod',$this); echo $this->ProcessTemplate('hello.tpl'); } function _do_goodbye($id, $params, $returnid) { $this->smarty->assign_by_ref('mod',$this); echo $this->ProcessTemplate('goodbye.tpl'); }
    30. HelloWorld/templates/goodbye.tpl {* smarty template. CMSROOT/modules/HelloWorld/templates/goodbye.tpl *} <h2>{$mod->Lang('goodbye_world_string')}</h2>
    31. Update lang file <?php /* CMSROOT/modules/HelloWorld/lang/en_US.php */ $lang['hello_world_string'] = 'Hello World!'; $lang['goodbye_world_string'] = 'Goodbye you cruel old planet.'; ?>
    32. Take a breath • what we’ve covered so far: • basic module file • localization • templates • multiple actions
    33. Oh Noes! • another Bitter Angry Little Man alert! • 12 people filed bug reports: • “I have one installation that accepts the ‘hello’ and ‘goodbye’ actions, and another that’s the older version, and I’m confused.” • “I have different versions on my sites and can’t tell them apart easily.”
    34. Solutions • we should have the module report its version • this opens up a can of worms: module installation, upgrades, uninstallation • which opens up another can of worms: separating files
    35. Reporting version • module method “GetVersion” • version-related module methods • install • upgrade • uninstall
    36. Adds to module function GetVersion() { return '1.0'; } function Install() { $this->Audit( 0,$this->GetName(),$this->Lang('installed', $this->GetVersion()) ); } function Uninstall() { $this->Audit( 0,$this->GetName(),$this->Lang('uninstalled') ); } function Upgrade($oldversion, $newversion) { $this->Audit( 0,$this->GetName(),$this->Lang('upgraded', $newversion) ); }
    37. update lang file <?php /* CMSROOT/modules/HelloWorld/lang/en_US.php */ $lang['hello_world_string'] = 'Hello World!'; $lang['goodbye_world_string'] = 'Goodbye you cruel old planet.'; $lang['installed'] = 'Hello World version %s installed.'; $lang['uninstalled'] = 'Hello World has been uninstalled.'; $lang['upgraded'] = 'Hello World upgraded to version %s.'; ?>
    38. Working backwards • separate files • method.install.php • method.upgrade.php • method.uninstall.php
    39. Separate file features • global handle to CMS is defined: $gCms • security tip: test for the global, so people can’t call the file directly and cause trouble • other variables pre-defined based on method that’s been split out
    40. method.install.php <?php // security measure if (!isset($gCms)) exit; $this->CreatePreference('use_random_phrase','y'); // entry in admin log $this->Audit( 0,$this->GetName(), $this->Lang('installed', $this->GetVersion()) ); ?>
    41. method.upgrade.php <?php // safety measure if (!isset($gCms)) exit; switch($oldversion) { case "0.0.0.1": $this->CreatePreference('use_random_phrase','y'); } $this->Audit( 0, $this->GetName(), $this->Lang('upgraded', $newversion)); ?>
    42. method.unistall.php <?php // safety measure if (!isset($gCms)) exit; $this->RemovePreference('use_random_phrase'); $this->Audit( 0,$this->GetName(), $this->Lang('uninstalled', $this->GetVersion()) ); ?>
    43. Separate actions • DoAction can also be split into multiple files • this keeps memory footprint smaller, code organization more logical • each file is called action.action_name.php • these files also have special pre-set variables
    44. action.action_name.php • has $gCms defined • has $id, $params, and $returnid defined (more on these later) • has $smarty defined
    45. action.default.php <?php // safety first if (!isset($gCms)) exit; $this->_do_hello($id, $params, $returnid) ?>
    46. action.goodbye.php <?php // safety first if (!isset($gCms)) exit; $this->_do_goodbye($id, $params, $returnid) ?>
    47. Wha?? • where’s our “Hello World?” • action “hello” is not the same as action “default” • we can either implement action “hello” by creating action.hello.php, or change our model such that this is the default.
    48. Another deep breath • what we’ve accomplished: • keeping track of module versions • providing a clean approach to module upgrades and uninstalls • breaking module into separate files for better memory management • set preferences, logged to the admin log
    49. Uh-Oh! • Bitter Angry Little Man alert! • 16 people filed feature requests: • “I should be able to load in multiple messages, not just ‘hello’ and ‘goodbye’!” • “Yeah, and they should have the option of displaying randomly according to that new preference you created!”
    50. The database • handle to database available via GetDb() method • supports ADODB functionality • hides a lot of the complexity for table creation. see http://phplens.com/lens/adodb/docs-datadict.htm
    51. Adds to method.install.php // get database handle $db = &$this->GetDb(); // mysql-specific, but ignored by other database $taboptarray = array( 'mysql' => 'TYPE=MyISAM' ); // database-independent table creation $dict = NewDataDictionary( $db ); $flds = "phrase_id I KEY AUTO, phrase C(255)"; $sqlarray = $dict->CreateTableSQL( cms_db_prefix(). 'module_hello', $flds, $taboptarray); $dict->ExecuteSQLArray($sqlarray); $db->Execute('insert into '.cms_db_prefix(). 'module_hello (phrase) values (?)', array($this->Lang('hello_world_string') ));
    52. Don’t forget! • implement similar code in the method.upgrade.php • bump the module’s version number to 1.1 • and implement code that uses the phrases from the database
    53. Revised function function _do_hello($id, $params, $returnid) { $db = &$this->GetDb(); if ($this->GetPreference('use_random_phrase','y') == 'y') { $count = $db->GetOne('select count(phrase) from '. cms_db_prefix().'module_hello'); $rand_line = rand(1,$count) - 1; $res = $db->SelectLimit('select phrase from '. cms_db_prefix().'module_hello',1,$rand_line); if ($res && $row=$res->FetchRow()) { $phrase = $row['phrase']; } } else { $phrase = $db->GetOne('select phrase from '.cms_db_prefix(). 'module_hello where phrase_id=1'); } $this->smarty->assign('phrase',$phrase); echo $this->ProcessTemplate('hello.tpl'); }
    54. Revised template/hello.tpl {* smarty template. CMSROOT/modules/HelloWorld/templates/hello.tpl *} <h2>{$phrase}</h2>
    55. Fear & loathing • Angry bitter little man alert! • 23 users complained about not being able to change the preference • 47 users complained about the lack of a form to add phrases
    56. Solution • need to create an admin area for the module • and need to create an input form
    57. Creating an admin • HasAdmin() method returns true • our other magic action: defaultadmin • separated file: action.defaultadmin.php
    58. Additions to HelloWorld.module.php function HasAdmin() { return true; } function VisibleToAdminUser() { return $this->CheckPermission('Manage All Content'); }
    59. action.defaultadmin.php <?php if (!isset($gCms)) exit; if (! $this->CheckPermission('Manage All Content')) exit; if (isset($params['random']) && !empty($params['random'])) { $this->SetPreference('use_random_phrase',$params['random']); $smarty->assign('message',$this->Lang('preference_set')); } $smarty->assign('start_form', $this->CreateFormStart($id, 'defaultadmin', $returnid)); $smarty->assign('input_pref',$this->CreateInputHidden($id,'random','n'). $this->CreateInputCheckbox($id, 'random', 'y', $this->GetPreference('use_random_phrase','y')). $this->Lang('title_use_random_phrase')); $smarty->assign('submit', $this->CreateInputSubmit($id, 'submit', lang('submit'))); echo $this->ProcessTemplate('adminpanel.tpl'); ?>
    60. templates/adminpanel.tpl {if isset($message)}<p class="pagemessage">{$message}</p>{/if} {$start_form} <div class="pageoverflow"> <p class="pagetext">{$input_pref}</p> <p class="pageinput">{$submit}</p> </div> </form>
    61. Create a user-side form • very much like the admin side
    62. action.add.php <?php if (!isset($gCms)) exit; if (isset($params['phrase']) && !empty($params['phrase'])) { $db = &$this->GetDb(); $db->Execute('insert into '.cms_db_prefix(). 'module_hello (phrase) values (?)', array($params['phrase'])); $smarty->assign('message',$this->Lang('phrase_added')); } $smarty->assign('start_form', $this->CreateFormStart($id, 'add', $returnid)); $smarty->assign('input_phrase', $this->CreateInputText($id,'phrase', $this->Lang('add_phrase_here'),40,255)); $smarty->assign('submit', $this->CreateInputSubmit($id, 'submit', $this->Lang('submit'))); $smarty->assign_by_ref('mod',$this); echo $this->ProcessTemplate('add.tpl'); ?>
    63. add.tpl {if isset($message)}<p class="pagemessage">{$message}</p>{/if} {$start_form} <fieldset><legend>{$mod->Lang('add_a_phrase')}</legend> <div> <p>{$input_phrase}</p> <div> <div> <p>{$submit}</p> </div> </fieldset> </form>
    64. What we’ve covered API Covered API Not Covered 10% 90%
    65. Resources • the Wiki http://wiki.cmsmadesimple.org/index.php/Developers • Skeleton module http://dev.cmsmadesimple.org/projects/skeleton • IRC freenode.net #cms
    66. Bitter, angry little man • hitting a moving target; keeping up to date • lots of complaints about the flavors of free ice cream available • give an inch, they’ll ask for a mile • using module for what it was never meant to do
    67. Preparing for CMSMS 2.0 • beware of direct access to API class variables! e.g., use $this->GetDb(), don’t use $this->db • no more PHP 4.x-isms • callbacks are going away! use the event system. • lots more – ORM, etc, so beware!
    68. Calguy’s cardinal rules • Do not use members of the module object, use accessors • Split everything up into logical classes • No separate entry points, use actions • Use the permissions model liberally • Use separate files for actions, tabs, install/ upgrade/uninstall actions
    69. Calguy’s rules, cont. • Cache data where practical data may be read (if it's feasible/expected that the more than one time in a request). • Use DEFINES or class constants rather than hardcoded strings • Separate logic from display... use Smarty • Provide the data to Smarty, let Smarty display it.
    70. Calguy’s rules, cont. • Use ADODB’s parameter cleansing stuff, don't build in all the params yourself. • Keep your code clean. • Get off my lawn, you kids!
    71. Closing
    72. Closing • Thanks for your attention
    73. Closing • Thanks for your attention • Goodbye cruel world...
    74. Appendix • module directory structure • source listings • random notes
    75. Module directory structure
    76. RegTM Module source <?php # extremely simple filter module; adds registered trademark symbol to your company's name class RegTM extends CMSModule { var $company_name = 'CMS Made Simple'; function GetName() { return 'RegTM'; } function GetFriendlyName() { return 'RegTM'; } function GetVersion() { return '0.1'; } function GetAuthor() { return 'SjG'; } function GetAuthorEmail()
    77. RegTM Module source, cont. function MinimumCMSVersion() { return "1.6"; } function SetParameters() { $this->AddEventHandler( 'Core', 'ContentPostRender', true ); } function DoEvent( $originator, $eventname, &$params ) { if ($originator == 'Core' && $eventname == 'ContentPostRender') { $params['content'] = str_replace($this->company_name, $this->company_name.'<sup>&reg;</sup>', $params['content']); } } } // end ?>
    78. User Warning Module source <?php # extremely simple event-handler module; sends email to admins giving them warning when a user is deleted. class UserWarning extends CMSModule { function GetName() { return 'UserWarning'; } function GetFriendlyName() { return 'UserWarning'; } function GetVersion() { return '0.1'; } function GetAuthor() { return 'SjG'; } function GetAuthorEmail() { return 'sjg@cmsmodules.com';
    79. User Warning Module source, cont. function MinimumCMSVersion() { return "1.6"; } function GetDependencies() { return array('CMSMailer'=>'1.73'); } function SetParameters() { $this->AddEventHandler( 'Core', 'DeleteUserPre', true ); }
    80. User Warning Module source, cont. function DoEvent( $originator, $eventname, &$params ) { if ($originator == 'Core' && $eventname == 'DeleteUserPre') { $db = $this->GetDB(); $user = $params['user']; $result = $db->Execute('select * from '.cms_db_prefix().'users where user_id <>?', array($user->id)); if ($result) { $mail =& $this->GetModuleInstance('CMSMailer'); if ($mail != FALSE) { $mail->reset(); $mail->SetSubject('Admin User Deleted'); $mail->SetBody('FYI: CMS admin user "'.$user->firstname.' '.$user->lastname. '" ('.$user->username.') has been terminated.'); } while ($row = $result->FetchRow()) { $mail->AddAddress($row['email'],$row['first_name'].' '.$row['last_name']); } $sent = $mail->Send(); } } } } // end
    81. Contact me • sjg@cmsmodules.com • drop by next time you’re in L.A.
    SlideShare Zeitgeist 2009

    + cmsmssjgcmsmssjg Nominate

    custom

    453 views, 2 favs, 2 embeds more stats

    A basic introduction to creating modules for &lt;a> more

    More info about this document

    CC Attribution License

    Go to text version

    • Total Views 453
      • 416 on SlideShare
      • 37 from embeds
    • Comments 0
    • Favorites 2
    • Downloads 14
    Most viewed embeds
    • 36 views on http://www.cmsmadesimple.de
    • 1 views on http://www.flugbild-per-hochstativ.de

    more

    All embeds
    • 36 views on http://www.cmsmadesimple.de
    • 1 views on http://www.flugbild-per-hochstativ.de

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories