This manual is deprecated. Please visit https://groupoffice.readthedocs.io for the latest documentation.

Setup Site Module (CMS)

From Group-Office Groupware and CRM Documentation
Revision as of 09:18, 18 October 2013 by Admin (Talk | contribs)

Jump to: navigation, search

GroupOffice Site Module BETA Getting Started Guide

This guide describes how to create and publish a website using Group-Office's Site module and its components. We will use the group-office.com website as an example to show you how a website is build.

Creating a new (site)module

A website has the same structure like any regular Group-Office module and uses the same folder structure.

Create a new folder named groupofficecom inside the groupoffice/modules folder

The name of the folder should only contain lowercase letters (a-z) and/or numbers (0-9) no spaces or special character may be used

Inside this folder create the following structure:
* groupofficecom
** controller
** model
** install
*** install.sql [file]
*** uninstall.sql [file]
** views
*** site
** GroupofficecomModule.php [file]
** siteconfig.php [file]

The install.sql and uninstall.sql SQL query files are executed when the module is installed or uninstalled in the GroupOffice modules tab You can add SQL code the create or drop tables here but will not be used in this guide.

We'll start by creating the GroupofficecomModule.php file and the siteconfig.php file. (the module file should be named the same is the module folder but starting with a capital.

Creating the startup scripts

The following 2 files are required for your site module. The contents will be explained with comments inside the file.

GroupofficeModule.php

<?php /**

*  This module will create the Group-Office website in the CMS site module
*/

class GO_Groupofficecom_GroupofficecomModule extends GO_Base_Module{

// If your website is using code from other modules // you can add the folder name of these modules to this depends array // The groupofficecom website used both the 'tickets' // module and the 'billing' module. It will have a page for customers // where then can create tickets and a webshop so there order will // be places inside te billing module. public function depends() { return array('site', 'tickets', 'billing'); }

// This name will be displayed inside the modules tab in GroupOffice public function name() { return "Group-Office.com website"; }

// And this description will be showing underneath it. public function description() { return "The new 2013 Group-Office website"; }

// This code will be executed when the module is installed. // It will create a new record in the site_site table // When this record is inserted the website will show up in // the Site module tab. the data below can later be modified // in this tab public function install() {

if (parent::install()){ $config = array( 'name'=>'Group-Office.com', 'domain'=>'www.group-office.com', 'module'=>'groupofficecom', 'ssl'=>false, 'mod_rewrite'=>true, 'base_path'=>'/' );

$site = new GO_Site_Model_Site(); $site->setAttributes($config);

return $site->save(); } else return false; } }

siteconfig.php

<?php // This as an array of content templates 'filename'=>'Label' $siteconfig['templates']=array( 'content'=>'Content', ); // This are routes to a controller action for shorter and remember-able URLs // 'support'=>'groupofficecom/ticket/index' mains: // www.group-office.com/support will execute 'acionIndex()' // of the 'TicketController' inside the 'groupofficecom' module // // value of <this> will be passed to the action inside $_GET['this'] // after the semi column a regular expression will be used. // the rule'shop/<action:\w+>' if value of action contains letters only $siteconfig['urls']=array( => 'groupofficecom/site/home', 'support'=>'groupofficecom/ticket/index', 'support/<action:\w+>'=>'groupofficecom/ticket/<action>', 'shop'=>'groupofficecom/shop/index', 'shop/<action:\w+>'=>'groupofficecom/shop/<action>', 'payment/<action:\w+>'=>'groupofficecom/payment/<action>', '<action:(trial|account)>' => 'groupofficecom/site/<action>', '<action:(login|logout|register|profile|resetpassword|recoverpassword)>' => 'site/account/<action>', '<module:\w+>/<controller:\w+>/<action:\w+>'=>'<module>/<controller>/<action>', '<slug:[\w\/.-]+>.html'=>'site/front/content', );

Now that the module structure is created we can create the first page.

Creating the first controller

Every page is rendered by a controller action. Inside the controller folder of the groupofficecom module add a PHP file named SiteController.php

SiteController.php

<?php class GO_Groupofficecom_Controller_Site extends GO_Site_Components_Controller {

// actionHome() is accessible by everyone // actionAccount() is not. If visitor is not logged in he will be redirected to site/site/login protected function allowGuests() { return array('home'); }

protected function ignoreAclPermissions(){ return array('home'); }

/** * Renders the static homepage of groupoffice.com * @param array $params (this will contain the content of $_REQUEST) */ protected function actionHome($params){ $this->render('homepage'); }

/** * Show the account page for the shop users with: * invoices * account detail * @param array $params */ protected function actionAccount($params) { $contact = GO::user()->contact;

//Load all Orders Where user_id == GO::user()->id (logged in user) AND status_id > 0 $findParams = GO_Base_Db_FindParams::newInstance()->criteria(GO_Base_Db_FindCriteria::newInstance() ->addCondition('user_id', GO::user()->id) ->addCondition('status_id', 0, '>'))->order('btime', 'DESC'); $invoices = GO_Billing_Model_Order::model()->find($findParams);


$this->render('account', array('contact'=>$contact, 'invoices'=>$invoices)); }

}

This controller contains 2 pages:

  • groupofficecom/site/home
  • groupofficecom/site/account

Take another look at the following line in your siteconfig.php file: => 'groupofficecom/site/home',

This will make sure that http://www.group-office.com/ will run actionHome() of this controller

At the end of the controller logic a view needs to be rendered: $this->render($view, $data);

  • $view is the view file that has yet to be created. It is stored inside groupofficecom/views/site/groupofficecom/$view.php
  • $data is an array of variables that will be passed to the view 'myvar'=>'Hello World'

The following line in your view <?php echo $myvar; ?> will output 'Hello World'

Creating a MasterPage

Every view can be surrounded by a so called masterpage. This masterpage will usualy contain the <head> part of your html page and the first and last part of the <body> for rendering a menu on every page for example.

Every site can have more then 1 masterpage/layout. These layouts are placed inside /groupofficecom/views/site/layouts/ The default view is called main.php if a second masterpage eg. shop.php is created the following line needs to be added to the controller to surround your view with this masterpage: $this->layout = 'shop'; If not specified the controller will be looking for /groupofficecom/views/site/layouts/main.php let's create this file.

main.php

<!doctype html> Group Office - <?php echo Site::controller()->getPageTitle(); ?> registerGapiScript('jquery'); ?>

This huge chuck of code is just the static HTML of the group-office.com website. There is nothing fancy about it. Right in the middle of the HTML page between the header and the footer you will find this line: <?php echo $content; ?>

To find the path where your images, CSS and Javascript is stored use: Site::template()->getUrl(); This will return a the location where your template assets are stored usually '/public/assets/template' This path in configures when we install the module we are creating.

This is where your view is rendered this view will differ from page to page.

Creating the first view

A view of a website is the html output the is returned by the browser.

On the account view that is rendered inside actionAccount() of the controller we create we passed 2 variables ($contact and $invoices) Lets have a look at how the view looks.

Create a file: /groupoffice/views/site/groupofficecom/account.php

<section class="page">

<section> <header>

Your account

</header>

<article>

<fieldset> <legend>Invoices</legend>

You can find your invoices below. Click on "pay" to go directly to the payment page and click on "Download" to download the invoice as a PDF file.

<thead> </thead> <tbody> <?php foreach($invoices as $i => $invoice): ?> <tr class="<?php echo ($i%2) ? 'even' : 'odd'; ?>">> </tr> <?php endforeach; ?> </tbody>
Your Invoices
Invoice no. Date Status
<?php echo $invoice->order_id; ?> <?php echo $invoice->getAttribute("ptime", "formatted"); ?> <?php if($invoice->status)echo $invoice->status->getName($invoice->language_id); ?>

<?php if(!empty($invoice->ptime)): ?> <?php echo $invoice->getAttribute("ptime","formatted"); ?> <?php else: ?> <a href="<?php echo Site::urlManager()->createUrl('groupofficecom/payment/payment', array('order_id'=>$invoice->id)); ?>">Pay</a> <?php endif; ?>

<a target="_blank" href="<?php echo Site::urlManager()->createUrl('billing/order/sitePdf',array('id'=>$invoice->id)); ?>">Download</a>


</fieldset>

</article> </section>

</section>

All it does is loop through $invoices (an array of GO_Billing_Model_Order objects) and render HTML table rows You can use any piece of PHP code inside a view but for manageable code do not write any logic inside the view. In this example we do not use the $contact variable we passed in the controller.

NOTE: that all the links inside this view are created with the following line: <?php echo Site::urlManager()->createUrl('groupofficecom/payment/payment', array('order_id'=>$invoice->id)); ?> This will return the url of the controller action and attaches the $_GET['order_id] We use this function so that the link will respect the rules specified in your siteconfig.php file this way createUrl('groupofficecom/ticket/index') will output /support and if you click the link it will call actionIndex() in the TicketController.php file

Components

The site module that is used by the groupofficecom module has a set of 10 useful components When creating the first view you already saw Site::urlManager() This component knows what controller action to call when and URL is called and can create links to a controller action using the createUrl() function.

Config

Site::config() will return the Config component this object contain all the variable set in you siteconfig.php file

Example usage:

When a new order is placed by a customer in our webshop we want to create a new GO_Billing_Model_Order object add the items in the cart to it and save it. Problem is an order needs a variable order_book_id. and a default status_id We could hardcode this in the controller action where we create and save the Order object but we might want to change this somewhere in the future and then we need to search through our code when we could have place variable into our siteconfig.php file

Open the siteconfig.php file and add to following lines: $siteconfig['shop']=array( 'order_pending_status_id'=>3, 'order_book_id'=>2 )

New we can use these 2 variable in our controller as follows: $order = new GO_Billing_Model_Order(); $order->user_id = GO::user()->id; $order->book_id = Site::config()->shop['order_book_id']; $order->statusId = Site::config()->shop['order_pending_status_id']; foreach($cart->items as $item) { $order->addItem($item->toProduct()); } $order->save(); Or something similar :) (In the groupofficecom example We creating a ShopController.php that shows the full code on how to create this logic)

Notifier

Site::notifier() will return the Notifier Component. This class can help you display error or success messages to the user. It has 3 functions that are important:

  • setMessage($key, $value)
  • hasMessage($key) (returns boolean)
  • getMessage($key) (returns $value set by setMessage())

Before placing an new order we need to validate tjat the user has checked that annoying little box saying "I agree with the terms and conditions" to do so you can add the following lines to the controller action: if(!isset($_POST['agreeTerms'])) Site::notifier()->setMessage('agreeFailed', 'You must agree to our terms and conditions'); else { $order->save(); $this->redirect('module/controller/action'); //where you want to go }

If the check-box was not checked the 'agreeTerms' value will not be posted and instead of saving the order a message will be set. In our view we could use the following code

<style> .error{ color: red; } </style> <?php if(Site::notifier()->hasMessage('agreeFailed')): ?>

  • <input type="checkbox" id="agreeTerms" name="agreeTerms"> <label class="error" for="agreeTerms">I agree to the <a href="/license-agreement.html" target="_blank">license terms and conditions</a></label>
    <?php echo Site::notifier()->getMessage('agreeFailed'); ?>
  • <?php else: ?>

  • <input type="checkbox" id="agreeTerms" name="agreeTerms"> <label for="agreeTerms">I agree to the <a href="/license-agreement.html" target="_blank">license terms and conditions</a></label>
  • <?php endif; ?>

    hasMessage() will continue to return true until getMessage() is called. (it is saved in a session)

    Scripts

    Site::scripts() will return the scripts component.

    This component can be used to add scripts to your HTML page. This includes CSS, JS, meta tags and script hosted on googleapis.com .eg 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js'

    Example usage:

    On your homepage you want to display a slider. You are using a jQuery plug-in but don't want to insert the JS and CSS file on every page since they have a large file size. The script needs to start after the Dom has loaded. but your footer and </body> tag is not in your view but in your master-page layout.

    We can tell the CMS to add some scripts and style to our master-page and we can tell the CMS where with the following code:

    // import jQuery from the googleapis.com repository Site::scripts()->registerGapiScript('jquery'); //jquery-ui is supported as well // add the slider plugin to the <head> of our document Site::scripts()->registerScriptFile(Site::template()->url().'/js/cycle.jquery.js', GO_Site_Components_Scripts::POS_HEAD); // add the CSS file to the <head> Site::scripts()->registerCssFile(Site::template()->url().'/css/cycle.cs', GO_Site_Components_Scripts::POS_END); // add a piece of code when the document is ready Site::scripts()->registerScript('cycle', '$("#slider").cycle();'); // add a <meta> tag to the <head> of your document Site::scripts)->registerMetaTag('the page description for search engines', 'description');

    You can use this code in your view our in your controller. When PHP returns its output to the browser it will add the following to your master-page

    //other stuff in the head //your page content // just before the

    When using registerScriptFile() the CMS will make sure the script is only included once. So when using 2 jQuery plug-in an accidentally used registerScriptFile() twice the path of the file will be used as a unique identifier and the script wont be included again.

    UrlManager

    Site::urlManager() return the url manager component. It is responsible for making your URL's pretty. Only one method should be used.

    • createUrl($route, $params)

    the route is the path to the controllers action like this /module/controller/action eg. 'groupofficecom/shop/checkout' will call ShopController::actionCheckout() found in your groupoffficecom module the $params variable is an array with key value pars and are placed in the global $_GET variable. These $_GET variables can be formatted as well.

    Example usage:

    In your siteconfig.php file the following line already exists: '<slug:[\w\/.-]+>.html'=>'site/front/content'

    Request the following URL: www.yoursite.com/about-us.html

    The CMS will call 'site/front/content' and $_GET['slug'] will return the string 'about-us'

    The other way around we can use createUrl to generate the URL www.yoursite.com/about-us.html as follows

    Site::urlManager()->createUrl('site/front/content', array('slug'=>'about-us'));

    AssetManager

    Site::assetManager() will return the AssetManager component This component is meant to publish assets that are not part of your template.

    Example:

    In the groupofficecom module's ticket submit page there is an upload component used for uploading files. This uses the PLUpload library to do so. This contains an extra .js file and a .swf file. These files are location inside the groupofficecom module and might not be accessible from your web-site's root folder.

    We need to copy these asset files so they can be included in the HTML source. We can do so with the following code:

    $assetUrl = Site::assetManager()->publish(GO::config()->root_path.'modules/site/widget/plupload/assets'); $this->_swfUrl = $assetUrl.'/assets/js/plupload.flash.swf'; Site::scripts()->registerCssFile($assetUrl.'/assets/style.css'); Site::scripts()->registerScriptFile($assetUrl.'/assets/js/plupload.full.js'); Site::scripts()->registerScriptFile($assetUrl.'/assets/js/jquery.plupload.queue/jquery.plupload.queue.js');

    the publish($path) function will copy all files the a public accessible directory and return the relative URL. This URL can then be used to insert a script-file into the head of your document.

    This is easy for creating re-usable components like an upload widget. There is an example of this widget in the site module /modules/site/widget/plupload


    Site

    The site model in the database

    Site::model() will return a GO_Site_Model_Site active record. This is the Site record in the database. The properties of this record can be modified in Group-Office by right clicking the site name in the CMS tab and select properties

    Abstract classes

    2 abstract classes can be extended in your code to proved useful functionality

    • GO_Site_Components_Controller
    • GO_Site_Components_Widget

    Controller

    All controllers that have actions that render web-pages should extend this class. This abstract controller class will add the following functions and properties to your controllers:

    Properties:

    • string: layout = 'main'

    This is the name of the .php file inside the groupoffice/views/site/layouts folder

    • string: pageTitle = ucfirst(<action name>)

    You can use the following code in your layout to make this function useful <title><?= Site::controller()->pageTitle; ?></title>

    Functions:

    • render(string: $view, array: $data, boolean: $return);

    Used the render a view with data passed to it. if the '$return value is set to true then the result will be returned as a string. NOTE: The $return parameter will be depricated in the future and default to true. This function can then be used like this: echo $this->render()

    • renderPartial(string: $view, array: $data, boolean: $return);

    Used to render a view without the masterpage wrapped around is. Useful when only a part of the view is needed (Ajax loaded calls)

    • redirect($url, $statusCode = 302);

    When this is called the HttpStatus code will return 302 and the browser will redirect to the provide URL. You can use Site::urlManager()->createUrl() to create this URL or pass an array as the first value like this array('site/front/content', 'slug'=>$slug) if the first parameter is an array it will automatically be wrapped inside createUrl()

    • getReturnUrl();

    This will return the URL where to user was trying to go after a redirect is performed. When the user has no access to a URL he is redirected to the login page. After login you can redirect the user again to the page he was trying to go.

    Widget

    All created Widgets extend this class. It's constructor will set all properties the are passed in the $config parameter is key=>value sets The widgets will also have an id property. If this is not set it will be generated. It is useful for setting the id attribute of the rendered HTML tag

    all widgets should overwrite the render() function. This function will eventually output the HTML.

    There are 4 working example Widgets inside the site/widget/ folder.

    • Plupload (discussed earlier)
    • Contact Form (render a simple contact form for in the footer of the page)
    • Form
    • Pager

    The last 2 are very useful for every page and will be explained.

    Form Widget

    In Group-Office the ActiveRecord pattern is used for loading and saving data from and to the database. (find out more here: http://en.wikipedia.org/wiki/Active_record_pattern) After loading data from the database we want to manipulated this in many cases and then throw it back in. To do this we use forms To generate these form we use the Form widget

    This widget will create inputfields, comboboxes, textareas, checkboxes, etc that connect to the properties of the ActiveRecord which in turn connect to columns in the database. In your controller you would load and save the data as in the following example where we place an new Order:

    // Parts where left out to show to purpose of this example protected function actionCheckout($params) { $order = new GO_Billing_Model_Order(); $order->book_id = 1;

    if(isset($_POST['Order'])) { $order->setAttributes($_POST['Order']) if($order->save()) { $this->redirect('payment'); } else { Site::notifier()->setMessage('saveerror', 'The order could not be saved'); } } $this->render('order', array('order'=>$order)); }

    Explained: - A new order object is created. - The book_id is set by default to 1 - if this is a post request. Set the post values to the $order object - Try to save the order - If success redirect to payment page - If failed set an appropriate error message - Render the HTML view 'order' with the order model passed to it.

    The 'order' view needs to contain a form with some field. (eg Name, place, zip code, etc) We will create this form with the Form widget. Here is a slimmed down example:

    <article>

    Please check if the data below is correct.

    <?php $form = new GO_Site_Widget_Form(); ?>

    <?php echo $form->beginForm(); ?>

    • <?php echo $form->label($order, 'customer_name'); ?> <?php echo $form->textField($order, 'customer_name'); ?> <?php echo $form->error($order, 'customer_name'); ?>
    • <?php echo $form->label($order, 'customer_email'); ?> <?php echo $form->textField($order, 'customer_email'); ?> <?php echo $form->error($order, 'customer_email'); ?>
    • <?php echo $form->label($order, 'customer_address'); ?> <?php echo $form->textField($order, 'customer_address'); ?> <?php echo $form->error($order, 'customer_address'); ?>
    • <?php echo $form->label($order, 'customer_country'); ?> <?php echo $form->dropDownList($order, 'customer_country', GO::language()->getCountries()); ?> <?php echo $form->error($order, 'customer_country'); ?>
    • Only enter the following field if you don't live in the Netherlands and you have a valid European Union VAT number.
    • <?php echo $form->label($order, 'customer_vat_no'); ?> <?php echo $form->textField($order, 'customer_vat_no'); ?> <?php echo $form->error($order, 'customer_vat_no'); ?>

    <?php echo $form->endForm(); ?>

    The form widget has methods that will output HTML. The following methods are provide

    • beginForm($action, $method, $htmlAttributes) : Will render the opening <form> tag
    • button($label, $htmlAttributes) : Will render in <input type=button> tag
    • checkBox($model, $attribute, $htmlAtributes): Will render an <input type="checkbox"> tag
    • error($model, $attribute, $htmlAtributes): Will render an
      tag with the validation error inside (only when ActiveRecord::getValidationError($attribute) returns a validation error string)
    • label($model, $attribute, $htmlAtributes): Will render an <label for="assoc_input_id"> tag it appends * if the attribute is required
    • hiddenField($model, $attribute, $data, $htmlAtributes): Will render an <input type="hidden"> tag
    • passwordField($model, $attribute, $htmlAtributes): Will render an <input type="password"> tag
    • radioButtonList($model, $attribute, $data, $htmlAtributes): Same as DropdDownList but will render <input="radio"> tags
    • textField($model, $attribute, $htmlAtributes): Will render an <input type="text"> tag
    • textArea($model, $attribute, $htmlAtributes): Will render a <textarea> tag
    • endForm(); render the end </form> tag
    • resetButton($label, $htmlAttributes) : Will render an <input type="reset"> tag
    • submitButton($label, $htmlAttributes): Will render an <input type="submit"> tag
    • GO_Site_Widget_Form::listData(ActiveRecord[], $keyattribute, $valueattribute): will turn an array of ActiveRecord into a usable data array to be used by dropDownList() and radioButtonList() there data attributes

    Pager Widget

    When displaying multiple ActiveRecord objects (in a table or list) You might not want to show all the record in the database table when there might be a lot. Loading 10 to 20 record per page, and display a paging navigation at the bottom is a common use case.

    In GroupOffice there is a class GO_Base_Data_DbStore the can be used to select record from a database in a specific order. It is basically the representation of a SELECT query.

    See the following example:

    Controller: public function actionTicketList($params) {

    $findParams = GO_Base_Db_FindParams::newInstance(); $findParams->getCriteria()->addCondition('user_id', GO::user()->id); //add where condition to query $findParams->order('mtime', 'DESC'); // add order by to query

    $store = new GO_Base_Data_DbStore('GO_Tickets_Model_Ticket', new GO_Base_Data_ColumnModel(),$params, $findParams); //the limit will be in $params $this->render('/tickets/ticketlist', array('ticketstore' => $store)); }

    This will render a view with an $store object

    View: <?php // Create a pager widget and add the Store object to it. $pager = new GO_Site_Widget_Pager(array( 'store'=>$ticketstore, 'previousPageClass'=>'pagination-arrow-right', 'nextPageClass'=>'pagination-arrow-left', )); ?>

    <thead>

    </thead> <tfoot>

    </tfoot> <tbody> <?php if(!$pager->getItems()): ?>

    <?php else: ?> <?php foreach($pager->getItems() as $i => $ticket): ?> <tr class="<?php echo ($i%2) ? 'even' : 'odd';; ?>">

    </tr> <?php endforeach; ?> <?php endif; ?> </tbody>

    Ticket No.NameStatusAgentCreated
    <?php $pager->render(); // will render the page navigation ?>
    No tickets found
    <?php echo '<a href="'.Site::urlManager()->createUrl("groupofficecom/ticket/ticket",array("ticket_number"=>$ticket->ticket_number,"ticket_verifier"=>$ticket->ticket_verifier)).'">'.$ticket->ticket_number.'</a>'; ?> <?php echo $ticket->subject; ?> <?php echo $ticket->getStatusName(); ?> <?php echo $ticket->agent?$ticket->agent->name:""; ?> <?php echo $ticket->getAttribute("ctime","formatted"); ?>

    That are all the components that can be used for creating web-pages with GroupOffice

    Be aware that the module is still in Beta and that the API might change slightly
    Retrieved from "http://www.group-office.com/w/index.php?title=Setup_Site_Module_(CMS)&oldid=3606"