PageRenderTime 71ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/html/AppCode/expressionengine/third_party/matrix/ft.matrix.php

https://github.com/w3bg/www.hsifin.com
PHP | 1949 lines | 1193 code | 412 blank | 344 comment | 195 complexity | cb56dc0771b062176465ddeb207b80bb MD5 | raw file
Possible License(s): AGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php if (! defined('BASEPATH')) exit('No direct script access allowed');
  2. if (! defined('MATRIX_VER'))
  3. {
  4. // get the version from config.php
  5. require PATH_THIRD.'matrix/config.php';
  6. define('MATRIX_VER', $config['version']);
  7. }
  8. /**
  9. * Matrix Fieldtype Class for EE2
  10. *
  11. * @package Matrix
  12. * @author Brandon Kelly <brandon@pixelandtonic.com>
  13. * @copyright Copyright (c) 2010 Pixel & Tonic, LLC
  14. */
  15. class Matrix_ft extends EE_Fieldtype {
  16. var $info = array(
  17. 'name' => 'Matrix',
  18. 'version' => MATRIX_VER
  19. );
  20. var $has_array_data = TRUE;
  21. var $bundled_celltypes = array('text', 'date', 'file');
  22. /**
  23. * Fieldtype Constructor
  24. */
  25. function Matrix_ft()
  26. {
  27. parent::EE_Fieldtype();
  28. // -------------------------------------------
  29. // Prepare Cache
  30. // -------------------------------------------
  31. if (! isset($this->EE->session->cache['matrix']))
  32. {
  33. $this->EE->session->cache['matrix'] = array('celltypes' => array());
  34. }
  35. $this->cache =& $this->EE->session->cache['matrix'];
  36. }
  37. // --------------------------------------------------------------------
  38. /**
  39. * Install
  40. */
  41. function install()
  42. {
  43. $this->EE->load->dbforge();
  44. // -------------------------------------------
  45. // Create the exp_matrix_cols table
  46. // -------------------------------------------
  47. if (! $this->EE->db->table_exists('matrix_cols'))
  48. {
  49. $this->EE->dbforge->add_field(array(
  50. 'col_id' => array('type' => 'int', 'constraint' => 6, 'unsigned' => TRUE, 'auto_increment' => TRUE),
  51. 'site_id' => array('type' => 'int', 'constraint' => 4, 'unsigned' => TRUE, 'default' => 1),
  52. 'field_id' => array('type' => 'int', 'constraint' => 6, 'unsigned' => TRUE),
  53. 'col_name' => array('type' => 'varchar', 'constraint' => 32),
  54. 'col_label' => array('type' => 'varchar', 'constraint' => 50),
  55. 'col_instructions' => array('type' => 'text'),
  56. 'col_type' => array('type' => 'varchar', 'constraint' => 50, 'default' => 'text'),
  57. 'col_required' => array('type' => 'char', 'constraint' => 1, 'default' => 'n'),
  58. 'col_search' => array('type' => 'char', 'constraint' => 1, 'default' => 'n'),
  59. 'col_order' => array('type' => 'int', 'constraint' => 3, 'unsigned' => TRUE),
  60. 'col_width' => array('type' => 'varchar', 'constraint' => 4),
  61. 'col_settings' => array('type' => 'text')
  62. ));
  63. $this->EE->dbforge->add_key('col_id', TRUE);
  64. $this->EE->dbforge->add_key('site_id');
  65. $this->EE->dbforge->add_key('field_id');
  66. $this->EE->dbforge->create_table('matrix_cols');
  67. }
  68. // -------------------------------------------
  69. // Create the exp_matrix_data table
  70. // -------------------------------------------
  71. if (! $this->EE->db->table_exists('matrix_data'))
  72. {
  73. $this->EE->dbforge->add_field(array(
  74. 'row_id' => array('type' => 'int', 'constraint' => 10, 'unsigned' => TRUE, 'auto_increment' => TRUE),
  75. 'site_id' => array('type' => 'int', 'constraint' => 4, 'unsigned' => TRUE, 'default' => 1),
  76. 'entry_id' => array('type' => 'int', 'constraint' => 10, 'unsigned' => TRUE),
  77. 'field_id' => array('type' => 'int', 'constraint' => 6, 'unsigned' => TRUE),
  78. 'row_order' => array('type' => 'int', 'constraint' => 4, 'unsigned' => TRUE)
  79. ));
  80. $this->EE->dbforge->add_key('row_id', TRUE);
  81. $this->EE->dbforge->add_key('site_id');
  82. $this->EE->dbforge->add_key('entry_id');
  83. $this->EE->dbforge->add_key('field_id');
  84. $this->EE->dbforge->create_table('matrix_data');
  85. }
  86. // -------------------------------------------
  87. // EE1 Conversion
  88. // -------------------------------------------
  89. if (! class_exists('FF2EE2')) require_once PATH_THIRD.'matrix/includes/ff2ee2/ff2ee2.php';
  90. // FF Matrix 1 conversion
  91. $converter = new FF2EE2(array('ff_matrix', 'matrix'), array(&$this, '_convert_field'));
  92. // Matrix 2 conversion
  93. $converter = new FF2EE2('matrix');
  94. return $converter->global_settings;
  95. }
  96. /**
  97. * Convert Field Settings
  98. *
  99. * @todo - find unique words and add them to the exp_channel_data cell
  100. */
  101. function _convert_field($settings, $field)
  102. {
  103. $settings['col_ids'] = array();
  104. if (isset($settings['cols']))
  105. {
  106. if ($settings['cols'])
  107. {
  108. // -------------------------------------------
  109. // Add the rows to exp_matrix_cols
  110. // -------------------------------------------
  111. $col_ids_by_key = array();
  112. $matrix_data_columns = array();
  113. foreach($settings['cols'] as $col_key => $col)
  114. {
  115. $col_type = $col['type'];
  116. $col_settings = $col['settings'];
  117. switch ($col_type)
  118. {
  119. case 'ff_checkbox':
  120. case 'ff_checkbox_group':
  121. if ($col_type == 'ff_checkbox')
  122. {
  123. $col_settings = array('options' => array('y' => $col_settings['label']));
  124. }
  125. $col_type = 'pt_checkboxes';
  126. break;
  127. case 'ff_select':
  128. $col_type = 'pt_dropdown';
  129. break;
  130. case 'ff_multiselect':
  131. $col_type = 'pt_multiselect';
  132. break;
  133. case 'ff_radio_group':
  134. $col_type = 'pt_radio_buttons';
  135. break;
  136. case 'ff_matrix_text':
  137. case 'ff_matrix_textarea':
  138. $col_settings['multiline'] = ($col_type == 'ff_matrix_text' ? 'n' : 'y');
  139. $col_type = 'text';
  140. break;
  141. case 'ff_matrix_date':
  142. $col_type = 'date';
  143. break;
  144. }
  145. $this->EE->db->insert('matrix_cols', array(
  146. 'site_id' => $field['site_id'],
  147. 'field_id' => $field['field_id'],
  148. 'col_name' => $col['name'],
  149. 'col_label' => $col['label'],
  150. 'col_type' => $col_type,
  151. 'col_search' => $field['field_search'],
  152. 'col_order' => $col_key,
  153. 'col_settings' => base64_encode(serialize($col_settings))
  154. ));
  155. // get the col_id
  156. $this->EE->db->select_max('col_id');
  157. $query = $this->EE->db->get('matrix_cols');
  158. $col_id = $query->row('col_id');
  159. $settings['col_ids'][] = $col_id;
  160. // add it to the matrix_data_columns queue
  161. $matrix_data_columns['col_id_'.$col_id] = array('type' => 'text');
  162. // map the col_id to the col_key for later
  163. $col_ids_by_key[$col_key] = $col_id;
  164. }
  165. // -------------------------------------------
  166. // Add the columns to matrix_data
  167. // -------------------------------------------
  168. $this->EE->dbforge->add_column('matrix_data', $matrix_data_columns);
  169. // -------------------------------------------
  170. // Move the field data into exp_matrix_data
  171. // -------------------------------------------
  172. $field_id = 'field_id_'.$field['field_id'];
  173. $this->EE->db->select('entry_id, '.$field_id);
  174. $this->EE->db->where($field_id.' !=', '');
  175. $entries = $this->EE->db->get('channel_data');
  176. foreach($entries->result_array() as $entry)
  177. {
  178. // unserialize the data
  179. $old_data = FF2EE2::_unserialize($entry[$field_id]);
  180. foreach ($old_data as $row_count => $row)
  181. {
  182. $data = array(
  183. 'site_id' => $field['site_id'],
  184. 'entry_id' => $entry['entry_id'],
  185. 'field_id' => $field['field_id'],
  186. 'row_order' => $row_count+1
  187. );
  188. foreach ($row as $col_key => $cell_data)
  189. {
  190. // does this col exist?
  191. if (! isset($col_ids_by_key[$col_key])) continue;
  192. // get the col_id
  193. $col_id = $col_ids_by_key[$col_key];
  194. // flatten the cell data if necessary
  195. $cell_data = $this->_flatten_data($cell_data);
  196. // queue it up
  197. $data['col_id_'.$col_id] = $cell_data;
  198. }
  199. // add the row to exp_matrix_data
  200. $this->EE->db->insert('matrix_data', $data);
  201. }
  202. // clear out the old field data from exp_channel_data
  203. $new_data = $this->_flatten_data($old_data);
  204. $this->EE->db->where('entry_id', $entry['entry_id']);
  205. $this->EE->db->update('channel_data', array($field_id => $new_data));
  206. }
  207. }
  208. // -------------------------------------------
  209. // Remove 'cols' from field settings
  210. // -------------------------------------------
  211. unset($settings['cols']);
  212. }
  213. return $settings;
  214. }
  215. // --------------------------------------------------------------------
  216. /**
  217. * Theme URL
  218. */
  219. private function _theme_url()
  220. {
  221. if (! isset($this->cache['theme_url']))
  222. {
  223. $theme_folder_url = $this->EE->config->item('theme_folder_url');
  224. if (substr($theme_folder_url, -1) != '/') $theme_folder_url .= '/';
  225. $this->cache['theme_url'] = $theme_folder_url.'third_party/matrix/';
  226. }
  227. return $this->cache['theme_url'];
  228. }
  229. /**
  230. * Include Theme CSS
  231. */
  232. private function _include_theme_css($file)
  233. {
  234. $this->EE->cp->add_to_head('<link rel="stylesheet" type="text/css" href="'.$this->_theme_url().$file.'" />');
  235. }
  236. /**
  237. * Include Theme JS
  238. */
  239. private function _include_theme_js($file)
  240. {
  241. $this->EE->cp->add_to_foot('<script type="text/javascript" src="'.$this->_theme_url().$file.'"></script>');
  242. }
  243. // --------------------------------------------------------------------
  244. /**
  245. * Insert CSS
  246. */
  247. private function _insert_css($css)
  248. {
  249. $this->EE->cp->add_to_head('<style type="text/css">'.$css.'</style>');
  250. }
  251. /**
  252. * Insert JS
  253. */
  254. private function _insert_js($js)
  255. {
  256. $this->EE->cp->add_to_foot('<script type="text/javascript">'.$js.'</script>');
  257. }
  258. // --------------------------------------------------------------------
  259. /**
  260. * Prepare Params
  261. */
  262. private function _prep_params(&$params)
  263. {
  264. $params = array_merge(array(
  265. 'cellspacing' => '1',
  266. 'cellpadding' => '10',
  267. 'dynamic_parameters' => '',
  268. 'row_id' => '',
  269. 'orderby' => '',
  270. 'sort' => 'asc',
  271. 'offset' => '',
  272. 'limit' => '',
  273. 'backspace' => ''
  274. ), $params);
  275. }
  276. // --------------------------------------------------------------------
  277. /**
  278. * Display Global Settings
  279. */
  280. function display_global_settings()
  281. {
  282. $license_key = isset($this->settings['license_key']) ? $this->settings['license_key'] : '';
  283. // load the language file
  284. $this->EE->lang->loadfile('matrix');
  285. // load the table lib
  286. $this->EE->load->library('table');
  287. // use the default template known as
  288. // $cp_pad_table_template in the views
  289. $this->EE->table->set_template(array(
  290. 'table_open' => '<table class="mainTable padTable" border="0" cellspacing="0" cellpadding="0">',
  291. 'row_start' => '<tr class="even">',
  292. 'row_alt_start' => '<tr class="odd">'
  293. ));
  294. $this->EE->table->set_heading(array('data' => lang('preference'), 'style' => 'width: 50%'), lang('setting'));
  295. $this->EE->table->add_row(
  296. lang('license_key', 'license_key'),
  297. form_input('license_key', $license_key, 'id="license_key" size="40"')
  298. );
  299. return $this->EE->table->generate();
  300. }
  301. /**
  302. * Save Global Settings
  303. */
  304. function save_global_settings()
  305. {
  306. return array(
  307. 'license_key' => isset($_POST['license_key']) ? $_POST['license_key'] : ''
  308. );
  309. }
  310. // --------------------------------------------------------------------
  311. /**
  312. * Get Field Cols
  313. */
  314. private function _get_field_cols($col_ids)
  315. {
  316. if (! $col_ids) return FALSE;
  317. $site_id = $this->EE->config->item('site_id');
  318. $this->EE->db->select('col_id, col_type, col_label, col_name, col_instructions, col_width, col_required, col_search, col_settings');
  319. //$this->EE->db->where('site_id', $site_id);
  320. $this->EE->db->where_in('col_id', $col_ids);
  321. $this->EE->db->order_by('col_order');
  322. $cols = $this->EE->db->get('matrix_cols')->result_array();
  323. // unserialize the settings
  324. foreach ($cols as &$col)
  325. {
  326. $col['col_settings'] = unserialize(base64_decode($col['col_settings']));
  327. if (! is_array($col['col_settings'])) $col['col_settings'] = array();
  328. }
  329. return $cols;
  330. }
  331. // --------------------------------------------------------------------
  332. /**
  333. * Get Celltype Class
  334. */
  335. private function _get_celltype_class($name, $text_fallback = FALSE)
  336. {
  337. // $name should look like exp_fieldtypes.name values
  338. if (substr($name, -3) == '_ft') $name = substr($name, 0, -3);
  339. $name = strtolower($name);
  340. // is this a bundled celltype?
  341. if (in_array($name, $this->bundled_celltypes))
  342. {
  343. $class = 'Matrix_'.$name.'_ft';
  344. if (! class_exists($class))
  345. {
  346. // load it from matrix/celltypes/
  347. require_once PATH_THIRD.'matrix/celltypes/'.$name.EXT;
  348. }
  349. }
  350. else
  351. {
  352. $class = ucfirst($name).'_ft';
  353. $this->EE->api_channel_fields->include_handler($name);
  354. }
  355. if (class_exists($class))
  356. {
  357. // method_exists() is supposed to accept the class name (string),
  358. // but running into at least one server where that's not the case...
  359. $ft = new $class();
  360. if (method_exists($ft, 'display_cell'))
  361. {
  362. if (! isset($this->cache['celltype_global_settings'][$name]))
  363. {
  364. $this->EE->db->select('settings');
  365. $this->EE->db->where('name', $name);
  366. $query = $this->EE->db->get('fieldtypes');
  367. $settings = $query->row('settings');
  368. $this->cache['celltype_global_settings'][$name] = is_array($settings) ? $settings : unserialize(base64_decode($settings));
  369. }
  370. return $class;
  371. }
  372. }
  373. return $text_fallback ? $this->_get_celltype_class('text') : FALSE;
  374. }
  375. // --------------------------------------------------------------------
  376. /**
  377. * Get Celltype
  378. */
  379. private function _get_celltype($name, $text_fallback = FALSE)
  380. {
  381. $class = $this->_get_celltype_class($name, $text_fallback);
  382. if (! $class) return FALSE;
  383. $celltype = new $class();
  384. $global_settings = $this->cache['celltype_global_settings'][$name];
  385. $celltype->settings = $global_settings && is_array($global_settings) ? $global_settings : array();
  386. return $celltype;
  387. }
  388. // --------------------------------------------------------------------
  389. /**
  390. * Get All Celltypes
  391. */
  392. private function _get_all_celltypes()
  393. {
  394. // this is only called once, from display_settings(),
  395. // so don't worry about caching the results
  396. // begin with what we already know about
  397. $ft_names = array_merge($this->bundled_celltypes);
  398. // get the fieldtypes from exp_fieldtypes
  399. $this->EE->db->select('name, settings');
  400. $query = $this->EE->db->get('fieldtypes');
  401. if (! isset($this->cache['celltype_global_settings']))
  402. {
  403. $this->cache['celltype_global_settings'] = array();
  404. }
  405. foreach($query->result_array() as $ft)
  406. {
  407. $ft_names[] = $ft['name'];
  408. $this->cache['celltype_global_settings'][$ft['name']] = unserialize(base64_decode($ft['settings']));
  409. }
  410. // now get the actual celltype instances
  411. $celltypes = array();
  412. foreach($ft_names as $name)
  413. {
  414. if (($ct = $this->_get_celltype($name)) !== FALSE)
  415. {
  416. $celltypes[$name] = $ct;
  417. }
  418. }
  419. return $celltypes;
  420. }
  421. // --------------------------------------------------------------------
  422. /**
  423. * Namespace Settings
  424. */
  425. function _namespace_settings(&$settings, $namespace)
  426. {
  427. $settings = preg_replace('/(name=([\'\"]))([^\'"\[\]]+)([^\'"]*)(\2)/i', '$1'.$namespace.'[$3]$4$5', $settings);
  428. }
  429. // --------------------------------------------------------------------
  430. /**
  431. * Celltype Settings HTML
  432. */
  433. private function _celltype_settings_html($namespace, $celltype, $data = array())
  434. {
  435. if (method_exists($celltype, 'display_cell_settings'))
  436. {
  437. $returned = $celltype->display_cell_settings($data);
  438. // should we create the html for them?
  439. if (is_array($returned))
  440. {
  441. $r = '<table class="matrix-col-settings" cellspacing="0" cellpadding="0" border="0">';
  442. $total_cell_settings = count($returned);
  443. foreach($returned as $cs_key => $cell_setting)
  444. {
  445. $tr_class = '';
  446. if ($cs_key == 0) $tr_class .= ' matrix-first';
  447. if ($cs_key == $total_cell_settings-1) $tr_class .= ' matrix-last';
  448. $r .= '<tr class="'.$tr_class.'">'
  449. . '<th class="matrix-first">'.$cell_setting[0].'</th>'
  450. . '<td class="matrix-last">'.$cell_setting[1].'</td>'
  451. . '</tr>';
  452. }
  453. $r .= '</table>';
  454. }
  455. else
  456. {
  457. $r = $returned;
  458. }
  459. $this->_namespace_settings($r, $namespace);
  460. }
  461. else
  462. {
  463. $r = '';
  464. }
  465. return $r;
  466. }
  467. // --------------------------------------------------------------------
  468. /**
  469. * Display Field Settings
  470. */
  471. function display_settings($data)
  472. {
  473. $max_rows = isset($data['max_rows']) ? $data['max_rows'] : '';
  474. $col_ids = isset($data['col_ids']) ? $data['col_ids'] : array();
  475. // include css and js
  476. $this->_include_theme_css('styles/matrix.css');
  477. $this->_include_theme_js('scripts/matrix.js');
  478. $this->_include_theme_js('scripts/matrix_text.js');
  479. $this->_include_theme_js('scripts/matrix_conf.js');
  480. // load the language file
  481. $this->EE->lang->loadfile('matrix');
  482. // -------------------------------------------
  483. // Get the celltypes
  484. // -------------------------------------------
  485. $celltypes = $this->_get_all_celltypes();
  486. $celltypes_select_options = array();
  487. $celltypes_js = array();
  488. foreach ($celltypes as $name => $celltype)
  489. {
  490. $celltypes_select_options[$name] = $celltype->info['name'];
  491. // default cell settings
  492. $celltypes_js[$name] = $this->_celltype_settings_html('matrix[cols][{COL_ID}][settings]', $celltype, $data);
  493. }
  494. // -------------------------------------------
  495. // Get the columns
  496. // -------------------------------------------
  497. // is this an existing field?
  498. if ($data['field_id'] && $col_ids)
  499. {
  500. $cols = $this->_get_field_cols($col_ids);
  501. $new = FALSE;
  502. }
  503. if (! isset($cols) || ! $cols)
  504. {
  505. $new = TRUE;
  506. // start off with a couple text cells
  507. $cols = array(
  508. array('col_id' => '0', 'col_label' => 'Cell 1', 'col_instructions' => '', 'col_name' => 'cell_1', 'col_type' => 'text', 'col_width' => '33%', 'col_required' => 'n', 'col_search' => 'n', 'col_settings' => array('maxl' => '', 'multiline' => 'n')),
  509. array('col_id' => '1', 'col_label' => 'Cell 2', 'col_instructions' => '', 'col_name' => 'cell_2', 'col_type' => 'text', 'col_width' => '', 'col_required' => 'n', 'col_search' => 'n', 'col_settings' => array('maxl' => '140', 'multiline' => 'y'))
  510. );
  511. }
  512. $cols_js = array();
  513. foreach ($cols as $col)
  514. {
  515. $cols_js[] = array(
  516. 'id' => ($new ? 'col_new_' : 'col_id_') . $col['col_id'],
  517. 'type' => $col['col_type']
  518. );
  519. }
  520. // -------------------------------------------
  521. // Max Rows
  522. // -------------------------------------------
  523. $this->EE->table->add_row(
  524. lang('max_rows', 'matrix_max_rows'),
  525. form_input('matrix[max_rows]', $max_rows, 'id="matrix_max_rows" style="width: 3em;"')
  526. );
  527. // -------------------------------------------
  528. // Matrix Configuration
  529. // -------------------------------------------
  530. $total_cols = count($cols);
  531. $table = '<div id="matrix-conf-container"><div id="matrix-conf">'
  532. . '<table class="matrix matrix-conf" cellspacing="0" cellpadding="0" border="0" style="background: #ecf1f4;">'
  533. . '<thead class="matrix">'
  534. . '<tr class="matrix matrix-first">'
  535. . '<td class="matrix-breakleft"></td>';
  536. // -------------------------------------------
  537. // Labels
  538. // -------------------------------------------
  539. foreach ($cols as $col_index => $col)
  540. {
  541. $col_id = $new ? 'col_new_'.$col_index : 'col_id_'.$col['col_id'];
  542. $class = 'matrix';
  543. if ($col_index == 0) $class .= ' matrix-first';
  544. if ($col_index == $total_cols - 1) $class .= ' matrix-last';
  545. $table .= '<th class="'.$class.'" scope="col">'
  546. . '<input type="hidden" name="matrix[col_order][]" value="'.$col_id.'" />'
  547. . '<span>'.$col['col_label'].'</span>'
  548. . '</th>';
  549. }
  550. $table .= '</tr>'
  551. . '<tr class="matrix matrix-last">'
  552. . '<td class="matrix-breakleft"></td>';
  553. // -------------------------------------------
  554. // Instructions
  555. // -------------------------------------------
  556. foreach ($cols as $col_index => $col)
  557. {
  558. $class = 'matrix';
  559. if ($col_index == 0) $class .= ' matrix-first';
  560. if ($col_index == $total_cols - 1) $class .= ' matrix-last';
  561. $table .= '<td class="'.$class.'">'.($col['col_instructions'] ? nl2br($col['col_instructions']) : '&nbsp;').'</td>';
  562. }
  563. $table .= '</tr>'
  564. . '</thead>'
  565. . '<tbody class="matrix">';
  566. // -------------------------------------------
  567. // Col Settings
  568. // -------------------------------------------
  569. $col_settings = array('type', 'label', 'name', 'instructions', 'width', 'search', 'settings');
  570. $total_settings = count($col_settings);
  571. foreach ($col_settings as $row_index => $col_setting)
  572. {
  573. $tr_class = 'matrix';
  574. if ($row_index == 0) $tr_class .= ' matrix-first';
  575. if ($row_index == $total_settings - 1) $tr_class .= ' matrix-last';
  576. $table .= '<tr class="'.$tr_class.'">'
  577. . '<th class="matrix-breakleft" scope="row">'.lang('col_'.$col_setting).'</th>';
  578. foreach ($cols as $col_index => $col)
  579. {
  580. $col_id = $new ? 'col_new_'.$col_index : 'col_id_'.$col['col_id'];
  581. $setting_name = 'matrix[cols]['.$col_id.']['.$col_setting.']';
  582. $td_class = 'matrix';
  583. if ($col_index == 0) $td_class .= ' matrix-first';
  584. if ($col_index == $total_cols - 1) $td_class .= ' matrix-last';
  585. switch ($col_setting)
  586. {
  587. case 'type':
  588. $shtml = form_dropdown($setting_name, $celltypes_select_options, $col['col_'.$col_setting]);
  589. break;
  590. case 'name':
  591. case 'width':
  592. $td_class .= ' matrix-text';
  593. $shtml = form_input($setting_name, $col['col_'.$col_setting], 'class="matrix-textarea"');
  594. break;
  595. case 'required':
  596. case 'search':
  597. $shtml = form_checkbox($setting_name, 'y', ($col['col_'.$col_setting] == 'y'));
  598. break;
  599. case 'settings':
  600. $cell_data = array_merge($data, is_array($col['col_'.$col_setting]) ? $col['col_'.$col_setting] : array());
  601. if (! ($shtml = $this->_celltype_settings_html($setting_name, $celltypes[$col['col_type']], $cell_data)))
  602. {
  603. $td_class .= ' matrix-disabled';
  604. $shtml = '&nbsp;';
  605. }
  606. break;
  607. default:
  608. $td_class .= ' matrix-text';
  609. $shtml = '<textarea class="matrix-textarea" name="'.$setting_name.'" rows="1">'.$col['col_'.$col_setting].'</textarea>';
  610. }
  611. $table .= '<td class="'.$td_class.'">'.$shtml.'</td>';
  612. }
  613. $table .= '</tr>';
  614. }
  615. // -------------------------------------------
  616. // Delete Row buttons
  617. // -------------------------------------------
  618. $table .= '<tr>'
  619. . '<td class="matrix-breakleft"></td>';
  620. foreach ($cols as $col)
  621. {
  622. $table .= '<td class="matrix-breakdown"><a class="matrix-btn" title="'.lang('remove_column').'"></a></td>';
  623. }
  624. $table .= '</tr>'
  625. . '</tbody>'
  626. . '</table>'
  627. . '<a class="matrix-btn matrix-add" title="'.lang('add_column').'"></a>'
  628. . '</div></div>';
  629. $this->EE->table->add_row(array(
  630. 'colspan' => '2',
  631. 'data' => lang('matrix_configuration', 'matrix_configuration')
  632. . $table
  633. ));
  634. // -------------------------------------------
  635. // Initialize the configurator js
  636. // -------------------------------------------
  637. $js = 'MatrixConf.EE2 = true;' . NL
  638. . 'var m = new MatrixConf("matrix", '
  639. . $this->EE->javascript->generate_json($celltypes_js, TRUE) . ', '
  640. . $this->EE->javascript->generate_json($cols_js, TRUE) . ', '
  641. . $this->EE->javascript->generate_json($col_settings, TRUE)
  642. . ');';
  643. if ($new) $js .= NL.'m.totalNewCols = 2;';
  644. $this->_insert_js($js);
  645. }
  646. /**
  647. * Save Field Settings
  648. */
  649. function save_settings($data)
  650. {
  651. $post = $this->EE->input->post('matrix');
  652. // -------------------------------------------
  653. // Delete any removed columns
  654. // -------------------------------------------
  655. if (isset($post['deleted_cols']))
  656. {
  657. $this->EE->load->dbforge();
  658. foreach($post['deleted_cols'] as $col_id)
  659. {
  660. $col_id = substr($col_id, 7);
  661. // delete the rows from exp_matrix_cols
  662. $this->EE->db->where('col_id', $col_id);
  663. $this->EE->db->delete('matrix_cols');
  664. // delete the actual column from exp_matrix_data
  665. $this->EE->dbforge->drop_column('matrix_data', 'col_id_'.$col_id);
  666. }
  667. }
  668. // -------------------------------------------
  669. // Add/update columns
  670. // -------------------------------------------
  671. $settings = array(
  672. 'max_rows' => (isset($post['max_rows']) && $post['max_rows'] ? $post['max_rows'] : ''),
  673. 'col_ids' => array()
  674. );
  675. $matrix_data_columns = array();
  676. foreach ($post['col_order'] as $col_order => $col_id)
  677. {
  678. $col = $post['cols'][$col_id];
  679. $cell_settings = isset($col['settings']) ? $col['settings'] : array();
  680. // give the celltype a chance to override
  681. $celltype = $this->_get_celltype($col['type']);
  682. if (method_exists($celltype, 'save_cell_settings'))
  683. {
  684. $cell_settings = $celltype->save_cell_settings($cell_settings);
  685. }
  686. $col_data = array(
  687. 'col_name' => $col['name'],
  688. 'col_label' => str_replace('$', '&#36;', $col['label']),
  689. 'col_instructions' => str_replace('$', '&#36;', $col['instructions']),
  690. 'col_type' => $col['type'],
  691. 'col_required' => (isset($col['required']) && $col['required'] ? 'y' : 'n'),
  692. 'col_search' => (isset($col['search']) && $col['search'] ? 'y' : 'n'),
  693. 'col_width' => $col['width'],
  694. 'col_order' => $col_order,
  695. 'col_settings' => base64_encode(serialize($cell_settings))
  696. );
  697. $new = (substr($col_id, 0, 8) == 'col_new_');
  698. if ($new)
  699. {
  700. $col_data['site_id'] = $this->EE->config->item('site_id');
  701. // insert the row
  702. $this->EE->db->insert('matrix_cols', $col_data);
  703. // get & save the col-id
  704. $this->EE->db->select_max('col_id');
  705. $query = $this->EE->db->get('matrix_cols');
  706. $col_id = $query->row('col_id');
  707. // add it to the matrix_data_columns queue
  708. $matrix_data_columns['col_id_'.$col_id] = array('type' => 'text');
  709. }
  710. else
  711. {
  712. $col_id = substr($col_id, 7);
  713. // just update the existing row
  714. $this->EE->db->where('col_id', $col_id);
  715. $this->EE->db->update('matrix_cols', $col_data);
  716. }
  717. // add the col_id to the field settings
  718. // - it's unfortunate that we can't just place the field_id in the matrix_cols
  719. // data, but alas, the future field_id is unknowable on new fields
  720. $settings['col_ids'][] = $col_id;
  721. }
  722. // add the new columns to exp_matrix_data
  723. if ($matrix_data_columns)
  724. {
  725. $this->EE->load->dbforge();
  726. $this->EE->dbforge->add_column('matrix_data', $matrix_data_columns);
  727. }
  728. // cross the T's
  729. $settings['field_fmt'] = 'none';
  730. $settings['field_show_fmt'] = 'n';
  731. $settings['field_type'] = 'matrix';
  732. return $settings;
  733. }
  734. // --------------------------------------------------------------------
  735. /**
  736. * Display Field
  737. */
  738. function display_field($data)
  739. {
  740. $max_rows = isset($this->settings['max_rows']) ? $this->settings['max_rows'] : FALSE;
  741. $col_ids = isset($this->settings['col_ids']) ? $this->settings['col_ids'] : FALSE;
  742. if (! $col_ids) return;
  743. // -------------------------------------------
  744. // Include dependencies
  745. // - this needs to happen *before* we load the celltypes,
  746. // in case the celltypes are loading their own JS
  747. // -------------------------------------------
  748. if (! isset($this->cache['included_dependencies']))
  749. {
  750. // load the language file
  751. $this->EE->lang->loadfile('matrix');
  752. // include css and js
  753. $this->_include_theme_css('styles/matrix.css');
  754. $this->_include_theme_js('scripts/matrix.js');
  755. // menu language
  756. $this->_insert_js('Matrix.lang = { '
  757. . 'add_row_above: "'.lang('add_row_above').'", '
  758. . 'add_row_below: "'.lang('add_row_below').'", '
  759. . 'remove_row: "'.lang('remove_row').'", '
  760. . 'remove_file: "'.lang('remove_file').'" };');
  761. $this->cache['included_dependencies'] = TRUE;
  762. }
  763. // -------------------------------------------
  764. // Get the columns
  765. // -------------------------------------------
  766. $cols = $this->_get_field_cols($col_ids);
  767. $total_cols = count($cols);
  768. if (! $total_cols) return;
  769. $col_settings = array();
  770. $select_col_ids = '';
  771. $show_instructions = FALSE;
  772. $cols_js = array();
  773. foreach($cols as $col)
  774. {
  775. // index the col by ID
  776. $select_col_ids .= ', col_id_'.$col['col_id'];
  777. // show instructions?
  778. if ($col['col_instructions']) $show_instructions = TRUE;
  779. // include this->settings in col settings
  780. $col_settings[$col['col_id']] = array_merge($this->settings, (is_array($col['col_settings']) ? $col['col_settings'] : array()));
  781. $celltype = $this->_get_celltype($col['col_type']);
  782. $celltype->settings = array_merge($celltype->settings, $col_settings[$col['col_id']]);
  783. $celltype->field_id = $this->field_id;
  784. $celltype->field_name = $this->field_name;
  785. $celltype->col_id = $col['col_id'];
  786. $celltype->cell_name = '{DEFAULT}';
  787. $new_cell_html = $celltype->display_cell('');
  788. $new_cell_settings = FALSE;
  789. $new_cell_class = FALSE;
  790. if (is_array($new_cell_html))
  791. {
  792. if (isset($new_cell_html['settings']))
  793. {
  794. $new_cell_settings = $new_cell_html['settings'];
  795. }
  796. if (isset($new_cell_html['class']))
  797. {
  798. $new_cell_class = $new_cell_html['class'];
  799. }
  800. $new_cell_html = $new_cell_html['data'];
  801. }
  802. // store the js-relevant stuff in $cols_js
  803. $cols_js[] = array(
  804. 'id' => 'col_id_'.$col['col_id'],
  805. 'name' => $col['col_name'],
  806. 'label' => $col['col_label'],
  807. 'required' => ($col['col_required'] == 'y' ? TRUE : FALSE),
  808. 'settings' => $col['col_settings'],
  809. 'type' => $col['col_type'],
  810. 'newCellHtml' => $new_cell_html,
  811. 'newCellSettings' => $new_cell_settings,
  812. 'newCellClass' => $new_cell_class
  813. );
  814. }
  815. // -------------------------------------------
  816. // Get the data
  817. // -------------------------------------------
  818. // autosave data?
  819. if (is_array($data) && isset($data['row_order']))
  820. {
  821. unset($data['row_order']);
  822. foreach ($data as $row_id => &$row)
  823. {
  824. if (substr($row_id, 0, 7) == 'row_id_')
  825. {
  826. $row['row_id'] = substr($row_id, 7);
  827. }
  828. }
  829. }
  830. else
  831. {
  832. $data = array();
  833. // is there post data?
  834. if (isset($_POST[$this->field_name]) && isset($_POST[$this->field_name]['row_order']) && $_POST[$this->field_name]['row_order'])
  835. {
  836. foreach ($_POST[$this->field_name]['row_order'] as $row_id)
  837. {
  838. $row = isset($_POST[$this->field_name][$row_id]) ? $_POST[$this->field_name][$row_id] : array();
  839. foreach ($cols as $col)
  840. {
  841. $data[$row_id]['col_id_'.$col['col_id']] = isset($row['col_id_'.$col['col_id']]) ? $row['col_id_'.$col['col_id']] : '';
  842. }
  843. }
  844. }
  845. else
  846. {
  847. // is this an existing entry?
  848. $entry_id = $this->EE->input->get('entry_id');
  849. if ($entry_id)
  850. {
  851. $this->EE->db->select('row_id' . $select_col_ids);
  852. $this->EE->db->where('site_id', $this->EE->config->item('site_id'));
  853. $this->EE->db->where('field_id', $this->field_id);
  854. $this->EE->db->where('entry_id', $entry_id);
  855. $this->EE->db->order_by('row_order');
  856. $query = $this->EE->db->get('matrix_data')->result_array();
  857. // is this a clone?
  858. $clone = ($this->EE->input->get('clone') == 'y');
  859. // re-index the query data
  860. foreach ($query as $count => $row)
  861. {
  862. $key = $clone ? 'row_new_'.$count : 'row_id_'.$row['row_id'];
  863. $data[$key] = $row;
  864. }
  865. }
  866. if (! $entry_id || ! $data)
  867. {
  868. foreach ($cols as $col)
  869. {
  870. $data['row_new_0']['col_id_'.$col['col_id']] = '';
  871. }
  872. }
  873. }
  874. }
  875. $total_rows = count($data);
  876. // -------------------------------------------
  877. // Table Head
  878. // -------------------------------------------
  879. $thead = '<thead class="matrix">';
  880. $headings = '';
  881. $instructions = '';
  882. // add left gutters if there can be more than one row
  883. if ($max_rows != '1')
  884. {
  885. $headings .= '<th class="matrix matrix-first"></th>';
  886. if ($show_instructions)
  887. {
  888. $instructions .= '<td class="matrix matrix-first"></td>';
  889. }
  890. }
  891. // add the labels and instructions
  892. foreach ($cols as $col_index => $col)
  893. {
  894. $count = $col_index + 1;
  895. $class = 'matrix';
  896. if ($max_rows == '1' && $count == 1) $class .= ' matrix-first';
  897. if ($count == $total_cols) $class .= ' matrix-last';
  898. $width = $col['col_width'] ? ' width="'.$col['col_width'].'"' : '';
  899. $headings .= '<th class="'.$class.'" scope="col"'.$width.'>'.$col['col_label'].'</th>';
  900. if ($show_instructions)
  901. {
  902. $instructions .= '<td class="'.$class.'">'.nl2br($col['col_instructions']).'</td>';
  903. }
  904. }
  905. $thead = '<thead class="matrix">'
  906. . '<tr class="matrix matrix-first'.($show_instructions ? '' : ' matrix-last').'">' . $headings . '</tr>'
  907. . ($show_instructions ? '<tr class="matrix matrix-last">' . $instructions . '</tr>' : '')
  908. . '</thead>';
  909. // -------------------------------------------
  910. // Table Body
  911. // -------------------------------------------
  912. $rows_js = array();
  913. $tbody = '<tbody class="matrix">';
  914. $row_count = 0;
  915. $total_new_rows = 0;
  916. foreach ($data as $row_id => &$row)
  917. {
  918. $row_count ++;
  919. // new?
  920. $new = (substr($row_id, 0, 8) == 'row_new_');
  921. if ($new) $total_new_rows ++;
  922. $row_js = array('id' => $row_id, 'cellSettings' => array());
  923. $tr_class = 'matrix';
  924. if ($row_count == 1) $tr_class .= ' matrix-first';
  925. if ($row_count == $total_rows) $tr_class .= ' matrix-last';
  926. $tbody .= '<tr class="'.$tr_class.'">';
  927. // add left heading if there can be more than one row
  928. if ($max_rows != '1')
  929. {
  930. $tbody .= '<th class="matrix matrix-first">'
  931. . '<div><span>'.$row_count.'</span><a title="'.lang('options').'"></a></div>'
  932. . '<input type="hidden" name="'.$this->field_name.'[row_order][]" value="'.$row_id.'" />'
  933. . '</th>';
  934. }
  935. // add the cell data
  936. foreach ($cols as $col_index => &$col)
  937. {
  938. $col_id = 'col_id_'.$col['col_id'];
  939. $col_count = $col_index + 1;
  940. $td_class = 'matrix';
  941. // is this the first data cell?
  942. if ($col_count == 1)
  943. {
  944. // is this also the first cell in the <tr>?
  945. if ($max_rows == '1') $td_class .= ' matrix-first';
  946. // use .matrix-firstcell for active state
  947. $td_class .= ' matrix-firstcell';
  948. }
  949. if ($col_count == $total_cols) $td_class .= ' matrix-last';
  950. // get new instance of this celltype
  951. $celltype = $this->_get_celltype($col['col_type']);
  952. $cell_name = $this->field_name.'['.$row_id.']['.$col_id.']';
  953. $cell_data = $row['col_id_'.$col['col_id']];
  954. // fill it up with crap
  955. $celltype->settings = array_merge($celltype->settings, $col_settings[$col['col_id']]);
  956. if (isset($row['row_id'])) $celltype->row_id = $row['row_id'];
  957. $celltype->field_id = $this->field_id;
  958. $celltype->field_name = $this->field_name;
  959. $celltype->col_id = $col['col_id'];
  960. $celltype->cell_name = $cell_name;
  961. // get the cell html
  962. $cell_html = $celltype->display_cell($cell_data);
  963. // is the celltype sending settings too?
  964. if (is_array($cell_html))
  965. {
  966. if (isset($cell_html['settings']))
  967. {
  968. $row_js['cellSettings'][$col_id] = $cell_html['settings'];
  969. }
  970. if (isset($cell_html['class']))
  971. {
  972. $td_class .= ' '.$cell_html['class'];
  973. }
  974. $cell_html = $cell_html['data'];
  975. }
  976. $tbody .= '<td class="'.$td_class.'">'.$cell_html.'</td>';
  977. }
  978. $tbody .= '</tr>';
  979. $rows_js[] = $row_js;
  980. }
  981. $tbody .= '</tbody>';
  982. // -------------------------------------------
  983. // Plug it all together
  984. // -------------------------------------------
  985. $margins = version_compare(APP_VER, '2.0.2', '<') ? '5px 10px 0 12px' : '5px 4px 0 0';
  986. $r = '<div id="'.$this->field_name.'" class="matrix" style="margin: '.$margins.'">'
  987. . '<table class="matrix" cellspacing="0" cellpadding="0" border="0">'
  988. . $thead
  989. . $tbody
  990. . '</table>';
  991. if ($max_rows == 1)
  992. {
  993. $r .= '<input type="hidden" name="'.$this->field_name.'[row_order][]" value="'.$rows_js[0]['id'].'" />';
  994. }
  995. else
  996. {
  997. $r .= '<a class="matrix-btn matrix-add'.($max_rows == count($data) ? ' matrix-btn-disabled' : '').'" title="'.lang('add_row').'"></a>';
  998. }
  999. $r .= '</div>';
  1000. // initialize the field js
  1001. $js = 'jQuery(document).ready(function(){'
  1002. . 'var m = new Matrix("'.$this->field_name . '", "'
  1003. . $this->settings['field_label'] . '", '
  1004. . $this->EE->javascript->generate_json($cols_js, TRUE) . ', '
  1005. . $this->EE->javascript->generate_json($rows_js, TRUE) . ($max_rows ? ', '.$max_rows : '')
  1006. . ');' . NL
  1007. . 'm.totalNewRows = '.$total_new_rows.';'
  1008. . '});';
  1009. $this->_insert_js($js);
  1010. return $r;
  1011. }
  1012. // --------------------------------------------------------------------
  1013. /**
  1014. * Flatten Data
  1015. */
  1016. private function _flatten_data($data)
  1017. {
  1018. $r = array();
  1019. if (is_array($data))
  1020. {
  1021. foreach ($data as $val)
  1022. {
  1023. $r[] = $this->_flatten_data($val);
  1024. }
  1025. }
  1026. else
  1027. {
  1028. $r[] = $data;
  1029. }
  1030. return implode(NL, array_filter($r));
  1031. }
  1032. // --------------------------------------------------------------------
  1033. /**
  1034. * Save
  1035. */
  1036. function save($data)
  1037. {
  1038. // ignore if no post data
  1039. if (! $data) return;
  1040. // -------------------------------------------
  1041. // Get the cols
  1042. // -------------------------------------------
  1043. $col_ids = isset($this->settings['col_ids']) ? $this->settings['col_ids'] : FALSE;
  1044. $cols = $this->_get_field_cols($col_ids);
  1045. if (! $cols) return;
  1046. // save the post data for later
  1047. $this->cache['saved'][$this->settings['field_id']] = array(
  1048. 'data' => $data,
  1049. 'cols' => $cols
  1050. );
  1051. $data_exists = FALSE;
  1052. $r = array();
  1053. foreach ($data['row_order'] as $row_order => $row_id)
  1054. {
  1055. if (! isset($data[$row_id])) continue;
  1056. $row = $data[$row_id];
  1057. foreach ($cols as $col)
  1058. {
  1059. $cell_data = isset($row['col_id_'.$col['col_id']]) ? $row['col_id_'.$col['col_id']] : '';
  1060. if ($cell_data || $cell_data === '0')
  1061. {
  1062. $data_exists = TRUE;
  1063. // searchable?
  1064. if ($col['col_search'] == 'y')
  1065. {
  1066. $r[] = $this->_flatten_data($cell_data).NL;
  1067. }
  1068. }
  1069. }
  1070. }
  1071. // return a flattened string of all the searchable
  1072. // columns' rows for searchability's sake
  1073. $r = $this->_flatten_data($r);
  1074. return $data_exists ? ($r ? $r : '1') : '';
  1075. }
  1076. /**
  1077. * Post Save
  1078. */
  1079. function post_save($data)
  1080. {
  1081. if (! isset($this->cache['saved'][$this->settings['field_id']])) return;
  1082. // get the data from the cache
  1083. $data = $this->cache['saved'][$this->settings['field_id']]['data'];
  1084. $delete_rows = isset($data['deleted_rows']) ? $data['deleted_rows'] : array();
  1085. // -------------------------------------------
  1086. // Get the cols
  1087. // -------------------------------------------
  1088. $cols = $this->cache['saved'][$this->settings['field_id']]['cols'];
  1089. $col_settings = array();
  1090. foreach ($cols as $col)
  1091. {
  1092. $col_settings[$col['col_id']] = array_merge($this->settings, (is_array($col['col_settings']) ? $col['col_settings'] : array()));
  1093. }
  1094. // -------------------------------------------
  1095. // Add/update rows
  1096. // -------------------------------------------
  1097. foreach ($data['row_order'] as $row_order => $row_id)
  1098. {
  1099. if (! isset($data[$row_id])) continue;
  1100. $row = $data[$row_id];
  1101. $new = (substr($row_id, 0, 8) == 'row_new_');
  1102. $save_row = FALSE;
  1103. $row_data = array(
  1104. 'row_order' => $row_order
  1105. );
  1106. foreach ($cols as $col)
  1107. {
  1108. $celltype = $this->_get_celltype($col['col_type']);
  1109. $cell_data = isset($row['col_id_'.$col['col_id']]) ? $row['col_id_'.$col['col_id']] : '';
  1110. // give the celltype a chance to do what it wants with it
  1111. if (method_exists($celltype, 'save_cell'))
  1112. {
  1113. $celltype->settings = array_merge($celltype->settings, $col_settings[$col['col_id']]);
  1114. $celltype->settings['col_id'] = $col['col_id'];
  1115. $celltype->settings['col_name'] = 'col_id_'.$col['col_id'];
  1116. $celltype->settings['row_name'] = $row_id;
  1117. $cell_data = $celltype->save_cell($cell_data);
  1118. }
  1119. if ($cell_data || $cell_data === '0') $save_row = TRUE;
  1120. $row_data['col_id_'.$col['col_id']] = $cell_data;
  1121. }
  1122. // does the row have any data to save?
  1123. if ($save_row)
  1124. {
  1125. if ($new)
  1126. {
  1127. $row_data['site_id'] = $this->EE->config->item('site_id');
  1128. $row_data['entry_id'] = $this->settings['entry_id'];
  1129. $row_data['field_id'] = $this->settings['field_id'];
  1130. // insert the row
  1131. $this->EE->db->insert('matrix_data', $row_data);
  1132. }
  1133. else
  1134. {
  1135. $row_id = substr($row_id, 7);
  1136. // just update the existing row
  1137. $this->EE->db->where('row_id', $row_id);
  1138. $this->EE->db->update('matrix_data', $row_data);
  1139. }
  1140. }
  1141. else
  1142. {
  1143. if (! $new)
  1144. {
  1145. // mark the row for deletion
  1146. $delete_rows[] = $row_id;
  1147. }
  1148. }
  1149. }
  1150. // -------------------------------------------
  1151. // Delete any removed rows
  1152. // -------------------------------------------
  1153. foreach($delete_rows as $row_id)
  1154. {
  1155. $row_id = substr($row_id, 7);
  1156. // delete the rows from exp_matrix_cols
  1157. $this->EE->db->where('row_id', $row_id);
  1158. $this->EE->db->delete('matrix_data');
  1159. }
  1160. }
  1161. // --------------------------------------------------------------------
  1162. /**
  1163. * Delete
  1164. */
  1165. function delete($entry_ids)
  1166. {
  1167. $this->EE->db->where_in('entry_id', $entry_ids);
  1168. $this->EE->db->delete('matrix_data');
  1169. }
  1170. // --------------------------------------------------------------------
  1171. /**
  1172. * Data Query
  1173. */
  1174. private function _data_query($params, $cols)
  1175. {
  1176. if (! $cols) return FALSE;
  1177. // -------------------------------------------
  1178. // What's and Where's
  1179. // -------------------------------------------
  1180. $select = 'row_id';
  1181. $where = '';
  1182. $use_where = FALSE;
  1183. $col_ids_by_name = array();
  1184. foreach ($cols as $col)
  1185. {
  1186. $col_id = 'col_id_'.$col['col_id'];
  1187. $select .= ', '.$col_id;
  1188. $col_ids_by_name[$col['col_name']] = $col['col_id'];
  1189. if (isset($params['search:'.$col['col_name']]))
  1190. {
  1191. $use_where = TRUE;
  1192. $terms = $params['search:'.$col['col_name']];
  1193. if (strncmp($terms, '=', 1) == 0)
  1194. {
  1195. // -------------------------------------------
  1196. // Exact Match e.g.: search:body="=pickle"
  1197. // -------------------------------------------
  1198. $terms = substr($terms, 1);
  1199. // special handling for IS_EMPTY
  1200. if (strpos($terms, 'IS_EMPTY') !== FALSE)
  1201. {
  1202. $terms = str_replace('IS_EMPTY', '', $terms);
  1203. $add_search = $this->EE->functions->sql_andor_string($terms, $col_id);
  1204. // remove the first AND output by $this->EE->functions->sql_andor_string() so we can parenthesize this clause
  1205. $add_search = substr($add_search, 3);
  1206. $not = (strncmp($terms, 'not ', 4) == 0);
  1207. $conj = ($add_search != '' && ! $not) ? 'OR' : 'AND';
  1208. if ($not)
  1209. {
  1210. $where .= 'AND ('.$add_search.' '.$conj.' '.$col_id.' != "") ';
  1211. }
  1212. else
  1213. {
  1214. $where .= 'AND ('.$add_search.' '.$conj.' '.$col_id.' = "") ';
  1215. }
  1216. }
  1217. else
  1218. {
  1219. $where .= $this->EE->functions->sql_andor_string($terms, $col_id).' ';
  1220. }
  1221. }
  1222. else
  1223. {
  1224. // -------------------------------------------
  1225. // "Contains" e.g.: search:body="pickle"
  1226. // -------------------------------------------
  1227. if (strncmp($terms, 'not ', 4) == 0)
  1228. {
  1229. $terms = substr($terms, 4);
  1230. $like = 'NOT LIKE';
  1231. }
  1232. else
  1233. {
  1234. $like = 'LIKE';
  1235. }
  1236. if (strpos($terms, '&&') !== FALSE)
  1237. {
  1238. $terms = explode('&&', $terms);
  1239. $andor = (strncmp($like, 'NOT', 3) == 0) ? 'OR' : 'AND';
  1240. }
  1241. else
  1242. {
  1243. $terms = explode('|', $terms);
  1244. $andor = (strncmp($like, 'NOT', 3) == 0) ? 'AND' : 'OR';
  1245. }
  1246. $where .= ' AND (';
  1247. foreach ($terms as $term)
  1248. {
  1249. if ($term == 'IS_EMPTY')
  1250. {
  1251. $where .= ' '.$col_id.' '.$like.' "" '.$andor;
  1252. }
  1253. else if (preg_match('/^[<>]=?/', $term, $match)) // less than/greater than
  1254. {
  1255. $term = substr($term, strlen($match[0]));
  1256. $where .= ' '.$col_id.' '.$match[0].' "'.$this->EE->db->escape_str($term).'" '.$andor;
  1257. }
  1258. else if (strpos($term, '\W') !== FALSE) // full word only, no partial matches
  1259. {
  1260. $not = ($like == 'LIKE') ? ' ' : ' NOT ';
  1261. // Note: MySQL's nutty POSIX regex word boundary is [[:>:]]
  1262. $term = '([[:<:]]|^)'.preg_quote(str_replace('\W', '', $term)).'([[:>:]]|$)';
  1263. $where .= ' '.$col_id.$not.'REGEXP "'.$this->EE->db->escape_str($term).'" '.$andor;
  1264. }
  1265. else
  1266. {
  1267. $where .= ' '.$col_id.' '.$like.' "%'.$this->EE->db->escape_like_str($term).'%" '.$andor;
  1268. }
  1269. }
  1270. $where = substr($where, 0, -strlen($andor)).') ';
  1271. }
  1272. }
  1273. }
  1274. // -------------------------------------------
  1275. // Row IDs
  1276. // -------------------------------------------
  1277. if (isset($params['row_id']) && $params['row_id'])
  1278. {
  1279. $use_where = TRUE;
  1280. if (strncmp($params['row_id'], 'not ', 4) == 0)
  1281. {
  1282. $not = 'NOT ';
  1283. $params['row_id'] = substr($params['row_id'], 4);
  1284. }
  1285. else
  1286. {
  1287. $not = '';
  1288. }
  1289. $where .= ' AND row_id '.$not.'IN (' . str_replace('|', ',', $params['row_id']) . ')';
  1290. }
  1291. $sql = 'SELECT '.(isset($params['count']) ? 'COUNT(row_id) count' : $select).'
  1292. FROM exp_matrix_data
  1293. WHERE field_id = '.$this->field_id.'
  1294. AND entry_id = '.$this->entry_id.'
  1295. '.($use_where ? $where : '');
  1296. // -------------------------------------------
  1297. // Orberby + Sort
  1298. // -------------------------------------------
  1299. $orderbys = (isset($params['orderby']) && $params['orderby']) ? explode('|', $params['orderby']) : array('row_order');
  1300. $sorts = (isset($params['sort']) && $params['sort']) ? explode('|', $params['sort']) : array();
  1301. $all_orderbys = array();
  1302. foreach($orderbys as $i => $name)
  1303. {
  1304. $name = (isset($col_ids_by_name[$name])) ? 'col_id_'.$col_ids_by_name[$name] : $name;
  1305. $sort = (isset($sorts[$i]) && strtoupper($sorts[$i]) == 'DESC') ? 'DESC' : 'ASC';
  1306. $all_orderbys[] = $name.' '.$sort;
  1307. }
  1308. $sql .= ' ORDER BY '.implode(', ', $all_orderbys);
  1309. // -------------------------------------------
  1310. // Offset and Limit
  1311. // -------------------------------------------
  1312. // if we're not sorting randomly, go ahead and set the offset and limit in the SQL
  1313. if ((! isset($params['sort']) || $params['sort'] != 'random') && (isset($params['limit']) || isset($params['offset'])))
  1314. {
  1315. $offset = (isset($params['offset']) && $params['offset']) ? $params['offset'] . ', ' : '';
  1316. $limit = (isset($params['limit']) && $params['limit']) ? $params['limit'] : 100;
  1317. $sql .= ' LIMIT ' . $offset . $limit;
  1318. }
  1319. // -------------------------------------------
  1320. // Run and return
  1321. // -------------------------------------------
  1322. $query = $this->EE->db->query($sql);
  1323. return isset($params['count']) ? $query->row('count') : ($query->num_rows() ? $query->result_array() : FALSE);
  1324. }
  1325. // --------------------------------------------------------------------
  1326. /**
  1327. * Replace Tag
  1328. */
  1329. function replace_tag($data, $params = array(), $tagdata = FALSE)
  1330. {
  1331. if (! isset($this->cache['Channel'])) return '';
  1332. // return table if single tag
  1333. if (! $tagdata)
  1334. {
  1335. return $this->replace_table($params, $tagdata, $data);
  1336. }
  1337. // dynamic params
  1338. if (isset($params['dynamic_parameters']))
  1339. {
  1340. $dynamic_parameters = explode('|', $params['dynamic_parameters']);
  1341. foreach ($dynamic_parameters as $param)
  1342. {
  1343. if (($val = $this->EE->input->post($param)) !== FALSE)
  1344. {
  1345. $params[$param] = $val;
  1346. }
  1347. }
  1348. }
  1349. $r = '';
  1350. // -------------------------------------------
  1351. // Get the columns
  1352. // -------------------------------------------
  1353. $col_ids = isset($this->settings['col_ids']) ? $this->settings['col_ids'] : array();
  1354. $cols = $this->_get_field_cols($col_ids);
  1355. if (! $cols) return $r;
  1356. // -------------------------------------------
  1357. // Get the data
  1358. // -------------------------------------------
  1359. $data = $this->_data_query($params, $cols);
  1360. if (! $data) return $r;
  1361. // -------------------------------------------
  1362. // Randomize
  1363. // -------------------------------------------
  1364. if (isset($params['sort']) && $params['sort'] == 'random')
  1365. {
  1366. shuffle($data);
  1367. // apply the limit now, since we didn't do it in the original query
  1368. if (isset($params['limit']) && $params['limit'])
  1369. {
  1370. $data = array_splice($data, 0, $params['limit']);
  1371. }
  1372. }
  1373. // -------------------------------------------
  1374. // Prep Iterators
  1375. // -------------------------------------------
  1376. $this->_switches = array();
  1377. $tagdata = preg_replace_callback('/'.LD.'switch\s*=\s*([\'\"])([^\1]+)\1'.RD.'/sU', array(&$this, '_get_switch_options'), $tagdata);
  1378. $iterator_count = 0;
  1379. // -------------------------------------------
  1380. // Tagdata
  1381. // -------------------------------------------
  1382. $total_rows = count($data);
  1383. foreach($data as $row_count => $row)
  1384. {
  1385. $row_tagdata = $tagdata;
  1386. $conditionals = array();
  1387. $tags = array();
  1388. foreach($cols as $col)
  1389. {
  1390. $col_id = 'col_id_'.$col['col_id'];
  1391. $conditionals[$col['col_name']] = $row[$col_id];
  1392. $tags[$col['col_name']] = array(
  1393. 'data' => $row[$col_id],
  1394. 'settings' => array_merge($this->settings, $col['col_settings']),
  1395. 'type' => $col['col_type'],
  1396. 'class' => $this->_get_celltype_class($col['col_type'], TRUE)
  1397. );
  1398. }
  1399. $vars = array(
  1400. 'total_rows' => $total_rows,
  1401. 'row_count' => $iterator_count + 1,
  1402. 'row_id' => $row['row_id']
  1403. );
  1404. $row_tagdata = $this->EE->functions->prep_conditionals($row_tagdata, array_merge($vars, $conditionals));
  1405. $row_tagdata = $this->EE->functions->var_swap($row_tagdata, $vars);
  1406. $this->_parse_tagdata($row_tagdata, $tags);
  1407. // {switch}
  1408. foreach($this->_switches as $i => $switch)
  1409. {
  1410. $option = $iterator_count % count($switch['options']);
  1411. $row_tagdata = str_replace($switch['marker'], $switch['options'][$option], $row_tagdata);
  1412. }
  1413. $r .= $row_tagdata;
  1414. // update the c…

Large files files are truncated, but you can click here to view the full file