PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/_backkup/HB_BACKUP_APRIL5_2012/public_html/system/expressionengine/third_party/matrix/ft.matrix.php

https://bitbucket.org/sims/heartbeets
PHP | 2766 lines | 1693 code | 564 blank | 509 comment | 288 complexity | 018b3b5fd34a5a9cff496a705e212d5b MD5 | raw file

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

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