PageRenderTime 23ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/symphony/lib/toolkit/fields/field.taglist.php

https://github.com/nils-werner/symphony-2
PHP | 468 lines | 311 code | 102 blank | 55 comment | 68 complexity | e5cfd162a96ec708b31d854ca0d743f2 MD5 | raw file
  1. <?php
  2. /**
  3. * @package toolkit
  4. */
  5. require_once FACE . '/interface.exportablefield.php';
  6. require_once FACE . '/interface.importablefield.php';
  7. /**
  8. * The Tag List field is really a different interface for the Select Box
  9. * field, offering a tag interface that can have static suggestions,
  10. * suggestions from another field or a dynamic list based on what an Author
  11. * has previously used for this field.
  12. */
  13. class FieldTagList extends Field implements ExportableField, ImportableField {
  14. public function __construct(){
  15. parent::__construct();
  16. $this->_name = __('Tag List');
  17. $this->_required = true;
  18. $this->set('required', 'no');
  19. }
  20. /*-------------------------------------------------------------------------
  21. Definition:
  22. -------------------------------------------------------------------------*/
  23. public function canFilter() {
  24. return true;
  25. }
  26. public function canPrePopulate(){
  27. return true;
  28. }
  29. public function requiresSQLGrouping() {
  30. return true;
  31. }
  32. public function allowDatasourceParamOutput(){
  33. return true;
  34. }
  35. /*-------------------------------------------------------------------------
  36. Setup:
  37. -------------------------------------------------------------------------*/
  38. public function createTable(){
  39. return Symphony::Database()->query("
  40. CREATE TABLE IF NOT EXISTS `tbl_entries_data_" . $this->get('id') . "` (
  41. `id` int(11) unsigned NOT NULL auto_increment,
  42. `entry_id` int(11) unsigned NOT NULL,
  43. `handle` varchar(255) default NULL,
  44. `value` varchar(255) default NULL,
  45. PRIMARY KEY (`id`),
  46. KEY `entry_id` (`entry_id`),
  47. KEY `handle` (`handle`),
  48. KEY `value` (`value`)
  49. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
  50. ");
  51. }
  52. /*-------------------------------------------------------------------------
  53. Utilities:
  54. -------------------------------------------------------------------------*/
  55. public function set($field, $value){
  56. if($field == 'pre_populate_source' && !is_array($value)) $value = preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
  57. $this->_fields[$field] = $value;
  58. }
  59. public function findAllTags(){
  60. if(!is_array($this->get('pre_populate_source'))) return;
  61. $values = array();
  62. foreach($this->get('pre_populate_source') as $item){
  63. $result = Symphony::Database()->fetchCol('value', sprintf(
  64. "SELECT DISTINCT `value` FROM tbl_entries_data_%d ORDER BY `value` ASC",
  65. ($item == 'existing' ? $this->get('id') : $item)
  66. ));
  67. if(!is_array($result) || empty($result)) continue;
  68. $values = array_merge($values, $result);
  69. }
  70. return array_unique($values);
  71. }
  72. private static function __tagArrayToString(array $tags){
  73. if(empty($tags)) return NULL;
  74. sort($tags);
  75. return implode(', ', $tags);
  76. }
  77. /*-------------------------------------------------------------------------
  78. Settings:
  79. -------------------------------------------------------------------------*/
  80. public function findDefaults(array &$settings){
  81. if(!isset($settings['pre_populate_source'])) $settings['pre_populate_source'] = array('existing');
  82. }
  83. public function displaySettingsPanel(XMLElement &$wrapper, $errors = null) {
  84. parent::displaySettingsPanel($wrapper, $errors);
  85. $label = Widget::Label(__('Suggestion List'));
  86. $sections = SectionManager::fetch(NULL, 'ASC', 'name');
  87. $field_groups = array();
  88. if(is_array($sections) && !empty($sections))
  89. foreach($sections as $section) $field_groups[$section->get('id')] = array('fields' => $section->fetchFields(), 'section' => $section);
  90. $options = array(
  91. array('existing', (in_array('existing', $this->get('pre_populate_source'))), __('Existing Values')),
  92. );
  93. foreach($field_groups as $group){
  94. if(!is_array($group['fields'])) continue;
  95. $fields = array();
  96. foreach($group['fields'] as $f){
  97. if($f->get('id') != $this->get('id') && $f->canPrePopulate()) $fields[] = array($f->get('id'), (in_array($f->get('id'), $this->get('pre_populate_source'))), $f->get('label'));
  98. }
  99. if(is_array($fields) && !empty($fields)) $options[] = array('label' => $group['section']->get('name'), 'options' => $fields);
  100. }
  101. $label->appendChild(Widget::Select('fields['.$this->get('sortorder').'][pre_populate_source][]', $options, array('multiple' => 'multiple')));
  102. $wrapper->appendChild($label);
  103. $this->buildValidationSelect($wrapper, $this->get('validator'), 'fields['.$this->get('sortorder').'][validator]');
  104. $div = new XMLElement('div', NULL, array('class' => 'two columns'));
  105. $this->appendRequiredCheckbox($div);
  106. $this->appendShowColumnCheckbox($div);
  107. $wrapper->appendChild($div);
  108. }
  109. public function commit(){
  110. if(!parent::commit()) return false;
  111. $id = $this->get('id');
  112. if($id === false) return false;
  113. $fields = array();
  114. $fields['pre_populate_source'] = (is_null($this->get('pre_populate_source')) ? NULL : implode(',', $this->get('pre_populate_source')));
  115. $fields['validator'] = ($fields['validator'] == 'custom' ? NULL : $this->get('validator'));
  116. return FieldManager::saveSettings($id, $fields);
  117. }
  118. /*-------------------------------------------------------------------------
  119. Publish:
  120. -------------------------------------------------------------------------*/
  121. public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null){
  122. $value = NULL;
  123. if(isset($data['value'])){
  124. $value = (is_array($data['value']) ? self::__tagArrayToString($data['value']) : $data['value']);
  125. }
  126. $label = Widget::Label($this->get('label'));
  127. if($this->get('required') != 'yes') $label->appendChild(new XMLElement('i', __('Optional')));
  128. $label->appendChild(
  129. Widget::Input('fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix, (strlen($value) != 0 ? General::sanitize($value) : NULL))
  130. );
  131. if($flagWithError != NULL) $wrapper->appendChild(Widget::Error($label, $flagWithError));
  132. else $wrapper->appendChild($label);
  133. if($this->get('pre_populate_source') != NULL){
  134. $existing_tags = $this->findAllTags();
  135. if(is_array($existing_tags) && !empty($existing_tags)){
  136. $taglist = new XMLElement('ul');
  137. $taglist->setAttribute('class', 'tags');
  138. foreach($existing_tags as $tag) {
  139. $taglist->appendChild(
  140. new XMLElement('li', General::sanitize($tag))
  141. );
  142. }
  143. $wrapper->appendChild($taglist);
  144. }
  145. }
  146. }
  147. public function checkPostFieldData($data, &$message, $entry_id = null){
  148. $message = NULL;
  149. if($this->get('required') == 'yes' && strlen($data) == 0){
  150. $message = __('ā€˜%sā€™ is a required field.', array($this->get('label')));
  151. return self::__MISSING_FIELDS__;
  152. }
  153. if($this->get('validator')) {
  154. $data = preg_split('/\,\s*/i', $data, -1, PREG_SPLIT_NO_EMPTY);
  155. $data = array_map('trim', $data);
  156. if(empty($data)) return self::__OK__;
  157. if(!General::validateString($data, $this->get('validator'))) {
  158. $message = __("'%s' contains invalid data. Please check the contents.", array($this->get('label')));
  159. return self::__INVALID_FIELDS__;
  160. }
  161. }
  162. return self::__OK__;
  163. }
  164. public function processRawFieldData($data, &$status, &$message=null, $simulate=false, $entry_id=NULL){
  165. $status = self::__OK__;
  166. $data = preg_split('/\,\s*/i', $data, -1, PREG_SPLIT_NO_EMPTY);
  167. $data = array_map('trim', $data);
  168. $result = array(
  169. 'value' => array(),
  170. 'handle' => array()
  171. );
  172. if(empty($data)) return null;
  173. // Do a case insensitive removal of duplicates
  174. $data = General::array_remove_duplicates($data, true);
  175. sort($data);
  176. $result = array();
  177. foreach($data as $value){
  178. $result['value'][] = $value;
  179. $result['handle'][] = Lang::createHandle($value);
  180. }
  181. return $result;
  182. }
  183. /*-------------------------------------------------------------------------
  184. Output:
  185. -------------------------------------------------------------------------*/
  186. public function appendFormattedElement(XMLElement &$wrapper, $data, $encode = false, $mode = null, $entry_id = null) {
  187. if (!is_array($data) or empty($data)) return;
  188. $list = new XMLElement($this->get('element_name'));
  189. if (!is_array($data['handle']) and !is_array($data['value'])) {
  190. $data = array(
  191. 'handle' => array($data['handle']),
  192. 'value' => array($data['value'])
  193. );
  194. }
  195. foreach ($data['value'] as $index => $value) {
  196. $list->appendChild(new XMLElement(
  197. 'item', General::sanitize($value), array(
  198. 'handle' => $data['handle'][$index]
  199. )
  200. ));
  201. }
  202. $wrapper->appendChild($list);
  203. }
  204. public function prepareTableValue($data, XMLElement $link=NULL, $entry_id = null){
  205. if(!is_array($data) || empty($data)) return;
  206. $value = NULL;
  207. if(isset($data['value'])){
  208. $value = (is_array($data['value']) ? self::__tagArrayToString($data['value']) : $data['value']);
  209. }
  210. return parent::prepareTableValue(array('value' => General::sanitize($value)), $link, $entry_id = null);
  211. }
  212. public function getParameterPoolValue(array $data, $entry_id=NULL) {
  213. return $this->prepareExportValue($data, ExportableField::LIST_OF + ExportableField::HANDLE, $entry_id);
  214. }
  215. /*-------------------------------------------------------------------------
  216. Import:
  217. -------------------------------------------------------------------------*/
  218. public function getImportModes() {
  219. return array(
  220. 'getValue' => ImportableField::STRING_VALUE,
  221. 'getPostdata' => ImportableField::ARRAY_VALUE
  222. );
  223. }
  224. public function prepareImportValue($data, $mode, $entry_id = null) {
  225. $message = $status = null;
  226. $modes = (object)$this->getImportModes();
  227. if(!is_array($data)) {
  228. $data = array($data);
  229. }
  230. if($mode === $modes->getValue) {
  231. return implode(', ', $data);
  232. }
  233. else if($mode === $modes->getPostdata) {
  234. return $this->processRawFieldData($data, $status, $message, true, $entry_id);
  235. }
  236. return null;
  237. }
  238. /*-------------------------------------------------------------------------
  239. Export:
  240. -------------------------------------------------------------------------*/
  241. /**
  242. * Return a list of supported export modes for use with `prepareExportValue`.
  243. *
  244. * @return array
  245. */
  246. public function getExportModes() {
  247. return array(
  248. 'listHandle' => ExportableField::LIST_OF
  249. + ExportableField::HANDLE,
  250. 'listValue' => ExportableField::LIST_OF
  251. + ExportableField::VALUE,
  252. 'listHandleToValue' => ExportableField::LIST_OF
  253. + ExportableField::HANDLE
  254. + ExportableField::VALUE,
  255. 'getPostdata' => ExportableField::POSTDATA
  256. );
  257. }
  258. /**
  259. * Give the field some data and ask it to return a value using one of many
  260. * possible modes.
  261. *
  262. * @param mixed $data
  263. * @param integer $mode
  264. * @param integer $entry_id
  265. * @return array|null
  266. */
  267. public function prepareExportValue($data, $mode, $entry_id = null) {
  268. $modes = (object)$this->getExportModes();
  269. if (isset($data['handle']) && is_array($data['handle']) === false) {
  270. $data['handle'] = array(
  271. $data['handle']
  272. );
  273. }
  274. if (isset($data['value']) && is_array($data['value']) === false) {
  275. $data['value'] = array(
  276. $data['value']
  277. );
  278. }
  279. // Handle => value pairs:
  280. if ($mode === $modes->listHandleToValue) {
  281. return isset($data['handle'], $data['value'])
  282. ? array_combine($data['handle'], $data['value'])
  283. : array();
  284. }
  285. // Array of handles:
  286. else if ($mode === $modes->listHandle) {
  287. return isset($data['handle'])
  288. ? $data['handle']
  289. : array();
  290. }
  291. // Array of values:
  292. else if ($mode === $modes->listValue) {
  293. return isset($data['value'])
  294. ? $data['value']
  295. : array();
  296. }
  297. // Comma seperated values:
  298. else if ($mode === $modes->getPostdata) {
  299. return isset($data['value'])
  300. ? implode(', ', $data['value'])
  301. : null;
  302. }
  303. }
  304. /*-------------------------------------------------------------------------
  305. Filtering:
  306. -------------------------------------------------------------------------*/
  307. public function displayDatasourceFilterPanel(XMLElement &$wrapper, $data = null, $errors = null, $fieldnamePrefix=NULL, $fieldnamePostfix=NULL){
  308. parent::displayDatasourceFilterPanel($wrapper, $data, $errors, $fieldnamePrefix, $fieldnamePostfix);
  309. if($this->get('pre_populate_source') != NULL){
  310. $existing_tags = $this->findAllTags();
  311. if(is_array($existing_tags) && !empty($existing_tags)){
  312. $taglist = new XMLElement('ul');
  313. $taglist->setAttribute('class', 'tags');
  314. foreach($existing_tags as $tag) {
  315. $taglist->appendChild(
  316. new XMLElement('li', General::sanitize($tag))
  317. );
  318. }
  319. $wrapper->appendChild($taglist);
  320. }
  321. }
  322. }
  323. public function buildDSRetrievalSQL($data, &$joins, &$where, $andOperation = false) {
  324. $field_id = $this->get('id');
  325. if (self::isFilterRegex($data[0])) {
  326. $this->buildRegexSQL($data[0], array('value', 'handle'), $joins, $where);
  327. }
  328. else if ($andOperation) {
  329. foreach ($data as $value) {
  330. $this->_key++;
  331. $value = $this->cleanValue($value);
  332. $joins .= "
  333. LEFT JOIN
  334. `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
  335. ON (e.id = t{$field_id}_{$this->_key}.entry_id)
  336. ";
  337. $where .= "
  338. AND (
  339. t{$field_id}_{$this->_key}.value = '{$value}'
  340. OR t{$field_id}_{$this->_key}.handle = '{$value}'
  341. )
  342. ";
  343. }
  344. }
  345. else {
  346. if (!is_array($data)) $data = array($data);
  347. foreach ($data as &$value) {
  348. $value = $this->cleanValue($value);
  349. }
  350. $this->_key++;
  351. $data = implode("', '", $data);
  352. $joins .= "
  353. LEFT JOIN
  354. `tbl_entries_data_{$field_id}` AS t{$field_id}_{$this->_key}
  355. ON (e.id = t{$field_id}_{$this->_key}.entry_id)
  356. ";
  357. $where .= "
  358. AND (
  359. t{$field_id}_{$this->_key}.value IN ('{$data}')
  360. OR t{$field_id}_{$this->_key}.handle IN ('{$data}')
  361. )
  362. ";
  363. }
  364. return true;
  365. }
  366. }