Thursday, February 20, 2014

Creating your own project and time tracking reports

Introduction

With the new projects version 2 module we have created a very powerful and flexible reporting system. We supply open source reports written in PHP so anybody with PHP knowledge can create their own reports.

The reports that come with Group-Office are located in “modules/projects2/report”. There are already reports about time tracking of employees, project information and a general overview of all projects.

All these reports extend a simple abstract class that holds some basic functionality.



Creating the PHP code

It's best to keep your own reports in your data folder (defined in $config['file_storage_path']='/home/groupoffice'). The projects module automatically detects reports in “/home/groupoffice/php/projects2/report”.

So if you would create a report called “MyReport” then you must create the file “MyReport.php” there. Remember that everything is case sensitive!

In MyReport.php define the class name like this:



The start “GOFS” in the class name will instruct the Group-Office class loader to look in the home folder for this report.

In the abstract class there are 3 methods that will control when this report template will be available:

  1. supportsSelectedProject
  2. supportsBatchReport
  3. supportedProjectTypes

It can show up when you run a batch report from the main toolbar in the module or when a project container or project was selected through the “New” → “Report” menu option.

In the following example we create an Excel report using the PHPExcel library.



<?php
/**
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Intermesh BV 
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */

/**
 * Simple XLSX report for project information
 *
 */
class GO_Projects2_Report_MyReport extends GO_Projects2_Report_Abstract {

 /**
  * Return filename extension.
  * @return string
  */
 public function fileExtension() {
  return 'xlsx';
 }

 /**
  * Return the name of the report used in the drop down
  * 
  * @return string
  */
 public function name() {
  return 'My Excel report';
 }

 /**
  * With this enabled it will only show up if this report is created from the
  * "New" menu in a project.
  * 
  * @return boolean defaults to false
  */
 public function supportsSelectedProject() {
  return true;
 }

 /**
  * With this enables it shows up when you create a report from the main toolbar
  * in the projects module.
  * 
  * @return boolean
  */
 public function supportsBatchReport() {
  return false;
 }

 /**
  * Indicates which project types are accepted for this report.
  * @return boolean Array containing for example: GO_Projects2_Model_Template::PROJECT_TYPE_PROJECT or GO_Projects2_Model_Template::PROJECT_TYPE_CONTAINER
  */
 public function supportedProjectTypes() {
  return array(GO_Projects2_Model_Template::PROJECT_TYPE_PROJECT);
 }

 /**
  * Indicate whether this report supports a start and end date
  * 
  * @return boolean defaults to false
  */
 public function supportsDateRange() {
  return false;
 }

 /**
  * Indicate whether this report supports a start and end date
  * 
  * @return boolean defaults to false
  */
 public function supportsStatusFilter() {
  return false;
 }

 /**
  *
  * @var GO_Base_Util_Excel 
  */
 private $_xls;

 /**
  * Start rendering the report.
  * 
  * @param boolean $return to return the file data as string.
  */
 public function render($return = false) {


  $this->project->id;

  //Create an Excel object. See http://www.codeplex.com/PHPExcel) for more 
  //information
  $this->_xls = new GO_Base_Util_Excel();

  $this->_xls->getProperties()->setCreator(GO::user()->name);
  $this->_xls->getProperties()->setLastModifiedBy(GO::user()->name);

  $this->_xls->getProperties()->setTitle($this->name());
  $this->_xls->getProperties()->setSubject($this->project->path);

  $this->_xls->setActiveSheetIndex(0);
  $this->_xls->getActiveSheet()->setTitle("Project information");


  $this->_xls->setDefaultStyle('Arial', 12);
  $this->_xls->setDefaultWidth(15);

  $this->_xls->setCellValue('A1', GO::t('strName'));
  $this->_xls->setCellValue('B1', $this->project->path);


  $this->_xls->setCellValue('A2', GO::t('status', 'projects2'));
  $this->_xls->setCellValue('B2', $this->project->status->name);

  $this->_xls->setCellValue('A3', GO::t('startTime', 'projects2'));
  $this->_xls->setCellValue('B3', $this->project->getAttribute('start_time', 'formatted'));


  $this->_xls->setCellValue('A4', GO::t('projectDue_time', 'projects2'));
  $this->_xls->setCellValue('B4', $this->project->getAttribute('due_time', 'formatted'));

  $this->_xls->setCellValue('A5', GO::t('lag', 'projects2'));
  $this->_xls->setCellValue('B5', GO_Base_Util_Date::minutesToTimeString($this->project->getLag()));

  $this->_xls->setCellValue('A6', GO::t('strDescription'));
  $this->_xls->setCellValue('B6', $this->project->description);
  $this->_xls->getActiveSheet()->getStyle("B6:B6")->getAlignment()->setWrapText(true);


  $file = GO_Base_Fs_File::tempFile($this->filename, $this->fileExtension());


  $this->_xls->save($file->path());


  if ($return) {
   //we must return it as string   
   $content = $file->getContents();
   $file->delete();
   return $content;
  } else {

   //we output it to the browser
   GO_Base_Util_Http::outputDownloadHeaders($file);
   $file->output();
  }
 }

}


Ending

I hope this simple example gets you started with project reports. The existing PDF reports are also a great resource to help you get going.

Tuesday, February 11, 2014

A new interface for Group-Office

First I will share something about the history of the Group-Office interface.

2003
When I first started with an interface for Group-Office back in 2003, life was easy. I just had to worry about the desktop. There were no smart phones and tablets yet! I built my own interface library in pure PHP 4.4 and these rendered HTML tables, treeviews etc. Javascript was not really evolved yet so all rendering was done with PHP. Every page was a separate PHP file that used various classes to render different widgets.

2007
Javascript evolved and it became possible to build very rich interfaces. I decided to make a complete rewrite of Group-Office using Sencha ExtJS 2! ExtJS is a very rich javascript library that has many widgets. The interface was now completely javascript driven and Group-Office had the most sophisticated web interface around. The whole interface was one PHP page that loaded the javascripts. All data is loaded with AJAX calls.

Today...
We still use ExtJS 3.4. This version is outdated but upgrading is a pain. Group-Office has become a big application and upgrading to ExtJS 4.x would in fact mean a complete rewrite of the interface. This is in my humble opinion a major drawback of ExtJS. They tend to completely change their API making it very backwards incompatible. The 2 to 3 upgrade was doable since we didn't even complete the ExtJS 2 version of Group-Office.

Another approach to take in consideration is responsive design. ExtJS is intended for the desktop and the desktop only. It doesn't work well on tablets and smart phones. Today tablets and smart phones can't be ruled out anymore. So if we want to keep up to date we have two options:

  1. Build a new Extjs 4 interface and a Sencha Touch application for tablets and smart phones
  2. Move away from Sencha and build a new responsive framework that works on the Desktop, Tablet and smartphone.

Option 1: Upgrade ExtJS and use Sencha Touch

Pro's:
  1. ExtJS is mature and has all the widgets we would ever need.
  2. The interface looks consistent throughout all modules.
Con's
  1. Upgrading to new major releases of ExtJS has proven to be difficult
  2. We need to maintain two applications. The desktop (ExtJS) and mobile version (Sencha Touch)
  3. The learning curve for ExtJS is steep. It's hard for new developers to start with Group-Office development.
  4. Theming is tough

Option 2: Build a completely new responsive interface framework

Pro's:
  1. One application for both desktop and mobile devices
  2. Flexibility, we can build it anyway we like. We can make it easy to theme.
  3. It will be fun to create your own framework and our developers will get more satisfaction with their own creations.
Con's
  1. We have to develop a lot of widgets that ExtJS already has.
  2. More testing to be done with cross browser support.
  3. More documentation to be written


Conclusion

I don't have a conclusion yet. I hope this article will bring some discussion here and in our company.

I will also start to experiment with alternatives to ExtJS and build a simple responsive interface.