PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/craft/app/fieldtypes/BaseElementFieldType.php

https://gitlab.com/madebycloud/derekman
PHP | 509 lines | 287 code | 61 blank | 161 comment | 39 complexity | f4864c6dc1b154d8bea576c5fae72e7b MD5 | raw file
  1. <?php
  2. namespace Craft;
  3. /**
  4. * Base element fieldtype class.
  5. *
  6. * @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
  7. * @copyright Copyright (c) 2014, Pixel & Tonic, Inc.
  8. * @license http://buildwithcraft.com/license Craft License Agreement
  9. * @see http://buildwithcraft.com
  10. * @package craft.app.fieldtypes
  11. * @since 1.0
  12. */
  13. abstract class BaseElementFieldType extends BaseFieldType
  14. {
  15. // Properties
  16. // =========================================================================
  17. /**
  18. * List of built-in component aliases to be imported.
  19. *
  20. * @var string $elementType
  21. */
  22. protected $elementType;
  23. /**
  24. * The JS class that should be initialized for the input.
  25. *
  26. * @var string|null $inputJsClass
  27. */
  28. protected $inputJsClass;
  29. /**
  30. * Whether to allow multiple source selection in the settings.
  31. *
  32. * @var bool $allowMultipleSources
  33. */
  34. protected $allowMultipleSources = true;
  35. /**
  36. * Whether to allow the Limit setting.
  37. *
  38. * @var bool $allowLimit
  39. */
  40. protected $allowLimit = true;
  41. /**
  42. * Template to use for field rendering.
  43. *
  44. * @var string
  45. */
  46. protected $inputTemplate = '_includes/forms/elementSelect';
  47. /**
  48. * Whether the elements have a custom sort order.
  49. *
  50. * @var bool $sortable
  51. */
  52. protected $sortable = true;
  53. /**
  54. * @var bool
  55. */
  56. private $_makeExistingRelationsTranslatable = false;
  57. // Public Methods
  58. // =========================================================================
  59. /**
  60. * @inheritDoc IComponentType::getName()
  61. *
  62. * @return string
  63. */
  64. public function getName()
  65. {
  66. return $this->getElementType()->getName();
  67. }
  68. /**
  69. * @inheritDoc IFieldType::defineContentAttribute()
  70. *
  71. * @return mixed
  72. */
  73. public function defineContentAttribute()
  74. {
  75. return false;
  76. }
  77. /**
  78. * @inheritDoc ISavableComponentType::getSettingsHtml()
  79. *
  80. * @return string|null
  81. */
  82. public function getSettingsHtml()
  83. {
  84. $sources = array();
  85. foreach ($this->getElementType()->getSources() as $key => $source)
  86. {
  87. if (!isset($source['heading']))
  88. {
  89. $sources[] = array('label' => $source['label'], 'value' => $key);
  90. }
  91. }
  92. return craft()->templates->render('_components/fieldtypes/elementfieldsettings', array(
  93. 'allowMultipleSources' => $this->allowMultipleSources,
  94. 'allowLimit' => $this->allowLimit,
  95. 'sources' => $sources,
  96. 'targetLocaleField' => $this->getTargetLocaleFieldHtml(),
  97. 'settings' => $this->getSettings(),
  98. 'type' => $this->getName()
  99. ));
  100. }
  101. /**
  102. * @inheritDoc IFieldType::validate()
  103. *
  104. * @param array $value
  105. *
  106. * @return true|string|array
  107. */
  108. public function validate($value)
  109. {
  110. $errors = array();
  111. if ($this->allowLimit && ($limit = $this->getSettings()->limit) && is_array($value) && count($value) > $limit)
  112. {
  113. if ($limit == 1)
  114. {
  115. $errors[] = Craft::t('There can’t be more than one selection.');
  116. }
  117. else
  118. {
  119. $errors[] = Craft::t('There can’t be more than {limit} selections.', array('limit' => $limit));
  120. }
  121. }
  122. if ($errors)
  123. {
  124. return $errors;
  125. }
  126. else
  127. {
  128. return true;
  129. }
  130. }
  131. /**
  132. * @inheritDoc IFieldType::prepValue()
  133. *
  134. * @param mixed $value
  135. *
  136. * @return ElementCriteriaModel
  137. */
  138. public function prepValue($value)
  139. {
  140. $criteria = craft()->elements->getCriteria($this->elementType);
  141. $criteria->locale = $this->getTargetLocale();
  142. // $value will be an array of element IDs if there was a validation error or we're loading a draft/version.
  143. if (is_array($value))
  144. {
  145. $criteria->id = array_values(array_filter($value));
  146. $criteria->fixedOrder = true;
  147. }
  148. else if ($value === '')
  149. {
  150. $criteria->id = false;
  151. }
  152. else if (isset($this->element) && $this->element->id)
  153. {
  154. $criteria->relatedTo = array(
  155. 'sourceElement' => $this->element->id,
  156. 'sourceLocale' => $this->element->locale,
  157. 'field' => $this->model->id
  158. );
  159. if ($this->sortable)
  160. {
  161. $criteria->order = 'sortOrder';
  162. }
  163. if (!$this->allowMultipleSources && $this->getSettings()->source)
  164. {
  165. $source = $this->getElementType()->getSource($this->getSettings()->source);
  166. // Does the source specify any criteria attributes?
  167. if (!empty($source['criteria']))
  168. {
  169. $criteria->setAttributes($source['criteria']);
  170. }
  171. }
  172. }
  173. else
  174. {
  175. $criteria->id = false;
  176. }
  177. if ($this->allowLimit && $this->getSettings()->limit)
  178. {
  179. $criteria->limit = $this->getSettings()->limit;
  180. }
  181. else
  182. {
  183. $criteria->limit = null;
  184. }
  185. return $criteria;
  186. }
  187. /**
  188. * @inheritDoc IFieldType::getInputHtml()
  189. *
  190. * @param string $name
  191. * @param mixed $criteria
  192. *
  193. * @return string
  194. */
  195. public function getInputHtml($name, $criteria)
  196. {
  197. $variables = $this->getInputTemplateVariables($name, $criteria);
  198. return craft()->templates->render($this->inputTemplate, $variables);
  199. }
  200. /**
  201. * @inheritDoc IFieldType::getSearchKeywords()
  202. *
  203. * @param ElementCriteriaModel $criteria
  204. *
  205. * @return string
  206. */
  207. public function getSearchKeywords($criteria)
  208. {
  209. $titles = array();
  210. foreach ($criteria->find() as $element)
  211. {
  212. $titles[] = (string) $element;
  213. }
  214. return parent::getSearchKeywords($titles);
  215. }
  216. /**
  217. * @inheritDoc IFieldType::onAfterElementSave()
  218. *
  219. * @return null
  220. */
  221. public function onAfterElementSave()
  222. {
  223. $targetIds = $this->element->getContent()->getAttribute($this->model->handle);
  224. if ($targetIds !== null)
  225. {
  226. craft()->relations->saveRelations($this->model, $this->element, $targetIds);
  227. }
  228. }
  229. /**
  230. * @inheritDoc BaseFieldType::getStaticHtml()
  231. *
  232. * @param mixed $value
  233. *
  234. * @return string
  235. */
  236. public function getStaticHtml($value)
  237. {
  238. if (count($value))
  239. {
  240. $html = '<div class="elementselect"><div class="elements">';
  241. foreach ($value as $element)
  242. {
  243. $html .= craft()->templates->render('_elements/element', array(
  244. 'element' => $element
  245. ));
  246. }
  247. $html .= '</div></div>';
  248. return $html;
  249. }
  250. else
  251. {
  252. return '<p class="light">'.Craft::t('Nothing selected.').'</p>';
  253. }
  254. }
  255. /**
  256. * @inheritDoc IFieldType::onBeforeSave()
  257. *
  258. * @return null
  259. */
  260. public function onBeforeSave()
  261. {
  262. $this->_makeExistingRelationsTranslatable = false;
  263. if ($this->model->id && $this->model->translatable)
  264. {
  265. $existingField = craft()->fields->getFieldById($this->model->id);
  266. if ($existingField && $existingField->translatable == 0)
  267. {
  268. $this->_makeExistingRelationsTranslatable = true;
  269. }
  270. }
  271. }
  272. /**
  273. * @inheritDoc IFieldType::onAfterSave()
  274. *
  275. * @return null
  276. */
  277. public function onAfterSave()
  278. {
  279. if ($this->_makeExistingRelationsTranslatable)
  280. {
  281. craft()->tasks->createTask('LocalizeRelations', null, array(
  282. 'fieldId' => $this->model->id,
  283. ));
  284. }
  285. }
  286. // Protected Methods
  287. // =========================================================================
  288. /**
  289. * Returns the label for the "Add" button.
  290. *
  291. * @return string
  292. */
  293. protected function getAddButtonLabel()
  294. {
  295. return Craft::t('Add {type}', array(
  296. 'type' => StringHelper::toLowerCase($this->getElementType()->getClassHandle())
  297. ));
  298. }
  299. /**
  300. * Returns the element type.
  301. *
  302. * @throws Exception
  303. * @return BaseElementType
  304. */
  305. protected function getElementType()
  306. {
  307. $elementType = craft()->elements->getElementType($this->elementType);
  308. if (!$elementType)
  309. {
  310. throw new Exception(Craft::t('No element type exists with the class “{class}”', array('class' => $this->elementType)));
  311. }
  312. return $elementType;
  313. }
  314. /**
  315. * Returns an array of variables that should be passed to the input template.
  316. *
  317. * @param string $name
  318. * @param mixed $criteria
  319. *
  320. * @return array
  321. */
  322. protected function getInputTemplateVariables($name, $criteria)
  323. {
  324. $settings = $this->getSettings();
  325. if (!($criteria instanceof ElementCriteriaModel))
  326. {
  327. $criteria = craft()->elements->getCriteria($this->elementType);
  328. $criteria->id = false;
  329. }
  330. $criteria->status = null;
  331. $criteria->localeEnabled = null;
  332. $selectionCriteria = $this->getInputSelectionCriteria();
  333. $selectionCriteria['localeEnabled'] = null;
  334. $selectionCriteria['locale'] = $this->getTargetLocale();
  335. return array(
  336. 'jsClass' => $this->inputJsClass,
  337. 'elementType' => new ElementTypeVariable($this->getElementType()),
  338. 'id' => craft()->templates->formatInputId($name),
  339. 'fieldId' => $this->model->id,
  340. 'storageKey' => 'field.'.$this->model->id,
  341. 'name' => $name,
  342. 'elements' => $criteria,
  343. 'sources' => $this->getInputSources(),
  344. 'criteria' => $selectionCriteria,
  345. 'sourceElementId' => (isset($this->element->id) ? $this->element->id : null),
  346. 'limit' => ($this->allowLimit ? $settings->limit : null),
  347. 'selectionLabel' => Craft::t($this->getSettings()->selectionLabel),
  348. );
  349. }
  350. /**
  351. * Returns an array of the source keys the field should be able to select elements from.
  352. *
  353. * @return array
  354. */
  355. protected function getInputSources()
  356. {
  357. if ($this->allowMultipleSources)
  358. {
  359. $sources = $this->getSettings()->sources;
  360. }
  361. else
  362. {
  363. $sources = array($this->getSettings()->source);
  364. }
  365. return $sources;
  366. }
  367. /**
  368. * Returns any additional criteria parameters limiting which elements the field should be able to select.
  369. *
  370. * @return array
  371. */
  372. protected function getInputSelectionCriteria()
  373. {
  374. return array();
  375. }
  376. /**
  377. * Returns the locale that target elements should have.
  378. *
  379. * @return string
  380. */
  381. protected function getTargetLocale()
  382. {
  383. if (craft()->isLocalized())
  384. {
  385. $targetLocale = $this->getSettings()->targetLocale;
  386. if ($targetLocale)
  387. {
  388. return $targetLocale;
  389. }
  390. else if (isset($this->element))
  391. {
  392. return $this->element->locale;
  393. }
  394. }
  395. return craft()->getLanguage();
  396. }
  397. /**
  398. * Returns the HTML for the Target Locale setting.
  399. *
  400. * @return string|null
  401. */
  402. protected function getTargetLocaleFieldHtml()
  403. {
  404. if (craft()->isLocalized() && $this->getElementType()->isLocalized())
  405. {
  406. $localeOptions = array(
  407. array('label' => Craft::t('Same as source'), 'value' => null)
  408. );
  409. foreach (craft()->i18n->getSiteLocales() as $locale)
  410. {
  411. $localeOptions[] = array('label' => $locale->getName(), 'value' => $locale->getId());
  412. }
  413. return craft()->templates->renderMacro('_includes/forms', 'selectField', array(
  414. array(
  415. 'label' => Craft::t('Target Locale'),
  416. 'instructions' => Craft::t('Which locale do you want to select {type} in?', array('type' => StringHelper::toLowerCase($this->getName()))),
  417. 'id' => 'targetLocale',
  418. 'name' => 'targetLocale',
  419. 'options' => $localeOptions,
  420. 'value' => $this->getSettings()->targetLocale
  421. )
  422. ));
  423. }
  424. }
  425. /**
  426. * @inheritDoc BaseSavableComponentType::defineSettings()
  427. *
  428. * @return array
  429. */
  430. protected function defineSettings()
  431. {
  432. if ($this->allowMultipleSources)
  433. {
  434. $settings['sources'] = AttributeType::Mixed;
  435. }
  436. else
  437. {
  438. $settings['source'] = AttributeType::String;
  439. }
  440. $settings['targetLocale'] = AttributeType::String;
  441. if ($this->allowLimit)
  442. {
  443. $settings['limit'] = array(AttributeType::Number, 'min' => 0);
  444. }
  445. $settings['selectionLabel'] = array(AttributeType::String, 'default' => $this->getAddButtonLabel());
  446. return $settings;
  447. }
  448. }