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

Difference between revisions of "Setup Site Module (CMS)"

From Group-Office Groupware and CRM Documentation
Jump to: navigation, search
 
Line 91: Line 91:
 
=== siteconfig.php ===
 
=== siteconfig.php ===
  
<code>
+
<pre>
 
<?php
 
<?php
 
// This as an array of content templates 'filename'=>'Label'
 
// This as an array of content templates 'filename'=>'Label'
Line 117: Line 117:
 
'<slug:[\w\/.-]+>.html'=>'site/front/content',
 
'<slug:[\w\/.-]+>.html'=>'site/front/content',
 
);
 
);
</code>
+
</pre>
  
 
Now that the module structure is created we can create the first page.
 
Now that the module structure is created we can create the first page.
Line 127: Line 127:
 
  SiteController.php
 
  SiteController.php
  
<code>
+
<pre>
 
<?php
 
<?php
 
class GO_Groupofficecom_Controller_Site extends GO_Site_Components_Controller {
 
class GO_Groupofficecom_Controller_Site extends GO_Site_Components_Controller {
Line 169: Line 169:
 
 
 
}
 
}
</code>
+
</pre>
  
 
This controller contains 2 pages:
 
This controller contains 2 pages:
Line 176: Line 176:
  
 
Take another look at the following line in your siteconfig.php file:
 
Take another look at the following line in your siteconfig.php file:
<code>
+
<pre>
 
'' => 'groupofficecom/site/home',
 
'' => 'groupofficecom/site/home',
</code>
+
</pre>
  
 
This will make sure that http://www.group-office.com/ will run actionHome() of this controller
 
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:
 
At the end of the controller logic a view needs to be rendered:
<code>
+
<pre>
 
$this->render($view, $data);
 
$this->render($view, $data);
</code>
+
</pre>
  
 
* $view is the view file that has yet to be created. It is stored inside '''groupofficecom/views/site/groupofficecom/$view.php'''
 
* $view is the view file that has yet to be created. It is stored inside '''groupofficecom/views/site/groupofficecom/$view.php'''
Line 191: Line 191:
  
 
The following line in your view
 
The following line in your view
<code><?php echo $myvar; ?></code>
+
<pre><?php echo $myvar; ?></pre>
 
will output 'Hello World'
 
will output 'Hello World'
  
Line 200: Line 200:
 
Every site can have more then 1 masterpage/layout. These layouts are placed inside '''/groupofficecom/views/site/layouts/'''
 
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:
 
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:
<code>$this->layout = 'shop';</code>
+
<pre>$this->layout = 'shop';</pre>
 
If not specified the controller will be looking for '''/groupofficecom/views/site/layouts/main.php''' let's create this file.
 
If not specified the controller will be looking for '''/groupofficecom/views/site/layouts/main.php''' let's create this file.
  
 
  main.php
 
  main.php
<code>
+
<pre>
 
<!doctype html>
 
<!doctype html>
 
<html>
 
<html>
Line 357: Line 357:
 
</body>  
 
</body>  
 
</html>
 
</html>
</code>
+
</pre>
  
 
This huge chuck of code is just the static HTML of the group-office.com website. There is nothing fancy about it.
 
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:
 
Right in the middle of the HTML page between the header and the footer you will find this line:
<code><?php echo $content; ?></code>
+
<pre><?php echo $content; ?></pre>
  
 
To find the path where your images, CSS and Javascript is stored use:
 
To find the path where your images, CSS and Javascript is stored use:
<code>Site::template()->getUrl();</code>
+
<pre>Site::template()->getUrl();</pre>
 
This will return a the location where your template assets are stored usually '/public/assets/template'
 
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 path in configures when we install the module we are creating.
Line 378: Line 378:
  
 
  Create a file: '''/groupoffice/views/site/groupofficecom/account.php'''
 
  Create a file: '''/groupoffice/views/site/groupofficecom/account.php'''
<code>
+
<pre>
 
<section class="page">
 
<section class="page">
 
<div class="wrapper">
 
<div class="wrapper">
Line 434: Line 434:
 
</div>
 
</div>
 
</section>
 
</section>
</code>
+
</pre>
  
 
All it does is loop through $invoices (an array of GO_Billing_Model_Order objects) and render HTML table rows
 
All it does is loop through $invoices (an array of GO_Billing_Model_Order objects) and render HTML table rows
Line 441: Line 441:
  
 
NOTE: that all the links inside this view are created with the following line:
 
NOTE: that all the links inside this view are created with the following line:
<code>
+
<pre>
 
<?php echo Site::urlManager()->createUrl('groupofficecom/payment/payment', array('order_id'=>$invoice->id)); ?>
 
<?php echo Site::urlManager()->createUrl('groupofficecom/payment/payment', array('order_id'=>$invoice->id)); ?>
</code>
+
</pre>
 
This will return the url of the controller action and attaches the $_GET['order_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
 
We use this function so that the link will respect the rules specified in your siteconfig.php file
Line 462: Line 462:
  
 
Open the siteconfig.php file and add to following lines:
 
Open the siteconfig.php file and add to following lines:
<code>
+
<pre>
 
$siteconfig['shop']=array(
 
$siteconfig['shop']=array(
 
'order_pending_status_id'=>3,
 
'order_pending_status_id'=>3,
 
'order_book_id'=>2
 
'order_book_id'=>2
 
)
 
)
</code>
+
</pre>
  
 
New we can use these 2 variable in our controller as follows:
 
New we can use these 2 variable in our controller as follows:
<code>
+
<pre>
 
$order = new GO_Billing_Model_Order();
 
$order = new GO_Billing_Model_Order();
 
$order->user_id = GO::user()->id;
 
$order->user_id = GO::user()->id;
Line 479: Line 479:
 
}
 
}
 
$order->save();
 
$order->save();
</code>
+
</pre>
 
Or something similar :) (In the groupofficecom example We creating a ShopController.php that shows the full code on how to create this logic)
 
Or something similar :) (In the groupofficecom example We creating a ShopController.php that shows the full code on how to create this logic)
  
Line 491: Line 491:
  
 
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:
 
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:
<code>
+
<pre>
 
if(!isset($_POST['agreeTerms']))
 
if(!isset($_POST['agreeTerms']))
 
Site::notifier()->setMessage('agreeFailed', 'You must agree to our terms and conditions');
 
Site::notifier()->setMessage('agreeFailed', 'You must agree to our terms and conditions');
Line 498: Line 498:
 
$this->redirect('module/controller/action'); //where you want to go
 
$this->redirect('module/controller/action'); //where you want to go
 
}
 
}
</code>
+
</pre>
  
 
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.
 
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
 
In our view we could use the following code
  
<code>
+
<pre>
 
<style> .error{ color: red; } </style>
 
<style> .error{ color: red; } </style>
 
<?php if(Site::notifier()->hasMessage('agreeFailed')): ?>
 
<?php if(Site::notifier()->hasMessage('agreeFailed')): ?>
Line 517: Line 517:
 
</li>
 
</li>
 
<?php endif; ?>
 
<?php endif; ?>
</code>
+
</pre>
  
 
hasMessage() will continue to return true until getMessage() is called. (it is saved in a session)
 
hasMessage() will continue to return true until getMessage() is called. (it is saved in a session)
Line 535: Line 535:
 
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:
 
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:
  
<code>
+
<pre>
 
// import jQuery from the googleapis.com repository
 
// import jQuery from the googleapis.com repository
 
Site::scripts()->registerGapiScript('jquery'); //jquery-ui is supported as well
 
Site::scripts()->registerGapiScript('jquery'); //jquery-ui is supported as well
Line 546: Line 546:
 
// add a <meta> tag to the <head> of your document
 
// add a <meta> tag to the <head> of your document
 
Site::scripts)->registerMetaTag('the page description for search engines', 'description');  
 
Site::scripts)->registerMetaTag('the page description for search engines', 'description');  
</code>
+
</pre>
  
 
You can use this code in your view our in your controller.  
 
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
 
When PHP returns its output to the browser it will add the following to your master-page
  
<code>
+
<pre>
 
<html>
 
<html>
 
<head>
 
<head>
Line 569: Line 569:
 
</body>  
 
</body>  
 
</html>
 
</html>
</code>
+
</pre>
  
 
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.
 
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.
Line 585: Line 585:
  
 
In your siteconfig.php file the following line already exists:
 
In your siteconfig.php file the following line already exists:
<code>
+
<pre>
 
'<slug:[\w\/.-]+>.html'=>'site/front/content'
 
'<slug:[\w\/.-]+>.html'=>'site/front/content'
</code>
+
</pre>
  
 
Request the following URL:
 
Request the following URL:
Line 596: Line 596:
 
The other way around we can use createUrl to generate the URL '''www.yoursite.com/about-us.html''' as follows
 
The other way around we can use createUrl to generate the URL '''www.yoursite.com/about-us.html''' as follows
  
<code>
+
<pre>
 
Site::urlManager()->createUrl('site/front/content', array('slug'=>'about-us'));
 
Site::urlManager()->createUrl('site/front/content', array('slug'=>'about-us'));
</code>
+
</pre>
  
 
=== AssetManager ===
 
=== AssetManager ===
Line 612: Line 612:
 
We need to copy these asset files so they can be included in the HTML source. We can do so with the following code:
 
We need to copy these asset files so they can be included in the HTML source. We can do so with the following code:
  
<code>
+
<pre>
 
$assetUrl = Site::assetManager()->publish(GO::config()->root_path.'modules/site/widget/plupload/assets');
 
$assetUrl = Site::assetManager()->publish(GO::config()->root_path.'modules/site/widget/plupload/assets');
 
$this->_swfUrl = $assetUrl.'/assets/js/plupload.flash.swf';
 
$this->_swfUrl = $assetUrl.'/assets/js/plupload.flash.swf';
Line 618: Line 618:
 
Site::scripts()->registerScriptFile($assetUrl.'/assets/js/plupload.full.js');
 
Site::scripts()->registerScriptFile($assetUrl.'/assets/js/plupload.full.js');
 
Site::scripts()->registerScriptFile($assetUrl.'/assets/js/jquery.plupload.queue/jquery.plupload.queue.js');  
 
Site::scripts()->registerScriptFile($assetUrl.'/assets/js/jquery.plupload.queue/jquery.plupload.queue.js');  
</code>
+
</pre>
  
 
the publish($path) function will copy all files the a public accessible directory and return the relative URL.
 
the publish($path) function will copy all files the a public accessible directory and return the relative URL.
Line 694: Line 694:
 
In your controller you would load and save the data as in the following example where we place an new Order:
 
In your controller you would load and save the data as in the following example where we place an new Order:
  
<code>
+
<pre>
 
// Parts where left out to show to purpose of this example
 
// Parts where left out to show to purpose of this example
 
protected function actionCheckout($params) {
 
protected function actionCheckout($params) {
Line 710: Line 710:
 
$this->render('order', array('order'=>$order));
 
$this->render('order', array('order'=>$order));
 
}
 
}
</code>
+
</pre>
  
 
Explained:
 
Explained:
Line 724: Line 724:
 
We will create this form with the Form widget. Here is a slimmed down example:
 
We will create this form with the Form widget. Here is a slimmed down example:
  
<code>
+
<pre>
 
<article>
 
<article>
 
<p>Please check if the data below is correct.</p>
 
<p>Please check if the data below is correct.</p>
Line 761: Line 761:
  
 
<?php echo $form->endForm(); ?>
 
<?php echo $form->endForm(); ?>
</code>
+
</pre>
  
 
The form widget has methods that will output HTML. The following methods are provide
 
The form widget has methods that will output HTML. The following methods are provide
Line 789: Line 789:
  
 
Controller:
 
Controller:
<code>
+
<pre>
 
public function actionTicketList($params) {
 
public function actionTicketList($params) {
 
 
Line 799: Line 799:
 
$this->render('/tickets/ticketlist', array('ticketstore' => $store));
 
$this->render('/tickets/ticketlist', array('ticketstore' => $store));
 
}
 
}
</code>
+
</pre>
  
 
This will render a view with an $store object
 
This will render a view with an $store object
  
 
View:
 
View:
<code>
+
<pre>
 
<?php  
 
<?php  
 
// Create a pager widget and add the Store object to it.
 
// Create a pager widget and add the Store object to it.
Line 836: Line 836:
 
</tbody>
 
</tbody>
 
</table>
 
</table>
</code>
+
</pre>
  
 
That are all the components that can be used for creating web-pages with GroupOffice
 
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
 
Be aware that the module is still in Beta and that the API might change slightly

Latest revision as of 09:24, 18 October 2013

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>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
	
		<title>Group Office - <?php echo Site::controller()->getPageTitle(); ?></title>
		<!--[if lt IE 9]>
		<script src="<?php echo Site::template()->getUrl(); ?>scripts/html5shiv.js"></script>
		<![endif]-->
		
		<?php Site::scripts()->registerGapiScript('jquery'); ?>
		
		<link rel="shortcut icon" type="image/x-icon" href="<?php echo Site::template()->getUrl(); ?>favicon.ico">
		<link rel="stylesheet" href="<?php echo Site::template()->getUrl(); ?>style.css">
	
	</head>

	<body id="top">
		<nav id="navigation-mobile-container">
			<div id="navigation-mobile">
				<ul>				
					<li><a href="/#features">Features</a></li>
					<li><a href="/#cases">Cases</a></li>	
				</ul>
				<ul>
					<li><a href="/#try-now">Products</a></li>
					<li><a href="/#twitter">News</a></li>
				</ul>
				<ul>				
					<li><a href="#contact">Contact</a></li>
					<li><a href="/#shop">Shop</a></li>
				</ul>
				<ul>				
					<?php if(GO::user()): ?>
						<li><a href="/support">Support</a></li>
						<li><a href="/logout" >Logout</a></li>
					<?php else: ?>
						<li><a href="/login">Login</a></li>
					<?php endif; ?>
				</ul>			
			</div>	
		</nav>	
		<header id="page-header">
			<div class="wrapper">
				<h1 id="logo-holder">
					<a href="/">
						<div id="logo"></div>
					</a>
				</h1>
				
				<nav id="secondary-menu">
					<ul>
						<?php if(GO::user()): ?>
							<li>Welcome <?php echo GO::user()->first_name; ?>,</li>
							<li><a href="/account">My Account</a></li>
							<li><a href="/logout" >Logout</a></li>
						<?php else: ?>
							<li><a href="/login" >Login</a></li>
							<li><a href="/register" >Register</a></li>
						<?php endif; ?>
	
					</ul>
				</nav>
				<nav id="main-menu">
					<ul>
						<li><a href="<?php if(Site::request()->getRequestUri()!='/') echo '/'; ?>#features" >Features</a></li>						
						<li><a href="<?php if(Site::request()->getRequestUri()!='/') echo '/'; ?>#cases" >Cases</a></li>
						<li><a href="<?php if(Site::request()->getRequestUri()!='/') echo '/'; ?>#try-now" >Products</a></li>
						<li><a href="<?php if(Site::request()->getRequestUri()!='/') echo '/'; ?>#twitter" >News <span id="tweet-indicator"class="rounded-corners">1</span></a></li>
						<li><a href="#contact" >Contact</a></li>	
						<li><a href="/shop" class="black-button rounded-corners">Shop</a></li>	
						<li><a href="/support" class="black-button rounded-corners">Support</a></li>
					</ul>
				</nav>
				
				<nav id="mobile-menu">
					<ul>
						<li><a id="toggle-menu" class="black-button rounded-corners">Menu</a></li>				
					</ul>
					
				</nav>
				
			</div>
		</header>
		
		<?php echo $content; ?>

		<footer id="contact">
			<div class="wrapper">
				
				<div id="contact-info">
					Intermesh BV<br />
					Zuid Willemsvaart 35<br />
					5211 SB 's-Hertogenbosch<br />
					<a href="https://maps.google.nl/maps?q=Intermesh+Holding+B.V.,+Hesselsstraat,+'s-Hertogenbosch&hl=nl&sll=51.710479,5.339273&sspn=0.134874,0.244102&oq=intermesh+holding&hq=intermesh+holding+bv&hnear=Hesselsstraat,+'s-Hertogenbosch,+Noord-Brabant&t=m&z=16&iwloc=A">View on map</a><br /><br />
					
					 +31 (0)73 6445508<br />
					<a href="mailto:info@group-office.com">info@group-offce.com</a><br /><br />
					<a href="https://twitter.com/Group Office" class="social-link"><div id="twitter-icon" class="social-icon"></div>Twitter</a>
					<a href="http://www.linkedin.com/groups?gid=3175416&mostPopular=&trk=tyah" class="social-link"><div id="linkedin-icon" class="social-icon"></div>Linkedin</a>

				</div>
					
				<div id="contact-form">
					Custom feature request or just want to get in touch? Send us a note:<br />
					<?php $contactForm = new GO_Site_Widget_Contactform_Widget(array(
						'emailFieldOptions' => array('placeholder'=>'Email'),
						'messageFieldOptions' => array('rows'=>4, 'placeholder'=>'Message'),
						'submitButtonText' => 'Send Message'
					)); 
					echo $contactForm->render();
					?>
				</div>
			</div>
		</footer>
		<div id="back-to-top"><a href="#top">Back to Top</a></div>
		
		<!-- SCRIPTS -->

		<script src="<?php echo Site::template()->getUrl(); ?>scripts/json.js"></script>	
		<script src="<?php echo Site::template()->getUrl(); ?>scripts/jquery.cycle.all.js"></script>
		<script src="<?php echo Site::template()->getUrl(); ?>scripts/twitter.js"></script>
		<script src="<?php echo Site::template()->getUrl(); ?>scripts/waypoints.js"></script>
		
		<script type="text/javascript" >
		
		jQuery(document).ready(function($){
		
			/* Animated scrolling */
			$('a[href^="#"]').bind('click.smoothscroll',function (e) {
			    e.preventDefault();
			    var target = this.hash;
			        $target = $(target);
			    $('html, body').stop().animate({
			        'scrollTop': $target.offset().top - 15
			    }, 1000, 'swing', function () {
			        window.location.hash = target;
			    });
			});
		
			/* Slide in menu on mobile devices */
			$("#toggle-menu").on("click", function(){
				$("#navigation-mobile-container").slideToggle();
				$(this).toggleClass("active");
			});
		
		});	

		</script>
	</body> 
</html>

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">
	<div class="wrapper">

		<section>
			<header>
				<h2>Your account</h2>								
			</header>	

			<article>
				
				<fieldset>
					<legend>Invoices</legend>
					<p>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.</p>
					<table>
					<caption>
						<span class="caption-header">Your Invoices</span> 
					</caption>
					<thead>
						<tr>
							<th align="left">Invoice no.</th>
							<th align="left">Date</th>
							<th align="left">Status</th>
							<th></th>
							<th></th>
						</tr>
					</thead>
					<tbody>
					<?php foreach($invoices as $i => $invoice): ?>
						<tr class="<?php echo ($i%2) ? 'even' : 'odd'; ?>">>
							<td><?php echo $invoice->order_id; ?></td>
							<td><?php echo $invoice->getAttribute("ptime", "formatted"); ?></td>
							<td><?php if($invoice->status)echo $invoice->status->getName($invoice->language_id); ?></td>
							<td>
								<?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; ?>
							</td>
							<td>
								<a target="_blank" href="<?php echo Site::urlManager()->createUrl('billing/order/sitePdf',array('id'=>$invoice->id)); ?>">Download</a>
							</td>
						</tr>
					<?php endforeach; ?>
					</tbody>
					</table>	
					
					
				</fieldset>
				
			</article>
		</section>
	</div>
</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')): ?>
	<li class="checkbox error" >
		<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>
		<div class="error"><?php echo Site::notifier()->getMessage('agreeFailed'); ?> </div>
	</li>
<?php else: ?>
	<li class="checkbox">
		<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>
	</li>
<?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

<html>
	<head>
		//other stuff in the head
		<meta name="description" content="the page description for search engines">
		<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
		<script type="text/javascript" src="/public/assets/template/js/jquery.cycle.js"></script>
		<link rel="stylesheet" href="/public/assets/template/css/cycle.css">
	</head>
	<body>
		//your page content

		// just before the </body>
		<script type="text/javascript">
			$('#slider').cycle({});
		</script>
	</body> 
</html>

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>
	<p>Please check if the data below is correct.</p>
	<?php $form = new GO_Site_Widget_Form(); ?>

<?php echo $form->beginForm(); ?>
<ul>
	<li>
		<?php echo $form->label($order, 'customer_name'); ?>
		<?php echo $form->textField($order, 'customer_name'); ?>
		<?php echo $form->error($order, 'customer_name'); ?>
	</li>
	<li>
		<?php echo $form->label($order, 'customer_email'); ?>
		<?php echo $form->textField($order, 'customer_email'); ?>
		<?php echo $form->error($order, 'customer_email'); ?>
	</li>
	<li>
		<?php echo $form->label($order, 'customer_address'); ?>
		<?php echo $form->textField($order, 'customer_address'); ?>
		<?php echo $form->error($order, 'customer_address'); ?>
	</li>
	<li>
		<?php echo $form->label($order, 'customer_country'); ?>
		<?php echo $form->dropDownList($order, 'customer_country', GO::language()->getCountries()); ?>
		<?php echo $form->error($order, 'customer_country'); ?>
	</li>

	<li>Only enter the following field if you don't live in the Netherlands and you have a valid European Union VAT number.</li>
	<li>
		<?php echo $form->label($order, 'customer_vat_no'); ?>
		<?php echo $form->textField($order, 'customer_vat_no'); ?>	
		<?php echo $form->error($order, 'customer_vat_no'); ?>
	</li>
</ul>

<?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',
	));
?>
<table id="your-tickets" summary="Your Tickets">
	<thead>
		<tr><th>Ticket No.</th><th>Name</th><th>Status</th><th>Agent</th><th>Created</th></tr>
	</thead>
	<tfoot>
		<tr><th colspan="5"><?php $pager->render(); // will render the page navigation ?></th></tr>
	</tfoot>
	<tbody>
		<?php if(!$pager->getItems()): ?>
			<tr><td  colspan="5">No tickets found</td></tr>
		<?php else: ?>
		<?php foreach($pager->getItems() as $i => $ticket): ?>
		<tr class="<?php echo ($i%2) ? 'even' : 'odd';; ?>">
			<td><?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>'; ?></td>
			<td><?php echo $ticket->subject; ?></td>
			<td><?php echo $ticket->getStatusName(); ?></td>
			<td><?php echo $ticket->agent?$ticket->agent->name:""; ?></td>
			<td><?php echo $ticket->getAttribute("ctime","formatted"); ?></td>
		</tr>
		<?php endforeach; ?>
		<?php endif; ?>
	</tbody>
</table>

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=3609"