add ods export to calendar
authorCornelius Weiß <mail@corneliusweiss.de>
Mon, 22 Dec 2014 13:47:49 +0000 (14:47 +0100)
committerPhilipp Schüle <p.schuele@metaways.de>
Mon, 29 Dec 2014 11:30:27 +0000 (12:30 +0100)
* export button to be added in master
** merge with import btn to a single height btn group

Change-Id: Ic506628f165a0d39a59571cecb1c7f9dd0096537
Reviewed-on: http://gerrit.tine20.com/customers/1461
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Jenkins CI (http://ci.tine20.com/)
13 files changed:
tests/tine20/Calendar/AllTests.php
tests/tine20/Calendar/Export/OdsTests.php [new file with mode: 0644]
tine20/Calendar/Calendar.jsb2
tine20/Calendar/Export/Abstract.php [new file with mode: 0644]
tine20/Calendar/Export/Ods.php [new file with mode: 0644]
tine20/Calendar/Export/definitions/cal_default_ods.xml [new file with mode: 0644]
tine20/Calendar/Frontend/Http.php
tine20/Calendar/js/ExportButton.js [new file with mode: 0644]
tine20/Calendar/js/MainScreenCenterPanel.js
tine20/Tinebase/Export/Abstract.php
tine20/Tinebase/Export/Spreadsheet/Ods.php
tine20/Tinebase/Record/RecordSet.php
tine20/Tinebase/Translation.php

index 5880f87..f15f3de 100644 (file)
@@ -42,6 +42,7 @@ class Calendar_AllTests
         $suite->addTestSuite('Calendar_Import_ICalTest');
         $suite->addTestSuite('Calendar_Import_CalDAVTest');
         $suite->addTestSuite('Calendar_Export_ICalTest');
+        $suite->addTestSuite('Calendar_Export_OdsTests');
         $suite->addTestSuite('Calendar_Convert_Event_VCalendar_AllTests');
         $suite->addTestSuite('Calendar_Setup_DemoDataTests');
         
diff --git a/tests/tine20/Calendar/Export/OdsTests.php b/tests/tine20/Calendar/Export/OdsTests.php
new file mode 100644 (file)
index 0000000..5eaa24a
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ *
+ * @package     Calendar
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Cornelius Weiss <c.weiss@metaways.de>
+ *
+ */
+
+/**
+ * Test class for Calendar_Export_Ods
+ */
+class Calendar_Export_OdsTests extends Calendar_TestCase
+{
+    public function testExportOds()
+    {
+        // export & check
+        $csvExportClass = new Calendar_Export_Ods(new Calendar_Model_EventFilter(array()));
+        $result = $csvExportClass->generate();
+    }
+}
\ No newline at end of file
index 837c81b..481d64c 100644 (file)
           "path": "js/"
         },
         {
+          "text": "ExportButton.js",
+          "path": "js/"
+        },
+        {
           "text": "MainScreenCenterPanel.js",
           "path": "js/"
         },
diff --git a/tine20/Calendar/Export/Abstract.php b/tine20/Calendar/Export/Abstract.php
new file mode 100644 (file)
index 0000000..e01c5a3
--- /dev/null
@@ -0,0 +1,101 @@
+<?php
+/**
+ * abstract calendar export class
+ *
+ * @package     Calendar
+ * @subpackage  Export
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Cornelius Weiss <c.weiss@metaways.de>
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+
+/**
+ * abstract calendar export class
+ *
+ * @package     Calendar
+ * @subpackage  Export
+ *
+ */
+abstract class Calendar_Export_Abstract extends Tinebase_Export_Spreadsheet_Ods
+{
+    /**
+     * @var string application of this export class
+     */
+    protected $_applicationName = 'Calendar';
+
+    /**
+     * @var Tinebase_DateTime
+     */
+    protected $_from = NULL;
+
+    /**
+     * @var Tinebase_DateTime
+     */
+    protected $_until = NULL;
+
+    /**
+     * the constructor
+     *
+     * @param Tinebase_Model_Filter_FilterGroup $_filter
+     * @param Tinebase_Controller_Record_Interface $_controller (optional)
+     * @param array $_additionalOptions (optional) additional options
+     */
+    public function __construct(Tinebase_Model_Filter_FilterGroup $_filter, Tinebase_Controller_Record_Interface $_controller = NULL, $_additionalOptions = array())
+    {
+        $periodFilter = $_filter->getFilter('period');
+
+        if ($periodFilter) {
+            $this->_from = $periodFilter->getFrom();
+            $this->_until = $periodFilter->getUntil();
+        }
+
+        parent::__construct($_filter, $_controller, $_additionalOptions);
+    }
+
+    /**
+     * export records
+     */
+    protected function _exportRecords()
+    {
+        // to support rrule & sorting we can't do pagination in calendar exports
+        $records = $this->_controller->search($this->_filter, NULL, $this->_getRelations, false, 'export');
+        Calendar_Model_Rrule::mergeAndRemoveNonMatchingRecurrences($records, $this->_filter);
+
+        $records->sort(function($r1, $r2) {
+            return $r1->container_id < $r2->container_id && $r1->dtstart < $r2->dtstart;
+        });
+
+        $this->_resolveRecords($records);
+        foreach($records as $idx => $record) {
+            $this->processRecord($record, $idx);
+        }
+
+        $result = array();
+
+        $this->_onAfterExportRecords($result);
+    }
+
+    /**
+     * resolve records and prepare for export (set user timezone, ...)
+     *
+     * @param Tinebase_Record_RecordSet $_records
+     */
+    protected function _resolveRecords(Tinebase_Record_RecordSet $_records)
+    {
+        Calendar_Model_Attender::resolveAttendee($_records->attendee, false, $_records);
+        $organizers = Addressbook_Controller_Contact::getInstance()->getMultiple(array_unique($_records->organizer), TRUE);
+
+        foreach($_records as $record) {
+            $attendee = $record->attendee->getName();
+            $record->attendee = implode('& ', $attendee);
+
+            $organizer = $organizers->getById($record->organizer);
+            if ($organizer) {
+                $record->organizer = $organizer->n_fileas;
+            }
+        }
+    }
+
+
+}
diff --git a/tine20/Calendar/Export/Ods.php b/tine20/Calendar/Export/Ods.php
new file mode 100644 (file)
index 0000000..92d2c37
--- /dev/null
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Ods export generation class
+ *
+ * @package     Calendar
+ * @subpackage  Export
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Cornelius Weiss <c.weiss@metaways.de>
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+
+/**
+ * Calendar Ods generation class
+ *
+ * @package     Calendar
+ * @subpackage  Export
+ *
+ */
+class Calendar_Export_Ods extends Calendar_Export_Abstract
+{
+    /**
+     * the constructor
+     *
+     * @param Tinebase_Model_Filter_FilterGroup $_filter
+     * @param Tinebase_Controller_Record_Interface $_controller (optional)
+     * @param array $_additionalOptions (optional) additional options
+     */
+    public function __construct(Tinebase_Model_Filter_FilterGroup $_filter, Tinebase_Controller_Record_Interface $_controller = NULL, $_additionalOptions = array())
+    {
+
+        parent::__construct($_filter, $_controller, $_additionalOptions);
+    }
+
+    /**
+     * default export definition name
+     *
+     * @var string
+     */
+    protected $_defaultExportname = 'cal_default_ods';
+}
diff --git a/tine20/Calendar/Export/definitions/cal_default_ods.xml b/tine20/Calendar/Export/definitions/cal_default_ods.xml
new file mode 100644 (file)
index 0000000..92170a4
--- /dev/null
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<config>
+    <model>Calendar_Model_Event</model>
+    <name>cal_default_ods</name>
+    <type>export</type>
+    <plugin>Calendar_Export_Ods</plugin>
+    <label>Default OpenDocument Export</label>
+    <description>default ods event export definition</description>
+    <header>1</header>
+    <headers>
+        <header>{date}</header>
+        <header>{user}</header>
+    </headers>
+    <columns>
+        <column>
+            <identifier>id</identifier>
+        </column>
+        <column>
+            <identifier>summary</identifier>
+        </column>
+        <column>
+            <identifier>dtstart</identifier>
+        </column>
+        <column>
+            <identifier>dtend</identifier>
+        </column>
+        <column>
+            <identifier>attendee</identifier>
+        </column>
+        <column>
+            <identifier>description</identifier>
+        </column>
+        <column>
+            <identifier>location</identifier>
+        </column>
+        <column>
+            <identifier>organizer</identifier>
+            <type>Contact</type>
+        </column>
+        <column>
+            <identifier>status</identifier>
+        </column>
+        <column>
+            <identifier>priority</identifier>
+        </column>
+        <column>
+            <identifier>class</identifier>
+        </column>
+        <column>
+            <identifier>transp</identifier>
+        </column>
+        <column>
+            <identifier>uid</identifier>
+        </column>
+        <column>
+            <identifier>rrule</identifier>
+        </column>
+        <column>
+            <identifier>recurid</identifier>
+        </column>
+        <column>
+            <identifier>tags</identifier>
+            <type>tags</type>
+        </column>
+        <column>
+            <identifier>container_id</identifier>
+        </column>
+        <column>
+            <identifier>created_by</identifier>
+        </column>
+        <column>
+            <identifier>creation_time</identifier>
+        </column>
+        <column>
+            <identifier>last_modified_by</identifier>
+        </column>
+        <column>
+            <identifier>last_modified_time</identifier>
+        </column>
+    </columns>
+</config>
index fdc5efd..6d51d2a 100644 (file)
 class Calendar_Frontend_Http extends Tinebase_Frontend_Http_Abstract
 {
     protected $_applicationName = 'Calendar';
-    
+
+    /**
+     * export events
+     *
+     * @param string $filter JSON encoded string with items ids for multi export or item filter
+     * @param string $options format or export definition id
+     */
+    public function exportEvents($filter, $options)
+    {
+        $decodedFilter = Zend_Json::decode($filter);
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Export filter: ' . print_r($decodedFilter, TRUE));
+
+        if (! is_array($decodedFilter)) {
+            $decodedFilter = array(array('field' => 'id', 'operator' => 'equals', 'value' => $decodedFilter));
+        }
+
+        $filter = new Calendar_Model_EventFilter($decodedFilter);
+        parent::_export($filter, Zend_Json::decode($options), Calendar_Controller_Event::getInstance());
+    }
 }
diff --git a/tine20/Calendar/js/ExportButton.js b/tine20/Calendar/js/ExportButton.js
new file mode 100644 (file)
index 0000000..1a7511d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Tine 2.0
+ * 
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Cornelius Weiß <c.weiss@metaways.de>
+ * @copyright   Copyright (c) 2-14 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+Ext.ns('Tine.Calendar');
+
+
+Tine.Calendar.ExportButton = Ext.extend(Tine.widgets.grid.ExportButton, {
+
+    doExport: function() {
+        var app = Tine.Tinebase.appMgr.get('Calendar'),
+            mainScreen = app.getMainScreen(),
+            centerPanel = mainScreen.getCenterPanel(),
+            filterData = centerPanel.getAllFilterData({
+                noPeriodFilter: false
+            });
+
+        if (this.showExportDialog) {
+            Tine.widgets.dialog.ExportDialog.openWindow({
+                appName: 'Calendar',
+                record: new Tine.Tinebase.Model.ExportJob({
+                    filter: filterData,
+                    format: this.format,
+                    exportFunction: this.exportFunction,
+                    recordsName: Tine.Calendar.Model.Event.getRecordsName(),
+                    count: '',
+                    model: 'Calendar_Model_Event'
+                })
+            });
+        } else {
+            this.startDownload(filterData);
+        }
+    }
+
+});
\ No newline at end of file
index 7bd9738..7317671 100644 (file)
@@ -183,7 +183,37 @@ Tine.Calendar.MainScreenCenterPanel = Ext.extend(Ext.Panel, {
                 }]
             }
         });
-        
+
+        /**
+         * @type {Ext.Action}
+         */
+        this.actions_exportEvents = new Ext.Action({
+            requiredGrant: 'exportGrant',
+            text: this.app.i18n._('Export Events'),
+            translationObject: this.app.i18n,
+            iconCls: 'action_export',
+            scope: this,
+            allowMultiple: true,
+            menu: {
+                items: [
+                    new Tine.Calendar.ExportButton({
+                        text: this.app.i18n._('Export as ODS'),
+                        format: 'ods',
+                        iconCls: 'tinebase-action-export-ods',
+                        exportFunction: 'Calendar.exportEvents',
+                        gridPanel: this
+                    }),
+                    new Tine.Calendar.ExportButton({
+                        text: this.app.i18n._('Export as ...'),
+                        iconCls: 'tinebase-action-export-xls',
+                        exportFunction: 'Calendar.exportEvents',
+                        showExportDialog: true,
+                        gridPanel: this
+                    })
+                ]
+            }
+        });
+
         this.showSheetView = new Ext.Button({
             pressed: String(this.activeView).match(/sheet$/i),
             scale: 'medium',
@@ -264,7 +294,16 @@ Tine.Calendar.MainScreenCenterPanel = Ext.extend(Ext.Panel, {
     getActionToolbar: Tine.widgets.grid.GridPanel.prototype.getActionToolbar,
     
     getActionToolbarItems: function() {
-        return {
+        return [/*{
+            xtype: 'buttongroup',
+            columns: 1,
+            rows: 2,
+            frame: false,
+            items: [
+                this.actions_exportEvents
+                // @TODO move import btn here
+            ]
+        }, */{
             xtype: 'buttongroup',
             plugins: [{
                 ptype: 'ux.itemregistry',
@@ -274,7 +313,7 @@ Tine.Calendar.MainScreenCenterPanel = Ext.extend(Ext.Panel, {
                 this.showSheetView,
                 this.showGridView
             ]
-        };
+        }];
     },
     
     /**
@@ -424,13 +463,13 @@ Tine.Calendar.MainScreenCenterPanel = Ext.extend(Ext.Panel, {
     /**
      * returns all filter data for current view
      */
-    getAllFilterData: function () {
+    getAllFilterData: function (options) {
         var store = this.getCalendarPanel(this.activeView).getStore();
         
-        var options = {
+        options = Ext.apply({
             refresh: true, // ommit loadMask
             noPeriodFilter: true
-        };
+        }, options || {});
         this.onStoreBeforeload(store, options);
         
         return options.params.filter;
index 1244a46..e98e734 100644 (file)
@@ -264,7 +264,7 @@ abstract class Tinebase_Export_Abstract
         } else {
             $types = $this->_resolvedFields;
         }
-        
+
         // resolve users
         foreach ($this->_userFields as $field) {
             if (in_array($field, $types) || in_array($field, $identifiers)) {
@@ -272,7 +272,7 @@ abstract class Tinebase_Export_Abstract
                 Tinebase_User::getInstance()->resolveMultipleUsers($_records, $field, TRUE);
             }
         }
-        
+
         // add notes
         if (in_array('notes', $types)) {
             Tinebase_Notes::getInstance()->getMultipleNotesOfRecords($_records, 'notes', 'Sql', FALSE);
index 44ed6e7..f1a3eed 100644 (file)
@@ -93,7 +93,14 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
      * @var OpenDocument_Document
      */
     protected $_openDocumentObject = NULL;
-    
+
+    /**
+     * the spreadsheet object
+     *
+     * @var OpenDocument_SpreadSheet
+     */
+    protected $_spreadSheetObject = NULL;
+
     /**
      * spreadsheet table
      * 
@@ -113,14 +120,14 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
         // build export table (use current table if using template)
         Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Creating export for ' . $this->_modelName . ' . ' . $this->_getDataTableName());
         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($this->_config->toArray(), TRUE));
-        
-        $spreadSheet = $this->_openDocumentObject->getBody();
+
+        $this->_spreadSheetObject = $this->_openDocumentObject->getBody();
         
         // append / use existing table
-        if ($spreadSheet->tableExists($this->_getDataTableName()) === true) {
-            $this->_activeTable = $spreadSheet->getTable($this->_getDataTableName());
+        if ($this->_spreadSheetObject->tableExists($this->_getDataTableName()) === true) {
+            $this->_activeTable = $this->_spreadSheetObject->getTable($this->_getDataTableName());
         } else {
-            $this->_activeTable = $spreadSheet->appendTable($this->_getDataTableName());
+            $this->_activeTable = $this->_spreadSheetObject->appendTable($this->_getDataTableName());
         }
         
         // add header (disabled at the moment)
@@ -223,38 +230,48 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
         // add record rows
         $i = 0;
         foreach ($_records as $record) {
-            
-            $row = $this->_activeTable->appendRow();
+            $this->processRecord($record, $i);
+            $i++;
+        }
+        
+    }
 
-            foreach ($this->_config->columns->column as $field) {
-                
-                //$altStyle = 'ceAlternate';
-                
-                // get type and value for cell
-                $cellType = $this->_getCellType($field->type);
-                $cellValue = $this->_getCellValue($field, $record, $cellType);
-                
-                // create cell with type and value and add style
-                $cell = $row->appendCell($cellValue, $cellType);
-                
-                // add formula
-                if ($field->formula) {
-                    $cell->setFormula($field->formula);
-                }
+    /**
+     * add single body row
+     *
+     * @param $record
+     */
+    public function processRecord($record, $idx)
+    {
+
+        $row = $this->_activeTable->appendRow();
+
+        foreach ($this->_config->columns->column as $field) {
 
-                /*
-                if ($i % 2 == 1) {
-                    $cell->setStyle($altStyle);
-                }
-                */
-                
-                //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($field->toArray(), true));
+            //$altStyle = 'ceAlternate';
+
+            // get type and value for cell
+            $cellType = $this->_getCellType($field->type);
+            $cellValue = $this->_getCellValue($field, $record, $cellType);
+
+            // create cell with type and value and add style
+            $cell = $row->appendCell($cellValue, $cellType);
+
+            // add formula
+            if ($field->formula) {
+                $cell->setFormula($field->formula);
             }
-            $i++;
+
+            /*
+            if ($i % 2 == 1) {
+                $cell->setStyle($altStyle);
+            }
+            */
+
+            //if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . print_r($field->toArray(), true));
         }
-        
     }
-    
+
     /**
      * add style/width to column
      *
@@ -265,7 +282,7 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
     {
         $this->_openDocumentObject->addStyle('<style:style style:name="' . $_styleName . '" style:family="table-column" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0"><style:table-column-properties style:column-width="' . $_columnWidth . '"/></style:style>');
     }
-    
+
     /**
      * get name of data table
      * 
index 80508ee..b423d05 100644 (file)
@@ -675,8 +675,12 @@ class Tinebase_Record_RecordSet implements IteratorAggregate, Countable, ArrayAc
      */
     public function sort($_field, $_direction = 'ASC', $_sortFunction = 'asort', $_flags = SORT_REGULAR)
     {
-        $offsetToSortFieldMap = $this->__get($_field);
-        
+        if (! is_string($_field) && is_callable($_field)) {
+            $_sortFunction = 'function';
+        } else {
+            $offsetToSortFieldMap = $this->__get($_field);
+        }
+
         switch ($_sortFunction) {
             case 'asort':
                 $fn = $_direction == 'ASC' ? 'asort' : 'arsort';
@@ -689,6 +693,10 @@ class Tinebase_Record_RecordSet implements IteratorAggregate, Countable, ArrayAc
                     $offsetToSortFieldMap = array_reverse($offsetToSortFieldMap);
                 }
                 break;
+            case 'function':
+                uasort ($this->_listOfRecords , $_field);
+                $offsetToSortFieldMap = $this->_listOfRecords;
+                break;
             default:
                 throw new Tinebase_Exception_InvalidArgument('Sort function unknown.');
         }
index a9f58ba..768038f 100644 (file)
@@ -519,17 +519,25 @@ class Tinebase_Translation
         
         $date = new Zend_Date($date->getTimestamp());
         $date->setTimezone($timezone);
-        
-        $dateString = $date->toString(Zend_Locale_Format::getDateFormat($locale), $locale);
-        if ($addWeekday) {
-            $dateString = $date->toString('EEEE', $locale) . ', ' . $dateString;
-        }
-        $timeString = $date->toString(Zend_Locale_Format::getTimeFormat($locale), $locale);
-        
-        switch($part) {
-            case 'date': return $dateString;
-            case 'time': return $timeString;
-            default: return $dateString . ' ' . $timeString;
+
+        if (in_array($part, array('date', 'time', 'datetime'))) {
+            $dateString = $date->toString(Zend_Locale_Format::getDateFormat($locale), $locale);
+            if ($addWeekday) {
+                $dateString = $date->toString('EEEE', $locale) . ', ' . $dateString;
+            }
+            $timeString = $date->toString(Zend_Locale_Format::getTimeFormat($locale), $locale);
+
+            switch ($part) {
+                case 'date':
+                    return $dateString;
+                case 'time':
+                    return $timeString;
+                default:
+                    return $dateString . ' ' . $timeString;
+            }
+        } else {
+            return $date->toString($part, $locale);
         }
+
     }
 }