Webylon 3.2 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • 1c
    • exchange
      • catalog
  • auth
  • Booking
  • building
    • company
  • cart
    • shipping
    • steppedcheckout
  • Catalog
    • monument
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • ReportAdmin
  • SS_Report
  • SS_ReportWrapper
  1 <?php
  2 /**
  3  * Base "abstract" class creating reports on your data.
  4  * 
  5  * Creating reports
  6  * ================
  7  * 
  8  * Creating a new report is a matter overloading a few key methods
  9  * 
 10  *  {@link title()}: Return the title - i18n is your responsibility
 11  *  {@link description()}: Return the description - i18n is your responsibility
 12  *  {@link sourceQuery()}: Return a DataObjectSet of the search results
 13  *  {@link columns()}: Return information about the columns in this report.
 14  *  {@link parameterFields()}: Return a FieldSet of the fields that can be used to filter this
 15  *  report.
 16  * 
 17  * If you can't express your report as a query, you can implement the this method instead:
 18  * 
 19  *     // Return an array of fields that can be used to sort the data
 20  *     public function sourceRecords($params, $sort, $limit) { ... }
 21  * 
 22  * The $sort value will be set to the corresponding key of the columns() array.  If you wish to
 23  * make only a subset of the columns sortable, then you can override `sortColumns()` to return a
 24  * subset of the array keys.
 25  * 
 26  * Note that this implementation is less efficient and should only be used when necessary.
 27  * 
 28  * If you wish to modify the report in more extreme ways, you could overload these methods instead.
 29  * 
 30  * {@link getReportField()}: Return a FormField in the place where your report's TableListField
 31  * usually appears.
 32  * {@link getCMSFields()}: Return the FieldSet representing the complete right-hand area of the 
 33  * report, including the title, description, parameter fields, and results.
 34  * 
 35  * Showing reports to the user
 36  * ===========================
 37  * 
 38  * Right now, all subclasses of SS_Report will be shown in the ReportAdmin.  However, we are planning
 39  * on adding an explicit registration mechanism, so that you can decide which reports go in the
 40  * report admin, and which go elsewhere (such as the side panel in the CMS).
 41  * 
 42  * @package cms
 43  * @subpackage reports
 44  */
 45 class SS_Report extends ViewableData {
 46     /**
 47      * Report registry populated by {@link SS_Report::register()}
 48      */
 49     private static $registered_reports = array();
 50 
 51     /**
 52      * This is the title of the report,
 53      * used by the ReportAdmin templates.
 54      *
 55      * @var string
 56      */
 57     protected $title = '';
 58 
 59     /**
 60      * This is a description about what this
 61      * report does. Used by the ReportAdmin
 62      * templates.
 63      *
 64      * @var string
 65      */
 66     protected $description = '';
 67     
 68     /**
 69      * The class of object being managed by this report.
 70      * Set by overriding in your subclass.
 71      */
 72     protected $dataClass = 'SiteTree';
 73     
 74     /**
 75      * Return the title of this report.
 76      * 
 77      * You have two ways of specifying the description:
 78      *  - overriding description(), which lets you support i18n 
 79      *  - defining the $description property
 80      */
 81     function title() {
 82         return $this->title;
 83     }
 84     
 85     /**
 86      * Return the description of this report.
 87      * 
 88      * You have two ways of specifying the description:
 89      *  - overriding description(), which lets you support i18n 
 90      *  - defining the $description property
 91      */
 92     function description() {
 93         return $this->description;
 94     }
 95     
 96     /**
 97      * Return a FieldSet specifying the search criteria for this report.
 98      * 
 99      * Override this method to define search criteria.
100      */
101     function parameterFields() {
102         return null;
103     }
104 
105     /**
106      * Return the {@link SQLQuery} that provides your report data.
107      */
108     function sourceQuery($params) {
109         if($this->hasMethod('sourceRecords')) {
110             $query = new SS_Report_FakeQuery($this, 'sourceRecords', $params);
111             $query->setSortColumnMethod('sortColumns');
112             return $query;
113         } else {
114             user_error("Please override sourceQuery()/sourceRecords() and columns() or, if necessary, override getReportField()", E_USER_ERROR);
115         }
116     }
117     
118     /**
119      * Return a DataObjectSet records for this report.
120      */
121     function records($params) {
122         if($this->hasMethod('sourceRecords')) return $this->sourceRecords($params, null, null);
123         else {
124             $query = $this->sourceQuery();
125             return singleton($this->dataClass())->buildDataObjectSet($query->execute(), "DataObjectSet", $query);
126         }
127     }
128 
129     /**
130      * Return an map of columns for your report.
131      *  - The map keys will be the source columns for your report (in TableListField dot syntax)
132      *  - The values can either be a string (the column title), or a map containing the following
133      *    column parameters:
134      *    - title: The column title
135      *    - formatting: A formatting string passed to {@link TableListField::setFieldFormatting()}
136      */
137     function columns() {
138         user_error("Please override sourceQuery() and columns() or, if necessary, override getReportField()", E_USER_ERROR);
139     }
140     
141     function sortColumns() {
142         return array_keys($this->columns());
143     }
144     
145     /**
146      * Return the number of records in this report with no filters applied.
147      */
148     function count() {
149         return (int)$this->sourceQuery(array())->unlimitedRowCount();
150     }
151     
152     /**
153      * Return the data class for this report
154      */
155     function dataClass() {
156         return $this->dataClass;
157     }
158 
159 
160     /**
161      * Returns a FieldSet with which to create the CMS editing form.
162      * You can use the extend() method of FieldSet to create customised forms for your other
163      * data objects.
164      *
165      * @uses getReportField() to render a table, or similar field for the report. This
166      * method should be defined on the SS_Report subclasses.
167      *
168      * @return FieldSet
169      */
170     function getCMSFields() {
171         $fields = new FieldSet(
172             new LiteralField(
173                 'ReportTitle', 
174                  "<h3>{$this->title()}</h3>"
175             )
176         );
177         
178         if($this->description) $fields->push(
179             new LiteralField('ReportDescription', "<p>{$this->description}</p>"));
180             
181         // Add search fields is available
182         if($params = $this->parameterFields()) {
183             $filters = new FieldGroup('Filters');
184             foreach($params as $param) {
185                 if ($param instanceof HiddenField) $fields->push($param);
186                 else $filters->push($param);
187             }
188             $fields->push($filters);
189 
190             // Add a search button
191             $fields->push(new FormAction('updatereport', _t('SS_Report.FilterAction', 'Filter')));
192         }
193         
194         $fields->push($this->getReportField());
195         
196         $this->extend('updateCMSFields', $fields);
197         
198         return $fields;
199     }
200     
201     function getCMSActions() {
202         // getCMSActions() can be extended with updateCMSActions() on a decorator
203         $actions = new FieldSet();
204         $this->extend('updateCMSActions', $actions);
205         return $actions;
206     }
207     
208     /**
209      * Return a field, such as a {@link ComplexTableField} that is
210      * used to show and manipulate data relating to this report.
211      * 
212      * Generally, you should override {@link columns()} and {@link records()} to make your report,
213      * but if they aren't sufficiently flexible, then you can override this method.
214      *
215      * @return FormField subclass
216      */
217     function getReportField() {
218         $columnTitles = array();
219         $fieldFormatting = array();
220         $csvFieldFormatting = array();
221         $fieldCasting = array();
222         
223         // Parse the column information
224         foreach($this->columns() as $source => $info) {
225             if(is_string($info)) $info = array('title' => $info);
226             
227             if(isset($info['formatting'])) $fieldFormatting[$source] = $info['formatting'];
228             if(isset($info['csvFormatting'])) $csvFieldFormatting[$source] = $info['csvFormatting'];
229             if(isset($info['casting'])) $fieldCasting[$source] = $info['casting'];
230             $columnTitles[$source] = isset($info['title']) ? $info['title'] : $source;
231         }
232         
233         // To do: implement pagination
234         $query = $this->sourceQuery($_REQUEST);
235             
236         $tlf = new TableListField('ReportContent', $this->dataClass(), $columnTitles);
237         $tlf->setCustomQuery($query);
238         $tlf->setShowPagination(true);
239         $tlf->setPageSize(50);
240         $tlf->setPermissions(array('export', 'print'));
241         
242         // Hack to figure out if we are printing
243         $urlData = explode('/', $_REQUEST['url']);
244         if (isset($_REQUEST['url']) && array_pop($urlData) == 'printall') {
245             $tlf->setTemplate('SSReportTableField');
246         }
247         
248         if($fieldFormatting) $tlf->setFieldFormatting($fieldFormatting);
249         if($csvFieldFormatting) $tlf->setCSVFieldFormatting($csvFieldFormatting);
250         if($fieldCasting) $tlf->setFieldCasting($fieldCasting);
251 
252         return $tlf;
253     }
254     
255     /**
256      * @param Member $member
257      * @return boolean
258      */
259     function canView($member = null) {
260         if(!$member && $member !== FALSE) {
261             $member = Member::currentUser();
262         }
263         
264         return true;
265     }
266     
267 
268     /**
269      * Return the name of this report, which
270      * is used by the templates to render the
271      * name of the report in the report tree,
272      * the left hand pane inside ReportAdmin.
273      *
274      * @return string
275      */
276     function TreeTitle() {
277         return $this->title();/* . ' (' . $this->count() . ')'; - this is too slow atm */
278     }
279     
280     /**
281      * Return the ID of this Report class.
282      * Because it doesn't have a number, we
283      * use the class name as the ID.
284      *
285      * @return string
286      */
287     function ID() {
288         return $this->class;
289     }
290     
291     /////////////////////////////////////////////////////////////////////////////////////////////
292     
293     /**
294      * Register a report.
295      * @param $list The list to add the report to: "ReportAdmin" or "SideReports"
296      * @param $reportClass The class of the report to add.
297      * @param $priority The priority.  Higher numbers will appear furhter up in the reports list.
298      * The default value is zero.
299      */
300     static function register($list, $reportClass, $priority = 0) {
301         if(strpos($reportClass, '(') === false && (!class_exists($reportClass) || !is_subclass_of($reportClass,'SS_Report'))) {
302             user_error("SS_Report::register(): '$reportClass' is not a subclass of SS_Report", E_USER_WARNING);
303             return;
304         }
305         
306         self::$registered_reports[$list][$reportClass] = $priority;
307     }
308     
309     /**
310      * Unregister a report, removing it from the list
311      */
312     static function unregister($list, $reportClass) {
313         unset(self::$registered_reports[$list][$reportClass]);
314     }
315     
316     /**
317      * Return the SS_Report objects making up the given list.
318      * @return An array of SS_Report objects
319      */
320     static function get_reports($list) {
321         $output = array();
322         if(isset(self::$registered_reports[$list])) {
323             $listItems = self::$registered_reports[$list];
324 
325             // Sort by priority, preserving internal order of items with the same priority
326             $groupedItems = array();
327             foreach($listItems as $k => $v) {
328                 $groupedItems[$v][] = $k;
329             }
330             krsort($groupedItems);
331             $sortedListItems = call_user_func_array('array_merge', $groupedItems);
332             
333             foreach($sortedListItems as $report) {
334                 if(strpos($report,'(') === false) $reportObj = new $report;
335                 else $reportObj = eval("return new $report;");
336 
337                 $output[$reportObj->ID()] = $reportObj;
338             } 
339         }
340         
341         return $output;
342     }
343 
344 }
345 
346 /**
347  * This is an object that can be used to dress up a more complex querying mechanism in the clothing
348  * of a SQLQuery object.  This means that you can inject it into a TableListField.
349  * 
350  * Use it like this:
351  * 
352  *     function sourceQuery($params) {
353  *         return new SS_Report_FakeQuery($this, 'sourceRecords', $params)
354  *     }
355  *     function sourceRecords($params, $sort, $limit) {
356  *         // Do some stuff
357  *         // Return a DataObjectSet of actual objects.
358  *     }
359  * 
360  * This object is used by the default implementation of sourceQuery() on SS_Report, to make use of
361  * a sourceReords() method if one exists.
362  */
363 class SS_Report_FakeQuery extends SQLQuery {
364     public $orderby;
365     public $limit;
366     
367     protected $obj, $method, $params;
368     
369     function __construct($obj, $method, $params) {
370         $this->obj = $obj;
371         $this->method = $method;
372         $this->params = $params;
373     }
374     
375     /**
376      * Provide a method that will return a list of columns that can be used to sort.
377      */
378     function setSortColumnMethod($sortColMethod) {
379         $this->sortColMethod = $sortColMethod;
380     }
381 
382     function limit($limit) {
383         $this->limit = $limit;
384     }
385     
386     function unlimitedRowCount($column = NULL) {
387         $source = $this->obj->{$this->method}($this->params, null, null);
388         return $source ? $source->Count() : 0;
389     }
390     
391     function execute() {
392         $output = array();
393         $source = $this->obj->{$this->method}($this->params, $this->orderby, $this->limit);
394         if($source) foreach($source as $item) {
395             $mapItem = $item->toMap();
396             $mapItem['RecordClassName'] = get_class($item);
397             $output[] = $mapItem;
398         }
399         return $output;
400     }
401     
402     function canSortBy($fieldName) {
403         $fieldName = preg_replace('/(\s+?)(A|DE)SC$/', '', $fieldName);
404         if($this->sortColMethod) {
405             $columns = $this->obj->{$this->sortColMethod}();
406             return in_array($fieldName, $columns);
407         } else {
408             return false;
409         }
410     }
411 }
412 
413 /**
414  * SS_ReportWrapper is a base class for creating report wappers.
415  * 
416  * Wrappers encapsulate an existing report to alter their behaviour - they are implementations of
417  * the standard GoF decorator pattern.
418  * 
419  * This base class ensure that, by default, wrappers behave in the same way as the report that is
420  * being wrapped.  You should override any methods that need to behave differently in your subclass
421  * of SS_ReportWrapper.
422  * 
423  * It also makes calls to 2 empty methods that you can override {@link beforeQuery()} and
424  * {@link afterQuery()}
425  * 
426  * @package cms
427  * @subpackage reports
428  */
429 abstract class SS_ReportWrapper extends SS_Report {
430     protected $baseReport;
431 
432     function __construct($baseReport) {
433         $this->baseReport = is_string($baseReport) ? new $baseReport : $baseReport;
434         $this->dataClass = $this->baseReport->dataClass();
435         parent::__construct();
436     }
437 
438     function ID() {
439         return get_class($this->baseReport) . '_' . get_class($this);
440     }
441 
442     ///////////////////////////////////////////////////////////////////////////////////////////
443     // Filtering
444 
445     function parameterFields() {
446         return $this->baseReport->parameterFields();
447     }
448 
449     ///////////////////////////////////////////////////////////////////////////////////////////
450     // Columns
451 
452     function columns() {
453         return $this->baseReport->columns();
454     }
455 
456     ///////////////////////////////////////////////////////////////////////////////////////////
457     // Querying
458 
459     /**
460      * Override this method to perform some actions prior to querying.
461      */
462     function beforeQuery($params) {
463     }
464 
465     /**
466      * Override this method to perform some actions after querying.
467      */
468     function afterQuery() {
469     }
470 
471     function sourceQuery($params) {
472         if($this->baseReport->hasMethod('sourceRecords')) {
473             // The default implementation will create a fake query from our sourceRecords() method
474             return parent::sourceQuery($params);
475 
476         } else if($this->baseReport->hasMethod('sourceQuery')) {
477             $this->beforeQuery($params);
478             $query = $this->baseReport->sourceQuery($params);
479             $this->afterQuery();
480             return $query;
481 
482         } else {
483             user_error("Please override sourceQuery()/sourceRecords() and columns() in your base report", E_USER_ERROR);
484         }
485 
486     }
487 
488     function sourceRecords($params = array(), $sort = null, $limit = null) {
489         $this->beforeQuery($params);
490         $records = $this->baseReport->sourceRecords($params, $sort, $limit);
491         $this->afterQuery();
492         return $records;
493     }
494 
495 
496     ///////////////////////////////////////////////////////////////////////////////////////////
497     // Pass-through
498 
499     function title() {
500         return $this->baseReport->title();
501     }
502     
503     function group() {
504         return $this->baseReport->hasMethod('group') ? $this->baseReport->group() : 'Group';
505     }
506     
507     function sort() {
508         return $this->baseReport->hasMethod('sort') ? $this->baseReport->sort() : 0;
509     }
510 
511     function description() {
512         return $this->baseReport->title();
513     }
514 
515     function canView($member = NULL) {
516         return $this->baseReport->canView($member);
517     }
518     
519 }
520 
521 ?>
522 
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.2 API Docs API documentation generated by ApiGen 2.8.0