diff --git a/CRM/Campaign/KPI.php b/CRM/Campaign/KPI.php index 2a82198..48dde5c 100644 --- a/CRM/Campaign/KPI.php +++ b/CRM/Campaign/KPI.php @@ -14,6 +14,8 @@ | written permission from the original author(s). | +--------------------------------------------------------*/ +require_once('CRM/CampaignTree/Tree.php'); + class CRM_Campaign_KPI { /** diff --git a/CRM/CampaignTree/BAO/Campaign.php b/CRM/CampaignTree/BAO/Campaign.php new file mode 100644 index 0000000..8d7196d --- /dev/null +++ b/CRM/CampaignTree/BAO/Campaign.php @@ -0,0 +1,555 @@ + $value) { + $campaignList[$id]['id'] = $value['id']; + $campaignList[$id]['name'] = $value['title']; + + // append parent names if in search mode + /*if (empty($params['parent_id']) && !empty($value['parents'])) { + $campaignIds = explode(',', $value['parents']); + $title = array(); + foreach ($campaignIds as $cId) { + $title[] = self::getCampaign($cId)['title']; + } + $campaignList[$id]['name'] .= '
' . ts('Child of') . ': ' . implode(', ', $title) . '
'; + $value['class'] = array_diff($value['class'], array('crm-row-parent')); + }*/ + $value['class'][] = 'crm-entity'; + $campaignList[$id]['class'] = $value['id'] . ',' . implode(' ', $value['class']); + + $campaignList[$id]['description'] = CRM_Utils_Array::value('description', $value); + if (!empty($value['type'])) { + $campaignList[$id]['type'] = $value['type']; + } else { + $campaignList[$id]['type'] = ''; + } + $campaignList[$id]['start_date'] = $value['start_date']; + if ($campaignList[$id]['start_date'] == '') { + // Makes we don't display "null" on datatable + $campaignList[$id]['start_date'] = ''; + } + $campaignList[$id]['end_date'] = $value['end_date']; + if ($campaignList[$id]['end_date'] == '') { + // Makes we don't display "null" on datatable + $campaignList[$id]['end_date'] = ''; + } + $campaignList[$id]['status'] = $value['status']; + $campaignList[$id]['links'] = $value['action']; + $campaignList[$id]['created_by'] = CRM_Utils_Array::value('created_by', $value); + if ((boolean)$value['is_active']) { + $campaignList[$id]['is_active'] = 'Yes'; + } + else { + $campaignList[$id]['is_active'] = 'No'; + } + + $campaignList[$id]['is_parent'] = $value['is_parent']; + $campaignList[$id]['external_id'] = CRM_Utils_Array::value('external_id', $value); + } + return $campaignList; + } + } + + public static function getCampaignList(&$params) + { + $values = array( + 'hasAccessCampaign' => FALSE, + 'isCampaignEnabled' => FALSE, + ); + //do check for component. + $values['isCampaignEnabled'] = $isValid = CRM_Campaign_BAO_Campaign::isCampaignEnable(); + //do check for permissions. + $values['hasAccessCampaign'] = $isValid = CRM_Campaign_BAO_Campaign::accessCampaign(); + if (!$values['isCampaignEnabled'] || !$values['hasAccessCampaign']) { + return $values; + } + + $config = CRM_Core_Config::singleton(); // Need this for dateformat + + $whereClause = self::whereClause($params, FALSE); + + if (!empty($params['rowCount']) && + $params['rowCount'] > 0 + ) { + $limit = " LIMIT {$params['offset']}, {$params['rowCount']} "; + } + + $orderBy = ' ORDER BY camp.title asc'; + if (!empty($params['sort'])) { + $orderBy = ' ORDER BY ' . CRM_Utils_Type::escape($params['sort'], 'String'); + + // CRM-16905 - Sort by count cannot be done with sql + if (strpos($params['sort'], 'count') === 0) { + $orderBy = $limit = ''; + } + } + + $query = " + SELECT camp.*, createdBy.sort_name as created_by + FROM civicrm_campaign camp + LEFT JOIN civicrm_contact createdBy + ON createdBy.id = camp.created_id + WHERE $whereClause + {$orderBy} + {$limit}"; + + $object = CRM_Core_DAO::executeQuery($query, $params, TRUE, 'CRM_Campaign_DAO_Campaign'); + //skip total if we are making call to show only children + if (empty($params['parent_id'])) { + // add total + $params['total'] = self::getCampaignCount($params); + } + + $campaignPermissions = array(CRM_Core_Permission::VIEW); + if (CRM_Core_Permission::check(array('administer CiviCampaign', 'manage campaign'))) { + $campaignPermissions[] = CRM_Core_Permission::EDIT; + $campaignPermissions[] = CRM_Core_Permission::DELETE; + } + + $campaignTypes = CRM_Core_OptionGroup::values('campaign_type'); + $campaignStatus = CRM_Core_OptionGroup::values('campaign_status'); + + $count = 0; + while ($object->fetch()) { + $values[$object->id] = array( + 'class' => array(), + 'count' => '0', + ); + CRM_Core_DAO::storeValues($object, $values[$object->id]); + + if (in_array(CRM_Core_Permission::EDIT, $campaignPermissions)) { + //$values[$object->id]['title'] = '' . $values[$object->id]['title'] . ''; + $view_url = CRM_Utils_System::url("civicrm/a/#/campaign/{$object->id}/view"); + $values[$object->id]['title'] = "".$values[$object->id]['title'].''; + $values[$object->id]['description'] = '
' . $values[$object->id]['description'] . '
'; + } + + $links = self::actionLinks($object->id); + $action = array_sum(array_keys($links)); + + if (array_key_exists('is_active', $object)) { + if ($object->is_active) { + $action -= CRM_Core_Action::ENABLE; + } else { + $values[$object->id]['class'][] = 'disabled'; + $action -= CRM_Core_Action::VIEW; + $action -= CRM_Core_Action::DISABLE; + } + } + + $action = $action & CRM_Core_Action::mask($campaignPermissions); + + if ($object->campaign_type_id) { + $values[$object->id]['type'] = $campaignTypes[$object->campaign_type_id]; + } + + // Created_by + if ($object->created_id) { + $contactUrl = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$object->created_id}"); + $values[$object->id]['created_by'] = "{$object->created_by}"; + } + if ($object->status_id) { + $values[$object->id]['status'] = $campaignStatus[$object->status_id]; + } + + // start_date / end_date + foreach (array('start_date', 'end_date') as $date) { + if ($object->$date) { + $values[$object->id][$date] = CRM_Utils_Date::customFormat($object->$date, $config->dateformatFull); + } + } + + $values[$object->id]['action'] = CRM_Core_Action::formLink($links, + $action, + array( + 'id' => $object->id, + ), + ts('more'), + FALSE, + 'campaign.selector.row', + 'Campaign', + $object->id + ); + + // If group has children, add class for link to view children + $values[$object->id]['is_parent'] = FALSE; + // A root campaign can be a parent but not a child + if (self::isRootCampaign($object->id)) { + $values[$object->id]['class'][] = "crm-campaign-root"; + $values[$object->id]['is_parent'] = TRUE; + } + // A parent campaign can be a parent and a child + if (self::isParentCampaign($object->id)) { + $values[$object->id]['class'][] = "crm-campaign-parent"; + $values[$object->id]['is_parent'] = TRUE; + } + // If group is a child, add child class (it can also be a parent) + if (array_key_exists('parent_id', $values[$object->id])) { + $values[$object->id]['class'][] = "crm-campaign-child"; + } + + if ($object->external_identifier) { + $values[$object->id]['external_id'] = $object->external_identifier; + } + } + + // Clear caches for next run + $_cache_campaign_all_parent_ids = NULL; + + return $values; + } + + /** + * Get all parent campaign IDs + * + * @return array + */ + public static function getCampaignAllParentIds() + { + // get all parent campaigns + $query = 'SELECT id FROM civicrm_campaign;'; + + $queryCount = 'SELECT count(parent_id) + FROM civicrm_campaign + WHERE parent_id = %1'; + + $parents = array(); + + $campaign = CRM_Core_DAO::executeQuery($query); + while ($campaign->fetch()) { + $count = CRM_Core_DAO::singleValueQuery($queryCount, array(1 => array($campaign->id, 'String'))); + if ($count > 0) { + $parents[] = $campaign->id; + } + } + + return $parents; + } + + public static function isParentCampaign($id) + { + global $_cache_campaign_all_parent_ids; + // Get list of parent IDs if not already cached + if ($_cache_campaign_all_parent_ids === NULL) { + $_cache_campaign_all_parent_ids = self::getCampaignAllParentIds(); + } + + foreach ($_cache_campaign_all_parent_ids as $p) { + if ($p == $id) { + return true; + } + } + return false; + } + + /** + * A campaign is a root campaign if it has children and parent_id is NOT set + * @param $id + * @param $parents + * + * @return boolean + */ + public static function isRootCampaign($id) + { + $parent_id = CRM_Core_DAO::singleValueQuery('SELECT parent_id FROM `civicrm_campaign` WHERE id='.$id); + if ($parent_id) { + return false; + } + if (self::isParentCampaign($id)) { + return true; + } + return false; + } + + /** + * @param array $params + * + * @return NULL|string + */ + public static function getCampaignCount(&$params) { + $whereClause = self::whereClause($params, FALSE); + $query = "SELECT COUNT(*) FROM civicrm_campaign camp"; + + if (!empty($params['created_by'])) { + $query .= " INNER JOIN civicrm_contact createdBy + ON createdBy.id = camp.created_id"; + } + $query .= " WHERE {$whereClause}"; + return CRM_Core_DAO::singleValueQuery($query, $params); + } + + /** + * Generate permissioned where clause for group search. + * @param array $params + * @param bool $sortBy + * @param bool $excludeHidden + * + * @return string + */ + public static function whereClause(&$params, $sortBy = TRUE, $excludeHidden = TRUE) { + $title = CRM_Utils_Array::value('title', $params); + if ($title) { + $clauses[] = "camp.title LIKE %1"; + if (strpos($title, '%') !== FALSE) { + $params[1] = array($title, 'String', FALSE); + } + else { + $params[1] = array($title, 'String', TRUE); + } + } + + $description = CRM_Utils_Array::value('description', $params); + if ($description) { + $clauses[] = "camp.description LIKE %2"; + if (strpos($description, '%') !== FALSE) { + $params[2] = array($description, 'String', FALSE); + } + else { + $params[2] = array($description, 'String', TRUE); + } + } + + $start_date = CRM_Utils_Date::processDate($params['start_date']); + if ($start_date) { + $clauses[] = "( camp.start_date >= %3 OR camp.start_date IS NULL )"; + $params[3] = array($start_date, 'String'); + } + + $end_date = CRM_Utils_Date::processDate($params['end_date'],'235959'); + if ($end_date) { + $clauses[] = "( camp.end_date <= %4 OR camp.end_date IS NULL )"; + $params[4] = array($end_date, 'String'); + } + + $campaign_type = CRM_Utils_Array::value('type', $params); + if ($campaign_type) { + if (is_array($campaign_type)) { + $campaign_type = implode(' , ', $campaign_type); + } + $clauses[] = "( camp.campaign_type_id IN ( {$campaign_type} ) )"; + } + + $campaign_status = CRM_Utils_Array::value('status', $params); + if ($campaign_status) { + if (is_array($campaign_status)) { + $campaign_status = implode(' , ', $campaign_status); + } + $clauses[] = "( camp.status_id IN ( {$campaign_status} ) )"; + } + + $external_id = CRM_Utils_Array::value('external_id', $params); + if ($external_id) { + $clauses[] = "camp.external_identifier LIKE %5"; + if (strpos($external_id, '%') !== FALSE) { + $params[5] = array($external_id, 'String', FALSE); + } + else { + $params[5] = array($external_id, 'String', TRUE); + } + } + + if (!empty($params['status_id'])) { + $statusId = $params['status_id']; + if (is_array($params['status_id'])) { + $statusId = implode(' , ', $params['status_id']); + } + $where[] = "( campaign.status_id IN ( {$statusId} ) )"; + } + + $groupType = CRM_Utils_Array::value('group_type', $params); + if ($groupType) { + $types = explode(',', $groupType); + if (!empty($types)) { + $clauses[] = 'groups.group_type LIKE %6'; + $typeString = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, $types) . CRM_Core_DAO::VALUE_SEPARATOR; + $params[6] = array($typeString, 'String', TRUE); + } + } + + $showActive = CRM_Utils_Array::value('showActive', $params); + if ($showActive) { + switch ($showActive) { + case 1: + $clauses[] = 'camp.is_active = 1'; + $params[7] = array($showActive, 'Integer'); + break; + + case 2: + $clauses[] = 'camp.is_active = 0'; + $params[7] = array($showActive, 'Integer'); + break; + + case 3: + $clauses[] = '(camp.is_active = 0 OR camp.is_active = 1 )'; + break; + } + } + + $rootOnly = CRM_Utils_Array::value('rootOnly', $params); + if ($rootOnly) { + $clauses[] = "(camp.parent_id IS NULL)"; + } + else { + // this is a bitmask: 1=root; 2=parents; 4=children, 8=other + $show = CRM_Utils_Array::value('show', $params); + if ($show & 1) { + // show root campaigns (no parent_id AND has children) + $showClauses[] = "((camp.parent_id IS NULL) AND EXISTS (SELECT parent_id FROM `civicrm_campaign` camp2 WHERE camp2.parent_id = camp.id))"; + } + if ($show & 2) { + // show parent campaigns (parent_id set AND has children) + $showClauses[] = "((camp.parent_id IS NOT NULL) AND EXISTS (SELECT parent_id FROM `civicrm_campaign` camp2 WHERE camp2.parent_id = camp.id))"; + } + if ($show & 4) { + // show child campaigns (parent_id set AND has no children) + $showClauses[] = "((camp.parent_id IS NOT NULL) AND NOT EXISTS (SELECT parent_id FROM `civicrm_campaign` camp2 WHERE camp2.parent_id = camp.id))"; + } + if ($show & 8) { + // show other campaigns (parent_id NOT set AND has no children) + $showClauses[] = "((camp.parent_id IS NULL) AND NOT EXISTS (SELECT parent_id FROM `civicrm_campaign` camp2 WHERE camp2.parent_id = camp.id))"; + } + + if (isset($showClauses)) { + $clauses[] = '(' . implode(' OR ', $showClauses) . ')'; + } + } + + // only show child groups of a specific parent group + $parent_id = CRM_Utils_Array::value('parent_id', $params); + if ($parent_id) { + $clauses[] = 'camp.id IN (SELECT id FROM civicrm_campaign WHERE parent_id = %7)'; + $params[7] = array($parent_id, 'Integer'); + } + + if ($createdBy = CRM_Utils_Array::value('created_by', $params)) { + $clauses[] = "createdBy.sort_name LIKE %8"; + if (strpos($createdBy, '%') !== FALSE) { + $params[8] = array($createdBy, 'String', FALSE); + } + else { + $params[8] = array($createdBy, 'String', TRUE); + } + } + + if (empty($clauses)) { + $clauses[] = '(camp.is_active = 0 OR camp.is_active = 1 )'; + } + //FIXME Do we need a permission clause? + + return implode(' AND ', $clauses); + } + + /** + * Define action links + * + * @param $objectId int id of campaign + * + * @return array + * array of action links + */ + public function actionLinks($objectId) { + $links = array( + CRM_Core_Action::VIEW => array( + 'name' => ts('View'), + 'url' => CRM_Utils_System::url('civicrm/a/#/campaign/'. $objectId .'/view'), + 'qs' => '', + 'class' => 'no-popup', + 'title' => ts('View Campaign'), + ), + CRM_Core_Action::UPDATE => array( + 'name' => ts('Edit'), + 'url' => CRM_Utils_System::url('civicrm/campaign/add'), + 'qs' => 'reset=1&action=update&id=%%id%%', + 'title' => ts('Update Campaign'), + ), + CRM_Core_Action::DISABLE => array( + 'name' => ts('Disable'), + 'title' => ts('Disable Campaign'), + 'ref' => 'crm-enable-disable', + ), + CRM_Core_Action::ENABLE => array( + 'name' => ts('Enable'), + 'title' => ts('Enable Campaign'), + 'ref' => 'crm-enable-disable', + ), + CRM_Core_Action::DELETE => array( + 'name' => ts('Delete'), + 'url' => CRM_Utils_System::url('civicrm/campaign/add'), + 'qs' => 'action=delete&reset=1&id=%%id%%', + 'title' => ts('Delete Campaign'), + ), + ); + return $links; + } +} diff --git a/CRM/CampaignTree/Form/Search.php b/CRM/CampaignTree/Form/Search.php new file mode 100644 index 0000000..883b761 --- /dev/null +++ b/CRM/CampaignTree/Form/Search.php @@ -0,0 +1,112 @@ +addPermissions('manage campaigns'); + } + + public function buildQuickForm() { + $this->add('text', 'title', ts('Name'), + CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Campaign', 'title') + ); + + $this->add('text', 'description', ts('Description'), + CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Campaign', 'description') + ); + + $this->add('text', 'created_by', ts('Created By'), + CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Group', 'title') + ); + + $this->add('text', 'external_id', ts('External ID'), + CRM_Core_DAO::getAttribute('CRM_Campaign_DAO_Campaign', 'external_id') + ); + + //campaign start date. + $this->addDate('start_date', ts('Start Date'), FALSE, array('formatType' => 'searchDate')); + + //campaign end date. + $this->addDate('end_date', ts('End Date'), FALSE, array('formatType' => 'searchDate', 'name' => 'end_date')); + + $campaignShow = array(ts('Root') => 1, ts('Parent') => 2, ts('Child') => 3, ts('Other') => 4); + $this->addCheckBox('show', + ts('Show Campaigns:'), + $campaignShow, + NULL, NULL, NULL, NULL, '   ' + ); + + //Active + $campaignActive = array(ts('Active') => 1, ts('Disabled') => 2); + $this->addCheckBox('active', + ts('Show Campaigns:'), + $campaignActive, + NULL, NULL, NULL, NULL, '   ' + ); + + //campaign type. + $campaignTypes = CRM_Campaign_PseudoConstant::campaignType(); + $this->add('select', 'type_id', ts('Campaign Type'), + array( + '' => ts('- any -'), + ) + $campaignTypes + ); + + $this->set('campaignTypes', $campaignTypes); + $this->assign('campaignTypes', json_encode($campaignTypes)); + + //campaign status + $campaignStatus = CRM_Campaign_PseudoConstant::campaignStatus(); + $this->addElement('select', 'status_id', ts('Campaign Status'), + array( + '' => ts('- any -'), + ) + $campaignStatus + ); + $this->set('campaignStatus', $campaignStatus); + $this->assign('campaignStatus', json_encode($campaignStatus)); + + $this->addButtons(array( + array( + 'type' => 'refresh', + 'name' => ts('Search'), + 'isDefault' => TRUE, + ), + array( + 'type' => 'cancel', + 'name' => ts('Reset'), + 'isDefault' => FALSE, + ), + )); + + parent::buildQuickForm(); + $this->assign('suppressForm', TRUE); + } + + public function postProcess() { + $params = $this->controller->exportValues($this->_name); + $parent = $this->controller->getParent(); + if (!empty($params)) { + $fields = array('title', 'created_by', 'campaign_type', 'visibility', 'active_status', 'inactive_status'); + foreach ($fields as $field) { + if (isset($params[$field]) && + !CRM_Utils_System::isNull($params[$field]) + ) { + $parent->set($field, $params[$field]); + } + else { + $parent->set($field, NULL); + } + } + } + parent::postProcess(); + } + +} + diff --git a/CRM/CampaignTree/Page/AJAX.php b/CRM/CampaignTree/Page/AJAX.php new file mode 100644 index 0000000..1856354 --- /dev/null +++ b/CRM/CampaignTree/Page/AJAX.php @@ -0,0 +1,121 @@ + 'camp.title', + 1 => 'camp.description', + 2 => 'camp.start_date', + 3 => 'camp.end_date' + ); + $sEcho = isset($_REQUEST['sEcho']) ? CRM_Utils_Type::escape($_REQUEST['sEcho'], 'Integer') : 1; + $offset = isset($_REQUEST['iDisplayStart']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayStart'], 'Integer') : 0; + $rowCount = isset($_REQUEST['iDisplayLength']) ? CRM_Utils_Type::escape($_REQUEST['iDisplayLength'], 'Integer') : 25; + $sort = isset($_REQUEST['iSortCol_0']) ? CRM_Utils_Array::value(CRM_Utils_Type::escape($_REQUEST['iSortCol_0'], 'Integer'), $sortMapper) : NULL; + $sortOrder = isset($_REQUEST['sSortDir_0']) ? CRM_Utils_Type::escape($_REQUEST['sSortDir_0'], 'MysqlOrderByDirection') : 'asc'; + + if ($sort && $sortOrder) { + $params['sortBy'] = $sort . ' ' . $sortOrder; + } + + // Search mode - some search parameters it makes sense to use tree view, some it does not + // When displayed in flatSearch there may be duplicates in the list + $params['rootOnly'] = 1; + + $flatSearch = array( + 'title', // name + 'description', + 'start_date', + 'end_date', + 'show', + 'showActive', + 'type', + 'status', + 'created_by', + 'external_id', + ); + foreach ($flatSearch as $p) { + if (!empty($params[$p])) { + $params['rootOnly'] = 0; + } + } + + $params['page'] = ($offset / $rowCount) + 1; + $params['rp'] = $rowCount; + + // get campaign list + $campaigns = CRM_CampaignTree_BAO_Campaign::getCampaignListSelector($params); + $iFilteredTotal = $iTotal = $params['total']; + + $selectorElements = array( + 'name', + 'description', + 'start_date', + 'end_date', + 'type', + 'status', + 'created_by', + 'external_id', + 'links', + 'is_active', + 'class', // This one MUST always be at the end, as the js code in search.tpl looks for the class in the last element + ); + + header('Content-Type: application/json'); + echo CRM_Utils_JSON::encodeDataTableSelector($campaigns, $sEcho, $iTotal, $iFilteredTotal, $selectorElements); + CRM_Utils_System::civiExit(); + } + } +} diff --git a/CRM/CampaignTree/Page/Dashboard.php b/CRM/CampaignTree/Page/Dashboard.php new file mode 100644 index 0000000..93afebc --- /dev/null +++ b/CRM/CampaignTree/Page/Dashboard.php @@ -0,0 +1,178 @@ +browse(); + + parent::run(); + } + + public function userContext($mode = NULL) { + return 'civicrm/campaign/dashboard'; + } + + /** + * Return user context uri params. + * + * @param null $mode + * + * @return string + */ + public function userContextParams($mode = NULL) { + return 'reset=1&action=browse'; + } + + /** + * We need to do slightly different things for groups vs saved search groups, hence we + * reimplement browse from Page_Basic + * + * @param int $action + * + * @return void + */ + public function browse($action = NULL) { + $campaignPermission = CRM_Core_Permission::check('manage campaigns') ? CRM_Core_Permission::EDIT : CRM_Core_Permission::VIEW; + $this->assign('campaignPermission', $campaignPermission); + + $this->_tabs = array( + 'campaign' => ts('Campaigns'), + 'survey' => ts('Surveys'), + 'petition' => ts('Petitions'), + ); + + $subPageType = CRM_Utils_Request::retrieve('type', 'String', $this); + if ($subPageType) { + if (!isset($this->_tabs[$subPageType])) { + CRM_Utils_System::permissionDenied(); + } + //load the data in tabs. + $this->{'browse' . ucfirst($subPageType)}(); + $this->assign('subPageType', ucfirst($subPageType)); + } + else { + //build the tabs. + $this->buildTabs(); + } + $res = CRM_Core_Resources::singleton(); + $res->addScriptFile('civicrm', 'templates/CRM/common/TabHeader.js', 1, 'html-header'); + $res->addSetting(array( + 'tabSettings' => array( + 'active' => strtolower(CRM_Utils_Array::value('subPage', $_GET, 'campaign')), + ))); + $res->addVars('campaigntree', array( + 'baseUrl' => $res->getUrl('de.systopia.campaign'), + )); + } + + /** + * @return mixed + */ + public function browseCampaign() { + if (isset($this->action)) { + if ($this->_action & (CRM_Core_Action::ADD | + CRM_Core_Action::UPDATE | + CRM_Core_Action::DELETE + ) + ) { + return; + } + } + + // ensure valid javascript (these must have a value set) + $this->assign('searchParams', json_encode(NULL)); + $this->assign('campaignTypes', json_encode(NULL)); + $this->assign('campaignStatus', json_encode(NULL)); + + $this->assign('addCampaignUrl', CRM_Utils_System::url('civicrm/campaign/add', 'reset=1&action=add')); + $campaignCount = CRM_Campaign_BAO_Campaign::getCampaignCount(); + //don't load find interface when no campaigns in db. + if (!$campaignCount) { + $this->assign('hasCampaigns', FALSE); + return; + } + $this->assign('hasCampaigns', TRUE); + + //build the ajaxify campaign search and selector. + $controller = new CRM_Core_Controller_Simple('CRM_CampaignTree_Form_Search', ts('Search Campaigns'), CRM_Core_Action::ADD); + $controller->set('searchTab', 'campaign'); + $controller->setEmbedded(TRUE); + $controller->setParent($this); + $controller->process(); + return $controller->run(); + } + + // Copy of function from CRM/Campaign/Page/Dashboard + /** + * @return mixed + */ + public function browseSurvey() { + // ensure valid javascript - this must have a value set + $this->assign('searchParams', json_encode(NULL)); + $this->assign('surveyTypes', json_encode(NULL)); + $this->assign('surveyCampaigns', json_encode(NULL)); + + $this->assign('addSurveyUrl', CRM_Utils_System::url('civicrm/survey/add', 'reset=1&action=add')); + + $surveyCount = CRM_Campaign_BAO_Survey::getSurveyCount(); + //don't load find interface when no survey in db. + if (!$surveyCount) { + $this->assign('hasSurveys', FALSE); + return; + } + $this->assign('hasSurveys', TRUE); + + //build the ajaxify survey search and selector. + $controller = new CRM_Core_Controller_Simple('CRM_Campaign_Form_Search_Survey', ts('Search Survey')); + $controller->set('searchTab', 'survey'); + $controller->setEmbedded(TRUE); + $controller->process(); + return $controller->run(); + } + + // Copy of function from browsePetition + /** + * Browse petitions. + * + * @return mixed|null + */ + public function browsePetition() { + // Ensure valid javascript - this must have a value set + $this->assign('searchParams', json_encode(NULL)); + $this->assign('petitionCampaigns', json_encode(NULL)); + + $this->assign('addPetitionUrl', CRM_Utils_System::url('civicrm/petition/add', 'reset=1&action=add')); + + $petitionCount = CRM_Campaign_BAO_Petition::getPetitionCount(); + //don't load find interface when no petition in db. + if (!$petitionCount) { + $this->assign('hasPetitions', FALSE); + return NULL; + } + $this->assign('hasPetitions', TRUE); + + // Build the ajax petition search and selector. + $controller = new CRM_Core_Controller_Simple('CRM_Campaign_Form_Search_Petition', ts('Search Petition')); + $controller->set('searchTab', 'petition'); + $controller->setEmbedded(TRUE); + $controller->process(); + return $controller->run(); + } + + public function buildTabs() { + $allTabs = array(); + foreach ($this->_tabs as $name => $title) { + $allTabs[$name] = array( + 'title' => $title, + 'valid' => TRUE, + 'active' => TRUE, + 'link' => CRM_Utils_System::url('civicrm/campaign/dashboard', "reset=1&type=$name"), + ); + } + $allTabs['campaign']['class'] = 'livePage'; + $this->assign('tabHeader', $allTabs); + } +} diff --git a/CRM/Campaign/Tree.php b/CRM/CampaignTree/Tree.php similarity index 98% rename from CRM/Campaign/Tree.php rename to CRM/CampaignTree/Tree.php index cc4acfe..cec8bb2 100644 --- a/CRM/Campaign/Tree.php +++ b/CRM/CampaignTree/Tree.php @@ -31,11 +31,10 @@ class CRM_Campaign_Tree { * * * @param integer $id campaign id - * @param integet $depth maximum depth + * @param integer $depth maximum depth * * @return array */ - public static function getCampaignIds($id, $depth) { // get all sub campaigns of current id $query = " @@ -117,7 +116,7 @@ public static function is_parent($id, $parents) { * * * @param integer $id campaign id - * @param integet $depth maximum depth + * @param integer $depth maximum depth * * @return array */ @@ -198,7 +197,7 @@ public static function createTree(&$list, $parent){ * * * @param integer $id campaign id - * @param integet $parentid new parent id + * @param integer $parentid new parent id * * @return empty */ @@ -268,6 +267,4 @@ public static function cloneCampaign($node_id, $parent_id, $depth, $adjustments) } return $result["id"]; } - - } diff --git a/api/v3/CampaignTree.php b/api/v3/CampaignTree.php index a5d3f33..f6c6b67 100644 --- a/api/v3/CampaignTree.php +++ b/api/v3/CampaignTree.php @@ -27,6 +27,8 @@ * @return array */ +require_once('CRM/CampaignTree/Tree.php'); + function civicrm_api3_campaign_tree_getids($params) { return CRM_Campaign_Tree::getCampaignIds($params['id'], $params['depth']); } @@ -104,3 +106,92 @@ function _civicrm_api3_campaign_tree_clone_spec(&$params) { $params['id']['api.required'] = 1; $params['depth']['api.default'] = 999; } + + +function civicrm_api3_campaign_tree_getcustominfo($params) { + // Get the Custom Group ID for campaign_information + try { + $customGroupId = civicrm_api3('CustomGroup', 'getsingle', array( + 'return' => "id", + 'name' => "campaign_information", + )); + } + catch (Exception $e) { + CRM_Core_Error::debug_log_message("Cannot find id for 'campaign_information' custom field group!"); + return; + } + + // Get list of custom fields in group + $customGroupFields = civicrm_api3('CustomField', 'get', array( + 'custom_group_id' => $customGroupId['id'], + )); + + $customValueFields = array(); // Selector Array for CustomValue_get + $customValueData = array(); // Data array to collect fields for output + // Create the selector array and store some values for use later + $customValueFields['entity_id'] = $params['entity_id']; + foreach($customGroupFields['values'] as $id => $fields) { + $customValueFields['return.custom_'.$id] = 1; + // These values are used later to build the output array + $customValueData[$fields['id']]['name'] = $fields['name']; + $customValueData[$fields['id']]['label'] = $fields['label']; + $customValueData[$fields['id']]['data_type'] = $fields['data_type']; + $customValueData[$fields['id']]['html_type'] = $fields['html_type']; + } + + // Custom values + $customValues = civicrm_api3('CustomValue', 'get', $customValueFields); + if (!isset($customValues)) { return; } + + $customInfo = array(); // This is the output array + // Merge together information from the $customValues array and the $customValueData array + // to generate the $customInfo output array + foreach ($customValues['values'] as $id => $values) { + $key = strtolower($customValueData[$id]['name']); + if (!isset($values[0])) { return array(); } // We assume that customvalue has a single value '0'. + $value = $values[0]; + $customInfo[$key]['title'] = $customValueData[$id]['label']; + $customInfo[$key]['value'] = ''; // Default to empty string if not defined + // Get actual values for references + if (!empty($value)) { + switch ($customValueData[$id]['data_type']) { + case 'ContactReference': + // Return the contact name, not the ID + $contactName = civicrm_api3('Contact', 'getvalue', array( + 'return' => "display_name", + 'id' => $value, + )); + $customInfo[$key]['value'] = $contactName; + break; + case 'String': + if ($customValueData[$id]['html_type'] == 'Select') { + try { + // Return the label, not the OptionValue ID + $optionGroupId = civicrm_api3('OptionGroup', 'getsingle', array( + 'return' => "id", + 'title' => $customValueData[$id]['label'], + )); + $optionLabel = civicrm_api3('OptionValue', 'getsingle', array( + 'return' => "label", + 'option_group_id' => $optionGroupId['id'], + 'value' => $value, + )); + } catch (Exception $e) { + CRM_Core_Error::debug_log_message("Cannot find OptionGroup or OptionValue. " . print_r($e, true)); + } + $customInfo[$key]['value'] = $optionLabel['label']; + } else { + $customInfo[$key]['value'] = $value; + } + break; + default: + $customInfo[$key]['value'] = $value; + } + } + } + return $customInfo; +} + +function _civicrm_api3_campaign_tree_getcustominfo_spec(&$params) { + $params['entity_id']['api.required'] = 1; +} diff --git a/campaign.php b/campaign.php index 660c2ef..bae4e14 100644 --- a/campaign.php +++ b/campaign.php @@ -169,7 +169,7 @@ function campaign_civicrm_links( $op, $objectName, $objectId, &$links, &$mask, & 'name' => ts('View', array('domain' => 'de.systopia.campaign')), 'title' => ts('View Campaign', array('domain' => 'de.systopia.campaign')), 'class' => 'no-popup', - 'url' => 'a/#/campaign/'. $objectId .'/view', + 'url' => CRM_Utils_System::url("civicrm/a/#/campaign/{$objectId}/view"), ); array_unshift($links, $viewLink); @@ -272,5 +272,17 @@ function campaign_civicrm_alterAPIPermissions($entity, $action, &$params, &$perm $permissions['campaign_tree']['gettree'] = array('manage campaign'); $permissions['campaign_tree']['setnodeparent'] = array('manage campaign'); $permissions['campaign_tree']['clone'] = array('manage campaign'); + $permissions['campaign_tree']['getcustominfo'] = array('manage campaign'); +} + +/** + * Implements hook_coreResourceList + * + * @param array $list + * @param string $region + */ +function campaign_civicrm_coreResourceList(&$list, $region) { + Civi::resources() + ->addStyleFile('de.systopia.campaign', 'css/campaign.css', 0, $region); } diff --git a/css/campaign.css b/css/campaign.css index 11b1b25..4c2395d 100644 --- a/css/campaign.css +++ b/css/campaign.css @@ -156,3 +156,30 @@ linegraph .axis line { stroke-width: 1; shape-rendering: crispEdges; } + +/* alter display of parent and child groups in Manage Groups selector */ +/* from civicrm.css: #crm-container .crm-group-parent td.crm-group-name { */ +#crm-container td.crm-campaign-name { + padding-left: 20px; + text-indent: -20px; +} + +/* from civicrm.css: #crm-container .crm-group-child td.crm-group-name.level_2 { */ +#crm-container td.crm-campaign-name.level_2 { + padding-left: 40px; + text-indent: -20px; +} +/* from civicrm.css: #crm-container .crm-group-child td.crm-group-name.level_3 { */ +#crm-container td.crm-campaign-name.level_3 { + padding-left: 60px; + text-indent: -20px; +} +/* from civicrm.css: #crm-container .crm-group-name span.crm-editable-enabled { */ +#crm-container .crm-campaign-name span.crm-editable-enabled { + text-indent: 0; +} + +.campaign-icon { + max-width: 20px; + vertical-align: middle; +} \ No newline at end of file diff --git a/images/campaign-icon-child_20x20.png b/images/campaign-icon-child_20x20.png new file mode 100644 index 0000000..16fa01b Binary files /dev/null and b/images/campaign-icon-child_20x20.png differ diff --git a/images/campaign-icon-other_20x20.png b/images/campaign-icon-other_20x20.png new file mode 100644 index 0000000..22e96a4 Binary files /dev/null and b/images/campaign-icon-other_20x20.png differ diff --git a/images/campaign-icon-parent_20x20.png b/images/campaign-icon-parent_20x20.png new file mode 100644 index 0000000..b4432a4 Binary files /dev/null and b/images/campaign-icon-parent_20x20.png differ diff --git a/images/campaign-icon-root_20x20.png b/images/campaign-icon-root_20x20.png new file mode 100644 index 0000000..43593ce Binary files /dev/null and b/images/campaign-icon-root_20x20.png differ diff --git a/info.xml b/info.xml index 7bc8173..623b7a6 100644 --- a/info.xml +++ b/info.xml @@ -14,9 +14,9 @@ https://github.com/systopia/de.systopia.campaign/issues http://www.gnu.org/licenses/agpl-3.0.html - 2016-03-07 - 1.0.beta1 - beta + 2017-04-28 + 1.2.dev424 + dev 4.6 diff --git a/js/campaign.js b/js/campaign.js index 2459492..851d559 100644 --- a/js/campaign.js +++ b/js/campaign.js @@ -45,7 +45,10 @@ }, expenses: function($route, crmApi) { return crmApi('CampaignExpense', 'get', {campaign_id: $route.current.params.id}); - } + }, + customInfo: function($route, crmApi) { + return crmApi('CampaignTree', 'getcustominfo', {entity_id: $route.current.params.id}); + }, } }); @@ -95,10 +98,11 @@ 'kpi', 'expenseSum', 'expenses', + 'customInfo', 'dialogService', 'crmApi', '$interval', - function($scope, $routeParams, $sce, currentCampaign, children, parents, kpi, expenseSum, expenses, dialogService, crmApi, $interval) { + function($scope, $routeParams, $sce, currentCampaign, children, parents, kpi, expenseSum, expenses, customInfo, dialogService, crmApi, $interval) { $scope.ts = CRM.ts('de.systopia.campaign'); $scope.currentCampaign = currentCampaign; $scope.currentCampaign.goal_general_htmlSafe = $sce.trustAsHtml($scope.currentCampaign.goal_general); @@ -109,6 +113,7 @@ $scope.parents = parents.parents.reverse(); $scope.expenseSum = expenseSum.values; $scope.expenses = []; + $scope.customInfo = customInfo; crmApi('OptionValue', 'get', {"option_group_id": "campaign_status", "return": "value,label"}).then(function (apiResult) { $scope.campaign_status = apiResult.values; @@ -232,6 +237,23 @@ }; }]); + campaign.filter("filterCustomInfo", function(){ + return function(items){ + var filtered = []; + for (var item in items) { + if (items.hasOwnProperty(item)) { + var current_item = items[item]; + // skip array values (can't be displayed) + if(Array.isArray(current_item.value)) { + continue; + } + filtered.push(current_item); + } + } + return filtered; + } + }); + campaign.filter("preFilterKPI", function(){ return function(items){ var filtered = []; diff --git a/partials/campaign_dashboard.html b/partials/campaign_dashboard.html index bb8c4f6..093bf16 100644 --- a/partials/campaign_dashboard.html +++ b/partials/campaign_dashboard.html @@ -79,6 +79,12 @@
{{ts('Description', {domain: 'de.systopia.campaign'})}}
{{currentCampaign.description ? currentCampaign.description : "-"}}
+ + + + + +
{{value.title}}{{value.value}}
diff --git a/templates/CRM/CampaignTree/Form/Search.tpl b/templates/CRM/CampaignTree/Form/Search.tpl new file mode 100644 index 0000000..5e2f508 --- /dev/null +++ b/templates/CRM/CampaignTree/Form/Search.tpl @@ -0,0 +1,400 @@ +{* + +--------------------------------------------------------------------+ + | CiviCRM version 4.6 | + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC (c) 2004-2015 | + +--------------------------------------------------------------------+ + | This file is a part of CiviCRM. | + | | + | CiviCRM is free software; you can copy, modify, and distribute it | + | under the terms of the GNU Affero General Public License | + | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | + | | + | CiviCRM is distributed in the hope that it will be useful, but | + | WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | + | See the GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public | + | License and the CiviCRM Licensing Exception along | + | with this program; if not, contact CiviCRM LLC | + | at info[AT]civicrm[DOT]org. If you have questions about the | + | GNU Affero General Public License or the licensing of CiviCRM, | + | see the CiviCRM license FAQ at http://civicrm.org/licensing | + +--------------------------------------------------------------------+ +*} +{if !$hasCampaigns} +
+
+   + {ts}None found.{/ts} +
+ +{else} + +{* build search form here *} + +{* Search form and results for campaigns *} +
+
+
+ {ts}Find Campaigns{/ts} +
+ + +
+ + + + + + + + + + + + + + + + + + + + +
+ {$form.title.label}
+ {$form.title.html}
+ + {ts}Complete OR partial campaign name.{/ts} + +
+ {$form.description.label}
+ {$form.description.html}
+ + {ts}Complete OR partial description.{/ts} + +
+ {$form.start_date.label}
+ {include file="CRM/common/jcalendar.tpl" elementName=start_date} +
+ {$form.end_date.label}
+ {include file="CRM/common/jcalendar.tpl" elementName=end_date} +
+ {$form.show.label}
+ {$form.show.html}
+
+ {$form.active.label}
+ {$form.active.html}
+
+ {$form.type_id.label}
+ {$form.type_id.html}
+ + {ts}Filter search by campaign type.{/ts} + +
+ {$form.status_id.label}
+ {$form.status_id.html}
+ + {ts}Filter search by campaign status.{/ts} + +
+ {$form.created_by.label}
+ {$form.created_by.html}
+ + {ts}Complete OR partial creator name.{/ts} + +
+ {$form.external_id.label}
+ {$form.external_id.html}
+ + +
{$form.buttons.html} +
+
+
+
+ + + + + + + + + + + + + + + +
{ts}Name{/ts}{ts}Description{/ts}{ts}Start Date{/ts}{ts}End Date{/ts}{ts}Type{/ts}{ts}Status{/ts}{ts}Created By{/ts}{ts}External ID{/ts}  
+{/if} {* end of search form build *} + +{* handle enable/disable actions*} +{include file="CRM/common/enableDisableApi.tpl"} + +{literal} + +{/literal} + +{* FOOTER *} +
+{include file="CRM/common/formButtons.tpl" location="bottom"} +
diff --git a/templates/CRM/CampaignTree/Page/Dashboard.tpl b/templates/CRM/CampaignTree/Page/Dashboard.tpl new file mode 100644 index 0000000..9527922 --- /dev/null +++ b/templates/CRM/CampaignTree/Page/Dashboard.tpl @@ -0,0 +1,40 @@ +{* + +--------------------------------------------------------------------+ + | CiviCRM version 4.6 | + +--------------------------------------------------------------------+ + | Copyright CiviCRM LLC (c) 2004-2017 | + +--------------------------------------------------------------------+ + | This file is a part of CiviCRM. | + | | + | CiviCRM is free software; you can copy, modify, and distribute it | + | under the terms of the GNU Affero General Public License | + | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | + | | + | CiviCRM is distributed in the hope that it will be useful, but | + | WITHOUT ANY WARRANTY; without even the implied warranty of | + | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | + | See the GNU Affero General Public License for more details. | + | | + | You should have received a copy of the GNU Affero General Public | + | License and the CiviCRM Licensing Exception along | + | with this program; if not, contact CiviCRM LLC | + | at info[AT]civicrm[DOT]org. If you have questions about the | + | GNU Affero General Public License or the licensing of CiviCRM, | + | see the CiviCRM license FAQ at http://civicrm.org/licensing | + +--------------------------------------------------------------------+ +*} + +{* CiviCampaign DashBoard (launch page) *} + +{if !empty($subPageType)} + {* load campaign/survey/petition tab *} + {if ($subPageType == 'Campaign')} + {include file="CRM/CampaignTree/Form/Search.tpl"} + {else} + {include file="CRM/Campaign/Form/Search/$subPageType.tpl"} + {/if} +{else} + {include file="CRM/common/TabHeader.tpl"} +
+{/if} + diff --git a/xml/Menu/campaign.xml b/xml/Menu/campaign.xml new file mode 100644 index 0000000..9c01d3f --- /dev/null +++ b/xml/Menu/campaign.xml @@ -0,0 +1,20 @@ + + + + civicrm/campaign + CRM_CampaignTree_Page_Dashboard + Dashboard + access CiviCRM + + + civicrm/campaign/search + CRM_CampaignTree_Form_Search + AdvancedSearch + access CiviCRM + + + civicrm/ajax/campaign/list + CRM_CampaignTree_Page_AJAX::getCampaignList + access CiviCRM + +