| 1 | <?php |
| 2 | /* |
| 3 | +--------------------------------------------------------------------+ |
| 4 | | CiviCRM version 4.6 | |
| 5 | +--------------------------------------------------------------------+ |
| 6 | | Copyright CiviCRM LLC (c) 2004-2014 | |
| 7 | +--------------------------------------------------------------------+ |
| 8 | | This file is a part of CiviCRM. | |
| 9 | | | |
| 10 | | CiviCRM is free software; you can copy, modify, and distribute it | |
| 11 | | under the terms of the GNU Affero General Public License | |
| 12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | |
| 13 | | | |
| 14 | | CiviCRM is distributed in the hope that it will be useful, but | |
| 15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | |
| 17 | | See the GNU Affero General Public License for more details. | |
| 18 | | | |
| 19 | | You should have received a copy of the GNU Affero General Public | |
| 20 | | License and the CiviCRM Licensing Exception along | |
| 21 | | with this program; if not, contact CiviCRM LLC | |
| 22 | | at info[AT]civicrm[DOT]org. If you have questions about the | |
| 23 | | GNU Affero General Public License or the licensing of CiviCRM, | |
| 24 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | |
| 25 | +--------------------------------------------------------------------+ |
| 26 | */ |
| 27 | |
| 28 | /** |
| 29 | * |
| 30 | * @package CRM |
| 31 | * @copyright CiviCRM LLC (c) 2004-2014 |
| 32 | * $Id$ |
| 33 | * |
| 34 | */ |
| 35 | |
| 36 | require_once 'HTML/QuickForm/Renderer/ArraySmarty.php'; |
| 37 | |
| 38 | /** |
| 39 | * customize the output to meet our specific requirements |
| 40 | */ |
| 41 | class CRM_Core_Form_Renderer extends HTML_QuickForm_Renderer_ArraySmarty { |
| 42 | |
| 43 | /** |
| 44 | * We only need one instance of this object. So we use the singleton |
| 45 | * pattern and cache the instance in this variable |
| 46 | * |
| 47 | * @var object |
| 48 | * @static |
| 49 | */ |
| 50 | static private $_singleton = NULL; |
| 51 | |
| 52 | /** |
| 53 | * The converter from array size to css class |
| 54 | * |
| 55 | * @var array |
| 56 | * @static |
| 57 | */ |
| 58 | static $_sizeMapper = array( |
| 59 | 2 => 'two', |
| 60 | 4 => 'four', |
| 61 | 6 => 'six', |
| 62 | 8 => 'eight', |
| 63 | 12 => 'twelve', |
| 64 | 20 => 'medium', |
| 65 | 30 => 'big', |
| 66 | 45 => 'huge', |
| 67 | ); |
| 68 | |
| 69 | /** |
| 70 | * Constructor |
| 71 | * |
| 72 | */ |
| 73 | function __construct() { |
| 74 | $template = CRM_Core_Smarty::singleton(); |
| 75 | parent::__construct($template); |
| 76 | } |
| 77 | |
| 78 | /** |
| 79 | * Static instance provider. |
| 80 | * |
| 81 | * Method providing static instance of as in Singleton pattern. |
| 82 | */ |
| 83 | public static function &singleton() { |
| 84 | if (!isset(self::$_singleton)) { |
| 85 | self::$_singleton = new CRM_Core_Form_Renderer(); |
| 86 | } |
| 87 | return self::$_singleton; |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Creates an array representing an element containing |
| 92 | * the key for storing this. We allow the parent to do most of the |
| 93 | * work, but then we add some CiviCRM specific enhancements to |
| 94 | * make the html compliant with our css etc |
| 95 | * |
| 96 | * |
| 97 | * @param $element |
| 98 | * HTML_QuickForm_element. |
| 99 | * @param $required |
| 100 | * Bool - Whether an element is required. |
| 101 | * @param $error |
| 102 | * String - Error associated with the element. |
| 103 | * |
| 104 | * @return array |
| 105 | */ |
| 106 | public function _elementToArray(&$element, $required, $error) { |
| 107 | self::updateAttributes($element, $required, $error); |
| 108 | |
| 109 | $el = parent::_elementToArray($element, $required, $error); |
| 110 | |
| 111 | // add label html |
| 112 | if (!empty($el['label'])) { |
| 113 | $id = $element->getAttribute('id'); |
| 114 | if (!empty($id)) { |
| 115 | $el['label'] = '<label for="' . $id . '">' . $el['label'] . '</label>'; |
| 116 | } |
| 117 | else { |
| 118 | $el['label'] = "<label>{$el['label']}</label>"; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | // Display-only (frozen) elements |
| 123 | if (!empty($el['frozen'])) { |
| 124 | if ($element->getAttribute('data-api-entity') && $element->getAttribute('data-entity-value')) { |
| 125 | $this->renderFrozenEntityRef($el, $element); |
| 126 | } |
| 127 | $el['html'] = '<span class="crm-frozen-field">' . $el['html'] . '</span>'; |
| 128 | } |
| 129 | // Active form elements |
| 130 | else { |
| 131 | if ($element->getType() == 'select' && $element->getAttribute('data-option-edit-path')) { |
| 132 | $this->addOptionsEditLink($el, $element); |
| 133 | } |
| 134 | |
| 135 | if ($element->getType() == 'group' && $element->getAttribute('allowClear')) { |
| 136 | $this->appendUnselectButton($el, $element); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | return $el; |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * Update the attributes of this element and add a few CiviCRM |
| 145 | * based attributes so we can style this form element better |
| 146 | * |
| 147 | * |
| 148 | * @param $element |
| 149 | * HTML_QuickForm_element object. |
| 150 | * @param $required |
| 151 | * Bool Whether an element is required. |
| 152 | * @param $error |
| 153 | * String Error associated with the element. |
| 154 | * |
| 155 | * @return array |
| 156 | * @static |
| 157 | */ |
| 158 | public static function updateAttributes(&$element, $required, $error) { |
| 159 | // lets create an id for all input elements, so we can generate nice label tags |
| 160 | // to make it nice and clean, we'll just use the elementName if it is non null |
| 161 | $attributes = array(); |
| 162 | if (!$element->getAttribute('id')) { |
| 163 | $name = $element->getAttribute('name'); |
| 164 | if ($name) { |
| 165 | $attributes['id'] = str_replace(array(']', '['), |
| 166 | array('', '_'), |
| 167 | $name |
| 168 | ); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | $class = $element->getAttribute('class'); |
| 173 | $type = $element->getType(); |
| 174 | if (!$class) { |
| 175 | if ($type == 'text') { |
| 176 | $size = $element->getAttribute('size'); |
| 177 | if (!empty($size)) { |
| 178 | $class = CRM_Utils_Array::value($size, self::$_sizeMapper); |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | if ($type == 'select' && $element->getAttribute('multiple')) { |
| 184 | $type = 'multiselect'; |
| 185 | } |
| 186 | // Add widget-specific class |
| 187 | if (!$class || strpos($class, 'crm-form-') === FALSE) { |
| 188 | $class = ($class ? "$class " : '') . 'crm-form-' . $type; |
| 189 | } |
| 190 | elseif (strpos($class, 'crm-form-entityref') !== FALSE) { |
| 191 | self::preProcessEntityRef($element); |
| 192 | } |
| 193 | elseif (strpos($class, 'crm-form-contact-reference') !== FALSE) { |
| 194 | self::preprocessContactReference($element); |
| 195 | } |
| 196 | |
| 197 | if ($required) { |
| 198 | $class .= ' required'; |
| 199 | } |
| 200 | |
| 201 | if ($error) { |
| 202 | $class .= ' error'; |
| 203 | } |
| 204 | |
| 205 | $attributes['class'] = $class; |
| 206 | $element->updateAttributes($attributes); |
| 207 | } |
| 208 | |
| 209 | /** |
| 210 | * Convert IDs to values and format for display |
| 211 | * |
| 212 | * @param $field |
| 213 | * HTML_QuickForm_element. |
| 214 | */ |
| 215 | public static function preProcessEntityRef($field) { |
| 216 | $val = $field->getValue(); |
| 217 | // Support array values |
| 218 | if (is_array($val)) { |
| 219 | $val = implode(',', $val); |
| 220 | $field->setValue($val); |
| 221 | } |
| 222 | if ($val) { |
| 223 | $entity = $field->getAttribute('data-api-entity'); |
| 224 | // Get api params, ensure it is an array |
| 225 | $params = $field->getAttribute('data-api-params'); |
| 226 | $params = $params ? json_decode($params, TRUE) : array(); |
| 227 | // Support serialized values |
| 228 | if (strpos($val, CRM_Core_DAO::VALUE_SEPARATOR) !== FALSE) { |
| 229 | $val = str_replace(CRM_Core_DAO::VALUE_SEPARATOR, ',', trim($val, CRM_Core_DAO::VALUE_SEPARATOR)); |
| 230 | $field->setValue($val); |
| 231 | } |
| 232 | $result = civicrm_api3($entity, 'getlist', array('id' => $val) + $params); |
| 233 | if ($field->isFrozen()) { |
| 234 | $field->removeAttribute('class'); |
| 235 | } |
| 236 | if (!empty($result['values'])) { |
| 237 | $field->setAttribute('data-entity-value', json_encode($result['values'])); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Render entity references as text. |
| 244 | * If user has permission, format as link (for now limited to contacts). |
| 245 | * @param $el |
| 246 | * Array. |
| 247 | * @param $field |
| 248 | * HTML_QuickForm_element. |
| 249 | */ |
| 250 | public function renderFrozenEntityRef(&$el, $field) { |
| 251 | $entity = $field->getAttribute('data-api-entity'); |
| 252 | $vals = json_decode($field->getAttribute('data-entity-value'), TRUE); |
| 253 | $display = array(); |
| 254 | |
| 255 | // Custom fields of type contactRef store their data in a slightly different format |
| 256 | if ($field->getAttribute('data-crm-custom') && $entity == 'contact') { |
| 257 | $vals = array(array('id' => $vals['id'], 'label' => $vals['text'])); |
| 258 | } |
| 259 | |
| 260 | foreach ($vals as $val) { |
| 261 | // Format contact as link |
| 262 | if ($entity == 'contact' && CRM_Contact_BAO_Contact_Permission::allow($val['id'], CRM_Core_Permission::VIEW)) { |
| 263 | $url = CRM_Utils_System::url("civicrm/contact/view", array('reset' => 1, 'cid' => $val['id'])); |
| 264 | $val['label'] = '<a class="view-' . $entity . ' no-popup" href="' . $url . '" title="' . ts('View Contact') . '">' . $val['label'] . '</a>'; |
| 265 | } |
| 266 | $display[] = $val['label']; |
| 267 | } |
| 268 | |
| 269 | $el['html'] = implode('; ', $display) . '<input type="hidden" value="' . $field->getValue() . '" name="' . $field->getAttribute('name') . '">'; |
| 270 | } |
| 271 | |
| 272 | /** |
| 273 | * Pre-fill contact name for a custom field of type ContactReference |
| 274 | * |
| 275 | * Todo: Migrate contact reference fields to use EntityRef |
| 276 | * |
| 277 | * @param $field |
| 278 | * HTML_QuickForm_element. |
| 279 | */ |
| 280 | public static function preprocessContactReference($field) { |
| 281 | $val = $field->getValue(); |
| 282 | if ($val && is_numeric($val)) { |
| 283 | |
| 284 | $list = array_keys(CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, |
| 285 | 'contact_reference_options' |
| 286 | ), '1'); |
| 287 | |
| 288 | $return = array_unique(array_merge(array('sort_name'), $list)); |
| 289 | |
| 290 | $contact = civicrm_api('contact', 'getsingle', array('id' => $val, 'return' => $return, 'version' => 3)); |
| 291 | |
| 292 | if (!empty($contact['id'])) { |
| 293 | $view = array(); |
| 294 | foreach ($return as $fld) { |
| 295 | if (!empty($contact[$fld])) { |
| 296 | $view[] = $contact[$fld]; |
| 297 | } |
| 298 | } |
| 299 | $field->setAttribute('data-entity-value', json_encode(array('id' => $contact['id'], 'text' => implode(' :: ', $view)))); |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * @param array $el |
| 306 | * @param HTML_QuickForm_element $field |
| 307 | */ |
| 308 | public function addOptionsEditLink(&$el, $field) { |
| 309 | if (CRM_Core_Permission::check('administer CiviCRM')) { |
| 310 | // NOTE: $path is used on the client-side to know which option lists need rebuilding, |
| 311 | // that's why we need that bit of data both in the link and in the form element |
| 312 | $path = $field->getAttribute('data-option-edit-path'); |
| 313 | // NOTE: If we ever needed to support arguments in this link other than reset=1 we could split $path here if it contains a ? |
| 314 | $url = CRM_Utils_System::url($path, 'reset=1'); |
| 315 | $el['html'] .= ' <a href="' . $url . '" class="crm-option-edit-link medium-popup crm-hover-button" target="_blank" title="' . ts('Edit Options') . '" data-option-edit-path="' . $path . '"><span class="icon ui-icon-wrench"></span></a>'; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | /** |
| 320 | * @param array $el |
| 321 | * @param HTML_QuickForm_element $field |
| 322 | */ |
| 323 | public function appendUnselectButton(&$el, $field) { |
| 324 | // Initially hide if not needed |
| 325 | // Note: visibility:hidden prevents layout jumping around unlike display:none |
| 326 | $display = $field->getValue() !== NULL ? '' : ' style="visibility:hidden;"'; |
| 327 | $el['html'] .= ' <a href="#" class="crm-hover-button crm-clear-link"' . $display . ' title="' . ts('Clear') . '"><span class="icon ui-icon-close"></span></a>'; |
| 328 | } |
| 329 | } |