3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | This file is a part of CiviCRM. |
8 | CiviCRM is free software; you can copy, modify, and distribute it |
9 | under the terms of the GNU Affero General Public License |
10 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
12 | CiviCRM is distributed in the hope that it will be useful, but |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
15 | See the GNU Affero General Public License for more details. |
17 | You should have received a copy of the GNU Affero General Public |
18 | License and the CiviCRM Licensing Exception along |
19 | with this program; if not, contact CiviCRM LLC |
20 | at info[AT]civicrm[DOT]org. If you have questions about the |
21 | GNU Affero General Public License or the licensing of CiviCRM, |
22 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
23 +--------------------------------------------------------------------+
27 * Copyright (C) 2009 James Rakich
28 * Licensed to CiviCRM under the Academic Free License version 3.0.
34 * civicrm.views.inc Called from civicrm.module, gives the views cache all information it needs
35 * to access CiviCRM's database for use in Views, as well as referencing the
36 * custom handlers for displaying, sorting, filtering and accepting arguments
39 // Defines tables, joins and relationships
40 * function civicrm_views_data() {
41 * CiviCRM Contacts Base Table
42 * CiviCRM Activities Base Table
43 * CiviCRM Relationships Base Table
44 * Custom Data Cache Query and Calls
46 * function civicrm_views_href( $text, $path, $query )
47 * Generates a link for CiviCRM Paths - unchanged from previous code (anonymous donation)
49 * function civicrm_views_custom_data_cache($data, $entity_type, $groupID, $subType, $style)
50 * Collects the data from Custom Data Groups and assigns them to base tables.
52 * function civicrm_views_get_field ($type)
53 * function civicrm_views_get_argument ($type)
54 * function civicrm_views_get_filter ($type)
55 * function civicrm_views_get_sort ($type)
56 * Assign handlers to custom fields based on the data type (from the database records)
58 * function civicrm_date_api_tables()
59 * function civicrm_date_api_fields()
60 * Tells Views which fields to expose as Date API fields
64 * Implements of hook_views_data().
65 * Run hook_views_data for active CiviCRM components
67 function civicrm_views_data_alter(&$data) {
68 if (!civicrm_initialize() || CRM_Utils_System::isInUpgradeMode()) {
72 require_once 'CRM/Core/Config.php';
73 require_once 'CRM/Core/BAO/CustomGroup.php';
74 require_once 'CRM/Core/DAO.php';
75 require_once 'CRM/Core/Error.php';
76 require_once 'CRM/Contact/BAO/Contact.php';
77 require_once 'CRM/Event/BAO/Query.php';
78 require_once 'CRM/Case/BAO/Case.php';
79 require_once 'components/civicrm.core.inc';
81 // Get list of enabled CiviCRM components
82 $enabled = CRM_Core_Component::getEnabledComponents();
85 // Load Core CiviCRM data
86 _civicrm_core_data($data, $enabled);
88 // Load enabled optional components
89 if (isset($enabled['CiviCampaign'])) {
90 include_once 'components/civicrm.campaign.inc';
91 _civicrm_campaign_data($data, $enabled);
93 // Though not explicitly effectively CiviPledge depends on CiviContribute
94 // so they share an include file
95 if (isset($enabled['CiviContribute']) ||
96 isset($enabled['CiviPledge'])
98 include_once 'components/civicrm.contribute.inc';
99 _civicrm_contribute_data($data, $enabled);
101 if (isset($enabled['CiviEvent'])) {
102 include_once 'components/civicrm.event.inc';
103 _civicrm_event_data($data, $enabled);
105 if (isset($enabled['CiviGrant'])) {
106 include_once 'components/civicrm.grant.inc';
107 _civicrm_grant_data($data, $enabled);
109 if (isset($enabled['CiviMail'])) {
110 include_once 'components/civicrm.mail.inc';
111 _civicrm_mail_data($data, $enabled);
113 if (isset($enabled['CiviMember'])) {
114 include_once 'components/civicrm.member.inc';
115 _civicrm_member_data($data, $enabled);
117 if (isset($enabled['CiviCase'])) {
118 include_once 'components/civicrm.case.inc';
119 _civicrm_case_data($data, $enabled);
121 if (isset($enabled['CiviContribute']) ||
122 isset($enabled['CiviEvent']) ||
123 isset($enabled['CiviMember'])) {
124 include_once 'components/civicrm.price_set.inc';
125 _civicrm_price_set_data($data, $enabled);
132 * Return link to CiviCRM path
140 * @return String path to CiviCRM
142 function civicrm_views_href($text, $path, $query) {
143 civicrm_initialize();
144 require_once 'CRM/Utils/System.php';
145 return CRM_Utils_System::href($text, $path, $query);
149 * Return url to CiviCRM path
151 * @param $path string The path being linked to, such as "civicrm/add"
152 * @param $query string A query string to append to the link.
153 * @param $absolute boolean Whether to force the output to be an absolute link (beginning with http:).
154 * Useful for links that will be displayed outside the site, such as in an
157 * @return string an HTML string containing a link to the given path.
159 function civicrm_views_url($path, $query, $absolute = FALSE) {
160 civicrm_initialize();
161 require_once 'CRM/Utils/System.php';
162 return CRM_Utils_System::url($path, $query, $absolute);
166 * Creates new View fields from CiviCRM fields
169 * Array of fields in a table obtained from a DAO fields method for $tableName
172 * Array returned to hook_views_data
175 * String tabled nam of field whose DAO is returned in $fields
178 * (optional) Array of fields not to add form the $fields table
180 function civicrm_views_add_fields(&$fields, &$data, $tableName, &$skipFields = NULL) {
181 foreach ($fields as $name => $value) {
182 // Only add fields not in $data or $skipFields and has a ['title']
183 if (isset($value['custom_field_id']) ||
184 CRM_Utils_Array::value($name, $skipFields) ||
185 CRM_Utils_Array::value($name, $data) ||
186 !isset($value['title']) ||
187 (isset($value['where']) &&
188 substr($value['where'], 0, strlen($tableName) + 1) != "{$tableName}."
194 // Ensure the field isn't alredy defined in $data using $data[$xyz]['real field'] where $xyz is a field name passed in $data
195 foreach ($data as $field => $current) {
196 if (isset($current['real field']) and $current['real field'] == $name) {
201 $type = CRM_Utils_Array::value('type', $value, 'String');
202 $data[$value['name']] = array(
203 'title' => $value['title'],
204 'help' => $value['title'],
205 'field' => civicrm_views_get_field($type),
206 'sort' => civicrm_views_get_sort($type),
207 'filter' => civicrm_views_get_filter($type),
208 'argument' => civicrm_views_get_argument($type),
211 // For date fields add in 6 arguments
212 // not sure how its numeric here, but leaving it as is for now
214 civicrm_views_add_date_arguments($data, $value);
220 * Function adds 6 date arguments to a date field
223 * Array passed back to hook_views_data()
226 * Array contains meta data about field from DAO fields function
229 function civicrm_views_add_date_arguments(&$data, $value) {
231 $data[$value['name']]['argument'] = array(
232 'handler' => 'date_views_argument_handler',
233 'empty field name' => t('Undated'),
236 $data[$value['name'] . '_full'] = array(
237 'title' => $value['title'],
238 'help' => t('In the form of CCYYMMDD.'),
240 'field' => $value['name'],
241 'handler' => 'views_handler_argument_civicrm_fulldate',
244 $data[$value['name'] . '_year_month'] = array(
245 'title' => t('%title year + month', array('%title' => $value['title'])),
246 'help' => t('In the form of YYYYMM.'),
248 'field' => $value['name'],
249 'handler' => 'views_handler_argument_civicrm_year_month',
252 $data[$value['name'] . '_year'] = array(
253 'title' => t('%title year', array('%title' => $value['title'])),
254 'help' => t('In the form of YYYY.'),
256 'field' => $value['name'],
257 'handler' => 'views_handler_argument_civicrm_year',
260 $data[$value['name'] . '_month'] = array(
261 'title' => t('%title month', array('%title' => $value['title'])),
262 'help' => t('In the form of MM (01 - 12).'),
264 'field' => $value['name'],
265 'handler' => 'views_handler_argument_civicrm_month',
268 $data[$value['name'] . '_day'] = array(
269 'title' => t('%title day', array('%title' => $value['title'])),
270 'help' => t('In the form of DD (01 - 31).'),
272 'field' => $value['name'],
273 'handler' => 'views_handler_argument_civicrm_day',
276 $data[$value['name'] . '_week'] = array(
277 'title' => t('%title week', array('%title' => $value['title'])),
278 'help' => t('In the form of WW (01 - 53).'),
280 'field' => $value['name'],
281 'handler' => 'views_handler_argument_civicrm_week',
287 * Add Custom Fields to $data array
290 * Array of fields passed to hook_views_data()
292 * @param $entity_type
293 * String CivicRM entity Type ie "Contact"
296 * Integer Id of the Custom Field Group
299 * (optional) Integer Id of the Sub-Type, ie Contact Sub Type
302 * Array with the new custom field appended
304 function civicrm_views_custom_data_cache(&$data, $entity_type, $group_id, $sub_type) {
305 // From http://forum.civicrm.org/index.php/topic,17658.msg73901.html#msg73901, CRM-7860.
306 $tree = CRM_Core_BAO_CustomGroup::getTree($entity_type, CRM_Core_DAO::$_nullObject, NULL, $group_id, $sub_type, NULL);
308 $join_table = civicrm_views_get_join_table($entity_type);
309 foreach ($tree as $groupkey => $current_group) {
310 // Ignore 'info' key as it is not a real field.
311 if ($groupkey == 'info') {
315 // Provide custom table data, including group title and implicit table join.
316 $data[$current_group['table_name']]['table'] = array(
317 'group' => t('CiviCRM Custom: !title', array('!title' => $current_group['title'])),
319 $join_table => array(
320 'left_field' => 'id',
321 'field' => 'entity_id',
326 // Provide implicit joins in the other direction.
327 // Skip 'contribution', activity' and 'event' as these are not directly related to users.
328 if ($join_table != 'civicrm_event' && $join_table != 'civicrm_activity' && $join_table != 'civicrm_contribution') {
329 // Expose custom data to users view.
330 $data[$current_group['table_name']]['table']['join']['users'] = array(
331 'left_table' => $join_table,
332 'left_field' => 'id',
333 'field' => 'entity_id',
336 // Expose custom data to contact view.
337 if ($join_table != 'civicrm_contact') {
338 $data[$current_group['table_name']]['table']['join']['civicrm_contact'] = array(
339 'left_table' => $join_table,
340 'left_field' => 'id',
341 'field' => 'entity_id',
346 foreach ($current_group['fields'] as $key => $current_field) {
347 //Create the Views Field
348 $option_group_id = isset($current_field['option_group_id']) ? $current_field['option_group_id'] : NULL;
349 $data[$current_group['table_name']][$current_field['column_name']] = array(
350 'title' => $current_field['label'],
351 'help' => empty($current_field['help_post']) ? t('Custom Data Field') : $current_field['help_post'],
352 'field' => civicrm_views_get_field($current_field['data_type'], $current_field['html_type']),
353 'argument' => civicrm_views_get_argument($current_field['data_type']),
354 'filter' => civicrm_views_get_filter($current_field['data_type'], $current_field['html_type'], $option_group_id),
355 'sort' => civicrm_views_get_sort($current_field['data_type']),
356 'relationship' => civicrm_views_get_relationship($current_field['data_type']),
359 //For date fields add in 6 arguments
360 if ($current_field['data_type'] == 'Date') {
361 //@TODO Still need to get the field under it's respecitve group, I may e able to set the civicrm_views_add_date_arguments() function with a group variable and default it to null
363 $value['title'] = $current_field['label'];
364 $value['name'] = $current_field['column_name'];
365 civicrm_views_add_date_arguments($data[$current_group['table_name']], $value);
373 * Return the implicit join table for a custom group table based on its entity type.
375 function civicrm_views_get_join_table($entity_type) {
376 switch ($entity_type) {
381 return 'civicrm_contact';
383 return 'civicrm_group';
385 return 'civicrm_address';
387 return 'civicrm_event';
389 return 'civicrm_participant';
391 return 'civicrm_contribution';
393 return 'civicrm_activity';
395 return 'civicrm_relationship';
397 return 'civicrm_membership';
399 return 'civicrm_grant';
402 return 'civicrm_campaign';
404 return 'civicrm_case';
411 * Acquire the proper field handler by checking against the field's data_type as defined by CRM_Utils_Type.
414 * A String containing the field data type
416 * A String containing the field html type
419 * An array containing the handler name and any extra settings
421 function civicrm_views_get_field($data_type, $html_type = NULL) {
422 // Relying on html types as opposed to data types seems like a code smell.
423 // Would love to be able to remove this logic.
424 $customHTMLTypes = array(
425 'Select', 'Multi-Select', 'AdvMulti-Select', 'Radio', 'CheckBox',
426 'Select State/Province', 'Select Country', 'Multi-Select Country',
427 'Multi-Select State/Province', 'Autocomplete-Select',
429 if (in_array($html_type, $customHTMLTypes)) {
431 'handler' => 'civicrm_handler_field_custom',
432 'click sortable' => TRUE,
435 if ($html_type == 'File') {
437 'handler' => 'civicrm_handler_field_custom_file',
438 'click sortable' => TRUE,
442 switch ($data_type) {
446 'handler' => 'civicrm_handler_field_markup',
447 'click sortable' => TRUE,
451 'handler' => 'views_handler_field_numeric',
452 'click sortable' => TRUE,
457 'handler' => 'civicrm_handler_field_custom',
458 'click sortable' => TRUE,
462 'handler' => 'civicrm_handler_field_datetime',
463 'click sortable' => TRUE,
467 'handler' => 'views_handler_field_boolean',
468 'click sortable' => TRUE,
470 case "StateProvince":
472 'handler' => 'civicrm_handler_field_state',
473 'click sortable' => TRUE,
477 'handler' => 'civicrm_handler_field_country',
478 'click sortable' => TRUE,
482 'handler' => 'civicrm_handler_field_pseudo_constant',
483 'click sortable' => TRUE,
484 'pseudo class' => 'CRM_Core_PseudoConstant',
485 'pseudo method' => 'county',
489 'views_handler_field',
490 'click sortable' => TRUE,
496 * Acquire the proper argument handler by checking against the field's data_type as defined by CRM_Utils_Type.
499 * A String containing the field type
502 * An array containing the handler name and any extra settings
504 function civicrm_views_get_argument($type) {
509 'handler' => 'views_handler_argument',
514 'handler' => 'views_handler_argument_numeric',
518 'handler' => 'views_handler_argument_date',
522 'handler' => 'views_handler_argument',
524 case "StateProvince":
526 'handler' => 'views_handler_argument',
530 'handler' => 'views_handler_argument',
534 'handler' => 'views_handler_argument',
538 'handler' => 'views_handler_argument',
544 * Acquire the proper filter handler by checking against the field's data_type as defined by CRM_Utils_Type.
547 * A String containing the field data type
549 * A string containing the field html type
552 * An array containing the handler name and any extra settings
554 function civicrm_views_get_filter($data_type, $html_type = NULL, $option_group_id = NULL) {
555 // Relying on html types as opposed to data types seems like a code smell.
556 // Would love to be able to remove this logic.
557 $customMultiValueHTMLTypes = array(
558 'Multi-Select', 'AdvMulti-Select', 'CheckBox', 'Multi-Select Country',
559 'Multi-Select State/Province',
561 if ($html_type == 'Multi-Select Country') {
563 'handler' => 'civicrm_handler_filter_country_multi',
566 elseif ($html_type == 'Multi-Select State/Province') {
568 'handler' => 'civicrm_handler_filter_state_multi',
571 elseif (!empty($option_group_id) && in_array($html_type, $customMultiValueHTMLTypes)) {
573 'handler' => 'civicrm_handler_filter_custom_option',
576 elseif (!empty($option_group_id)) {
578 'handler' => 'civicrm_handler_filter_custom_single_option',
582 switch ($data_type) {
586 'handler' => 'views_handler_filter_string',
587 'allow empty' => TRUE,
593 'handler' => 'views_handler_filter_numeric',
594 'allow empty' => TRUE,
598 return array('handler' => 'civicrm_handler_filter_datetime');
601 return array('handler' => 'views_handler_filter_boolean_operator');
603 case "StateProvince":
605 'handler' => 'civicrm_handler_filter_pseudo_constant',
606 'pseudo class' => 'CRM_Core_PseudoConstant',
607 'pseudo method' => 'stateProvince',
608 'allow empty' => TRUE,
613 'handler' => 'civicrm_handler_filter_pseudo_constant',
614 'pseudo class' => 'CRM_Core_PseudoConstant',
615 'pseudo method' => 'country',
616 'allow empty' => TRUE,
621 'handler' => 'civicrm_handler_filter_pseudo_constant',
622 'pseudo class' => 'CRM_Core_PseudoConstant',
623 'pseudo method' => 'county',
624 'allow empty' => TRUE,
629 'handler' => 'views_handler_filter_string',
630 'allow empty' => TRUE,
636 * Acquire the proper sort handler by checking against the field's data_type as defined by CRM_Utils_Type.
639 * A String containing the field type
642 * An array containing the handler name and any extra settings
644 function civicrm_views_get_sort($type) {
651 'handler' => 'views_handler_sort',
655 'handler' => 'views_handler_sort_date',
659 'handler' => 'views_handler_sort',
661 case "StateProvince":
663 'handler' => 'views_handler_sort',
667 'handler' => 'views_handler_sort',
671 'handler' => 'views_handler_sort',
675 'handler' => 'views_handler_sort',
681 * Acquire the proper relationship handler by checking against the field's data_type as defined by CRM_Utils_Type.
684 * A String containing the field type
687 * An array containing the handler name and any extra settings
689 function civicrm_views_get_relationship($type) {
691 case "ContactReference":
693 'handler' => 'views_handler_relationship',
694 'base' => 'civicrm_contact',
695 'base field' => 'id',
703 * Implements hook_date_views_fields().
705 function civicrm_date_views_fields($field) {
707 // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
708 'sql_type' => DATE_DATETIME,
709 // Timezone handling options: 'none', 'site', 'date', 'utc'.
710 'tz_handling' => 'none',
711 // Needed only for dates that use 'date' tz_handling.
712 'timezone_field' => '',
713 // Needed only for dates that use 'date' tz_handling.
714 'offset_field' => '',
715 // Array of "table.field" values for related fields that should be
716 // loaded automatically in the Views SQL.
717 'related_fields' => array(),
718 // Granularity of this date field's db data.
719 'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
723 case 'civicrm_event.start_date':
724 case 'civicrm_event.end_date':
725 case 'civicrm_event.registration_start_date':
726 case 'civicrm_event.registration_end_date':
727 case 'civicrm_mailing_job.scheduled_date':
728 case 'civicrm_mailing_job.start_date':
729 case 'civicrm_mailing_job.end_date':
730 case 'civicrm_activity.activity_date_time':
731 case 'civicrm_campaign.start_date':
732 case 'civicrm_campaign.end_date':
733 case 'civicrm_case.start_date':
734 case 'civicrm_case.end_date':
740 * Implements hook_date_api_tables().
742 function civicrm_date_views_tables() {
744 'civicrm_mailing_job',
753 * Implements hook_views_plugins().
755 function civicrm_views_plugins() {
758 // This just tells us that the themes are elsewhere
759 $data['module'] = 'civicrm';
761 // Default argument to pull CiviCRM IDs from the URL
762 $data['argument default']['civicrm_id'] = array(
763 'title' => t('CiviCRM ID from URL'),
764 'handler' => 'civicrm_plugin_argument_default_civicrm_id'
767 // Calendar module integration
768 if (module_exists('calendar')) {
769 $civicrm_module_path = drupal_get_path('module', 'civicrm');
770 $data['row'] = array(
771 'civicrm_event_calendar' => array(
772 'title' => t('Calendar Items'),
773 'help' => t('Displays each selected event as a Calendar item.'),
774 'handler' => 'calendar_plugin_row_civicrm',
775 'path' => "$civicrm_module_path/modules/views/plugins",
776 'base' => array('civicrm_event'),
777 'uses options' => TRUE,
778 'uses fields' => TRUE,
780 'dao class' => 'CRM_Event_DAO_Event',
781 'title field' => 'title',
783 'civicrm_activity_calendar' => array(
784 'title' => t('Calendar Items'),
785 'help' => t('Displays each selected activity as a Calendar item.'),
786 'handler' => 'calendar_plugin_row_civicrm',
787 'path' => "$civicrm_module_path/modules/views/plugins",
788 'base' => array('civicrm_activity'),
789 'uses options' => TRUE,
790 'uses fields' => TRUE,
792 'dao class' => 'CRM_Activity_DAO_Activity',
793 'title field' => 'subject',
795 'civicrm_case_calendar' => array(
796 'title' => t('Case Items'),
797 'help' => t('Displays each selected case as a Calendar item.'),
798 'handler' => 'calendar_plugin_row_civicrm',
799 'path' => "$civicrm_module_path/modules/views/plugins",
800 'base' => array('civicrm_case'),
801 'uses options' => TRUE,
802 'uses fields' => TRUE,
804 'dao class' => 'CRM_Case_DAO_Case',
805 'title field' => 'subject',
807 'civicrm_mail_calendar' => array(
808 'title' => t('Calendar Items'),
809 'help' => t('Displays each selected mailing as a Calendar item.'),
810 'handler' => 'calendar_plugin_row_civicrm',
811 'path' => "$civicrm_module_path/modules/views/plugins",
812 'base' => array('civicrm_mail'),
813 'uses options' => TRUE,
814 'uses fields' => TRUE,
816 'dao class' => 'CRM_Mailing_DAO_MailingJob',
817 // @TODO come up with a better title field
818 'title field' => 'mailing_id',
820 'civicrm_campaign_calendar' => array(
821 'title' => t('Calendar Items'),
822 'help' => t('Displays each selected campaign as a Calendar item.'),
823 'handler' => 'calendar_plugin_row_civicrm',
824 'path' => "$civicrm_module_path/modules/views/plugins",
825 'base' => array('civicrm_campaign'),
826 'uses options' => TRUE,
827 'uses fields' => TRUE,
829 'dao class' => 'CRM_Campaign_DAO_Campaign',
830 'title field' => 'title',