PageRenderTime 58ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/fuel/modules/fuel/controllers/Module.php

http://github.com/daylightstudio/FUEL-CMS
PHP | 2388 lines | 1678 code | 416 blank | 294 comment | 255 complexity | 46b38ce1205c7dad8c0cc20cb3617741 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. require_once(FUEL_PATH.'/libraries/Fuel_base_controller.php');
  3. class Module extends Fuel_base_controller {
  4. public $module_obj; // the module object
  5. public $module = ''; // the name of the module
  6. public $uploaded_data = array(); // reference to the uploaded data
  7. protected $_orig_post = array(); // used for reference
  8. function __construct($validate = TRUE)
  9. {
  10. parent::__construct($validate);
  11. $this->load->module_model(FUEL_FOLDER, 'fuel_archives_model');
  12. if (empty($this->module))
  13. {
  14. $this->module = fuel_uri_segment(1);
  15. }
  16. if (empty($this->module))
  17. {
  18. show_error(lang('cannot_determine_module', fuel_url()));
  19. }
  20. $params = array();
  21. if ($this->fuel->modules->exists($this->module, FALSE))
  22. {
  23. $this->module_obj = $this->fuel->modules->get($this->module, FALSE);
  24. $params = $this->module_obj->info();
  25. }
  26. else if ($this->fuel->modules->exists($this->module.'_'.fuel_uri_segment(2), FALSE))
  27. {
  28. // if it is a module with multiple controllers, then we'll check first and second FUEL segment with an underscore'
  29. $this->module = $this->module.'_'.fuel_uri_segment(2);
  30. if ($this->fuel->modules->exists($this->module, FALSE))
  31. {
  32. $this->module_obj = $this->fuel->modules->get($this->module, FALSE);
  33. $params = $this->module_obj->info();
  34. }
  35. }
  36. else if ($this->fuel->modules->exists(fuel_uri_segment(2), FALSE))
  37. {
  38. $this->module = fuel_uri_segment(2);
  39. $this->module_obj = $this->fuel->modules->get($this->module, FALSE);
  40. if ($this->module AND $this->module_obj)
  41. {
  42. $mod_name = $this->module_obj->name();
  43. }
  44. if (empty($mod_name))
  45. {
  46. show_error(lang('error_missing_module', fuel_uri_segment(1)));
  47. }
  48. unset($mod_name);
  49. $params = $this->module_obj->info();
  50. }
  51. // stop here if the module is disabled
  52. if (empty($params) OR $params['disabled'] === TRUE)
  53. {
  54. show_404();
  55. }
  56. foreach($params as $key => $val)
  57. {
  58. $this->$key = $val;
  59. }
  60. // load any configuration
  61. if ( ! empty($this->configuration))
  62. {
  63. if (is_array($this->configuration))
  64. {
  65. $config_module = key($this->configuration);
  66. $config_file = current($this->configuration);
  67. $this->config->module_load($config_module, $config_file);
  68. }
  69. else
  70. {
  71. $this->config->load($this->configuration);
  72. }
  73. }
  74. // load any language
  75. if ( ! empty($this->language))
  76. {
  77. if (is_array($this->language))
  78. {
  79. $lang_module = key($this->language);
  80. $lang_file = current($this->language);
  81. // now check to see if we need to load the language file or not...
  82. // we load the main language file automatically with the Fuel_base_controller.php
  83. $this->load->module_language($lang_module, $lang_file, $this->fuel->auth->user_lang());
  84. }
  85. else
  86. {
  87. $this->load->language($this->language);
  88. }
  89. }
  90. // load the model
  91. if ( ! empty($this->model_location))
  92. {
  93. $this->load->module_model($this->model_location, $this->model_name);
  94. }
  95. else
  96. {
  97. $this->load->model($this->model_name);
  98. }
  99. // get the model name
  100. $model_parts = explode('/', $this->model_name);
  101. $model = end($model_parts);
  102. // set the module_uri
  103. if (empty($this->module_uri)) $this->module_uri = $this->module;
  104. $this->js_controller_params['module'] = $this->module_uri;
  105. if ( ! empty($model))
  106. {
  107. $this->model =& $this->$model;
  108. }
  109. else
  110. {
  111. show_error(lang('incorrect_route_to_module'));
  112. }
  113. // global variables
  114. $vars = array();
  115. if ( ! empty($params['js']))
  116. {
  117. if (is_string($params['js']))
  118. {
  119. $params['js'] = preg_split("/,\s*/", $params['js']);
  120. }
  121. $vars['js'] = $params['js'];
  122. }
  123. if ( ! empty($this->nav_selected)) $vars['nav_selected'] = $this->nav_selected;
  124. $this->load->vars($vars);
  125. $this->fuel->admin->load_js_localized($params['js_localized']);
  126. if ( ! empty($this->permission) AND $validate)
  127. {
  128. $this->_validate_user($this->permission);
  129. }
  130. }
  131. // --------------------------------------------------------------------
  132. /**
  133. * Displays the list (table) view
  134. *
  135. * @access public
  136. * @return void
  137. */
  138. function index()
  139. {
  140. $this->items();
  141. }
  142. // --------------------------------------------------------------------
  143. /**
  144. * Displays the list (table) view
  145. *
  146. * @access public
  147. * @return void
  148. */
  149. function items()
  150. {
  151. $this->load->library('data_table');
  152. $filters = $this->model->filters($this->filters);
  153. if (is_object($filters) && ($filters instanceof Base_model_fields)) {
  154. $filters = $filters->get_fields();
  155. }
  156. $this->filters = array_merge($this->filters, $filters);
  157. // set the language dropdown if there is a language column
  158. if ($this->fuel->language->has_multiple() AND !empty($this->language_col) AND method_exists($this->model, 'get_languages'))
  159. {
  160. $languages = $this->model->get_languages($this->language_col);
  161. $first_option = current($languages);
  162. if (( ! empty($languages) AND (is_string($first_option) OR (is_array($first_option)) AND count($first_option) > 1)) AND empty($this->filters[$this->language_col.'_equal']))
  163. {
  164. $lang_filter = array('type' => 'select', 'options' => $languages, 'label' => lang('label_language'), 'first_option' => lang('label_select_a_language'));
  165. $this->filters[$this->language_col.'_equal'] = $lang_filter;
  166. $this->model->add_filter_join($this->language_col.'_equal', 'and');
  167. }
  168. }
  169. $params = $this->_list_process();
  170. // save page state
  171. $this->fuel->admin->save_page_state($params);
  172. // filter the list
  173. $this->_filter_list($params);
  174. // to prevent it from being called unnecessarily with ajax
  175. if ( ! is_ajax())
  176. {
  177. $item_total = $this->model->list_items_total();
  178. if ($this->single_item_navigate AND $item_total == 1)
  179. {
  180. $items = $this->model->list_items();
  181. $id = $items[0][$this->model->key_field()];
  182. // lets check a few permissions
  183. if ($this->fuel->auth->has_permission($this->permission, "edit"))
  184. {
  185. $url = fuel_url($this->module_uri.'/edit/'.$id);
  186. }
  187. elseif ($this->fuel->auth->has_permission($this->permission, "view"))
  188. {
  189. $url = fuel_url($this->module_uri.'/view/'.$id);
  190. }
  191. elseif ($this->fuel->auth->has_permission($this->permission, "show"))
  192. {
  193. $url = fuel_url($this->module_uri.'/show/'.$id);
  194. }
  195. if (!empty($url))
  196. {
  197. redirect($url);
  198. }
  199. }
  200. $this->config->set_item('enable_query_strings', FALSE);
  201. // pagination
  202. $query_str_arr = $this->input->get(NULL, TRUE);
  203. unset($query_str_arr['offset']);
  204. $query_str = ( ! empty($query_str_arr)) ? http_build_query($query_str_arr) : '';
  205. $config['base_url'] = fuel_url($this->module_uri).'/items/?'.$query_str;
  206. $uri_segment = 4 + (count(explode('/', $this->module_uri)) - 1);
  207. $config['total_rows'] = $item_total;
  208. $config['uri_segment'] = fuel_uri_index($uri_segment);
  209. $config['per_page'] = (int) $params['limit'];
  210. $config['query_string_segment'] = 'offset';
  211. $config['page_query_string'] = TRUE;
  212. $config['num_links'] = 5;
  213. $config['prev_link'] = lang('pagination_prev_page');
  214. $config['next_link'] = lang('pagination_next_page');
  215. $config['first_link'] = lang('pagination_first_link');
  216. $config['last_link'] = lang('pagination_last_link');
  217. // must reset these in case a config file has something different
  218. $config['full_tag_open'] = NULL;
  219. $config['full_tag_close'] = NULL;
  220. $config['num_tag_open'] = '&nbsp;';
  221. $config['num_tag_close'] = NULL;
  222. $config['cur_tag_open'] = '&nbsp;<strong>';
  223. $config['cur_tag_close'] = '</strong>';
  224. $config['next_tag_open'] = '&nbsp;';
  225. $config['next_tag_close'] = '&nbsp;';
  226. $config['prev_tag_open'] = '&nbsp;';
  227. $config['prev_tag_close'] = NULL;
  228. $config['first_tag_open'] = '&nbsp;';
  229. $config['first_tag_close'] = '&nbsp;';
  230. $config['last_tag_open'] = NULL;
  231. $config['last_tag_close'] = NULL;
  232. $this->pagination->initialize($config);
  233. $this->fuel->admin->set_notification(number_format($item_total).' '.pluralize($item_total, lang('num_items')), 'info');
  234. if (method_exists($this->model, 'tree'))
  235. {
  236. $vars['tree'] = "\n<ul></ul>\n";
  237. }
  238. // reset offset if total rows is less then limit
  239. if ($config['total_rows'] < $params['limit'])
  240. {
  241. $params['offset'] = 0;
  242. }
  243. }
  244. // set vars
  245. $vars['params'] = $params;
  246. $vars['table'] = '';
  247. // reload table
  248. if (is_ajax())
  249. {
  250. // data table items... check col value to know if we want to send sorting parameter
  251. if (empty($params['col']) OR empty($params['order']))
  252. {
  253. $items = $this->model->list_items($params['limit'], $params['offset']);
  254. }
  255. else
  256. {
  257. $items = $this->model->list_items($params['limit'], $params['offset'], $params['col'], $params['order']);
  258. $this->data_table->set_sorting($params['col'], $params['order']);
  259. }
  260. $has_edit_permission = $this->fuel->auth->has_permission($this->permission, "edit") ? '1' : '0';
  261. $has_delete_permission = $this->fuel->auth->has_permission($this->permission, "delete") ? '1' : '0';
  262. // set data table actions... look first for item_actions set in the fuel_modules
  263. $delete_func = function($cols) use ($has_delete_permission) {
  264. $CI =& get_instance();
  265. $link = "";
  266. if ($has_delete_permission)
  267. {
  268. if (!empty($cols[$CI->model->key_field()]))
  269. {
  270. $url = fuel_url($this->module_uri."/delete/".$cols[$CI->model->key_field()]);
  271. $link = "<a href=\"".$url."\" class=\"action_delete\">".lang("table_action_delete")."</a>";
  272. $link .= " <input type=\"checkbox\" name=\"delete[".$cols[$CI->model->key_field()]."]\" value=\"1\" id=\"delete_".$cols[$CI->model->key_field()]."\" class=\"multi_delete\"/>";
  273. }
  274. }
  275. return $link;
  276. };
  277. foreach($this->table_actions as $key => $val)
  278. {
  279. if ( ! is_int($key))
  280. {
  281. $action_type = 'url';
  282. $action_val = $this->table_actions[$key];
  283. $attrs = array();
  284. if (is_array($val))
  285. {
  286. if (isset($val['url']))
  287. {
  288. $action_type = 'url';
  289. $action_val = $val['url'];
  290. if (isset($val['attrs']))
  291. {
  292. $attrs = $val['attrs'];
  293. }
  294. }
  295. else
  296. {
  297. $action_type = key($val);
  298. $action_val = current($val);
  299. }
  300. $attrs = (isset($val['attrs'])) ? $val['attrs'] : array();
  301. }
  302. $this->data_table->add_action($key, $action_val, $action_type, $attrs);
  303. }
  304. else if (strtoupper($val) == 'EDIT')
  305. {
  306. if ($this->fuel->auth->has_permission($this->permission, "edit"))
  307. {
  308. $action_url = fuel_url($this->module_uri.'/edit/{'.$this->model->key_field().'}');
  309. $this->data_table->add_action(lang('table_action_edit'), $action_url, 'url');
  310. }
  311. }
  312. else if (strtoupper($val) == 'DELETE')
  313. {
  314. $this->data_table->add_action($val, $delete_func, 'func');
  315. }
  316. else
  317. {
  318. if (strtoupper($val) != 'VIEW' OR ( ! empty($this->preview_path) AND strtoupper($val) == 'VIEW'))
  319. {
  320. $action_name = lang('table_action_'.strtolower($val));
  321. if (empty($action_name)) $action_name = $val;
  322. $action_url = fuel_url($this->module_uri.'/'.strtolower($val).'/{'.$this->model->key_field().'}');
  323. $this->data_table->add_action($action_name, $action_url, 'url');
  324. }
  325. }
  326. }
  327. if ( ! $this->rows_selectable)
  328. {
  329. $this->data_table->id = 'data_table_noselect';
  330. $this->data_table->row_action = FALSE;
  331. }
  332. else
  333. {
  334. $this->data_table->row_action = TRUE;
  335. }
  336. $this->data_table->row_alt_class = 'alt';
  337. if ($this->model->has_auto_increment())
  338. {
  339. $this->data_table->only_data_fields = array($this->model->key_field());
  340. }
  341. // Key and boolean fields are data only
  342. // $this->data_table->only_data_fields = array_merge(array($this->model->key_field()), $this->model->boolean_fields);
  343. $this->data_table->auto_sort = TRUE;
  344. $this->data_table->actions_field = 'last';
  345. $this->data_table->no_data_str = lang('no_data');
  346. $this->data_table->lang_prefix = 'form_label_';
  347. $this->data_table->row_id_key = $this->model->key_field();
  348. $boolean_fields = $this->model->boolean_fields;
  349. if ( ! in_array('published', $boolean_fields)) $boolean_fields[] = 'published';
  350. if ( ! in_array('active', $boolean_fields)) $boolean_fields[] = 'active';
  351. $has_publish_permission = ($this->fuel->auth->has_permission($this->permission, 'publish')) ? '1' : '0';
  352. $has_edit_permission = $this->fuel->auth->has_permission($this->permission, 'edit') ? '1' : '0';
  353. $_publish_toggle_callback = function($cols, $heading) {
  354. $can_publish = (($heading == "published" OR $heading == "active") AND '.$has_publish_permission.' OR
  355. (($heading != "published" AND $heading != "active") AND '.$has_edit_permission.'));
  356. $no = lang("form_enum_option_no");
  357. $yes = lang("form_enum_option_yes");
  358. $col_txt = lang('click_to_toggle');
  359. $key_field = $this->model->key_field();
  360. // boolean fields
  361. if (is_null($cols[$heading]) OR $cols[$heading] == "")
  362. {
  363. return "";
  364. }
  365. else if (!is_true_val($cols[$heading]))
  366. {
  367. $text_class = ($can_publish) ? "publish_text unpublished toggle_on" : "unpublished";
  368. $action_class = ($can_publish) ? "publish_action unpublished hidden" : "unpublished hidden";
  369. return '<span class="publish_hover"><span class="'.$text_class.'" id="row_published_'.$cols[$key_field].'" data-field="'.$heading.'">'.$no.'</span><span class="'.$action_class.'">'.$col_txt.'</span></span>';
  370. }
  371. else
  372. {
  373. $text_class = ($can_publish) ? "publish_text published toggle_off" : "published";
  374. $action_class = ($can_publish) ? "publish_action published hidden" : "published hidden";
  375. return '<span class="publish_hover"><span class="'.$text_class.'" id="row_published_'.$cols[$key_field].'" data-field="'.$heading.'">'.$yes.'</span><span class="'.$action_class.'">'.$col_txt.'</span></span>';
  376. }
  377. };
  378. foreach($boolean_fields as $bool)
  379. {
  380. $this->data_table->add_field_formatter($bool, $_publish_toggle_callback);
  381. }
  382. $this->data_table->auto_sort = TRUE;
  383. $heading_sort_func = (isset($this->disable_heading_sort) AND $this->disable_heading_sort) ? '' : 'fuel.sortList';
  384. $this->data_table->sort_js_func = $heading_sort_func;
  385. $this->data_table->assign_data($items, $this->table_headers);
  386. $vars['table'] = $this->data_table->render();
  387. if ( ! empty($items[0]) AND ( ! empty($this->precedence_col) AND isset($items[0][$this->precedence_col])))
  388. {
  389. $vars['params']['precedence'] = 1;
  390. }
  391. $this->load->module_view(FUEL_FOLDER, '_blocks/module_list_table', $vars);
  392. return;
  393. }
  394. else
  395. {
  396. $this->load->library('form_builder');
  397. $this->js_controller_params['method'] = 'items';
  398. $this->js_controller_params['precedence_col'] = $this->precedence_col;
  399. $vars['table'] = $this->load->module_view(FUEL_FOLDER, '_blocks/module_list_table', $vars, TRUE);
  400. $vars['pagination'] = $this->pagination->create_links();
  401. // for extra module 'filters'
  402. $field_values = array();
  403. foreach($this->filters as $key => $val)
  404. {
  405. $field_values[$key] = $params[$key];
  406. }
  407. $this->form_builder->question_keys = array();
  408. //$this->form_builder->hidden = (array) $this->model->key_field();
  409. $this->form_builder->label_layout = 'left';
  410. $this->form_builder->load_custom_fields(APPPATH.'config/custom_fields.php');
  411. $this->form_builder->set_validator($this->model->get_validation());
  412. $this->form_builder->submit_value = NULL;
  413. $this->form_builder->use_form_tag = FALSE;
  414. $this->form_builder->set_fields($this->filters);
  415. $this->form_builder->display_errors = FALSE;
  416. $this->form_builder->css_class = 'more_filters';
  417. if ($this->config->item('date_format'))
  418. {
  419. $this->form_builder->date_format = $this->config->item('date_format');
  420. }
  421. $this->form_builder->set_field_values($field_values);
  422. if (method_exists($this->model, 'friendly_filter_info'))
  423. {
  424. $friendly_filter_info = $this->model->friendly_filter_info($field_values);
  425. if ( ! empty($friendly_filter_info))
  426. {
  427. $vars['info'] = $friendly_filter_info;
  428. }
  429. }
  430. // keycheck is already put in place by $this->form->close() in module_list layout
  431. $this->form_builder->key_check = FALSE;
  432. $vars['more_filters'] = $this->form_builder->render_divs();
  433. $vars['actions'] = $this->load->module_view(FUEL_FOLDER, '_blocks/module_list_actions', $vars, TRUE);
  434. $vars['form_action'] = $this->module_uri.'/items';
  435. $vars['form_method'] = 'get';
  436. $vars['query_string'] = $query_str;
  437. $vars['description'] = $this->description;
  438. $crumbs = array($this->module_uri => $this->module_name);
  439. $this->fuel->admin->set_titlebar($crumbs);
  440. $inline = $this->input->get('inline', TRUE);
  441. $this->fuel->admin->set_inline($inline);
  442. if ($inline === TRUE)
  443. {
  444. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT_TITLEBAR);
  445. }
  446. $this->fuel->admin->render($this->views['list'], $vars, '', FUEL_FOLDER);
  447. }
  448. }
  449. // --------------------------------------------------------------------
  450. /**
  451. * Displays the list (table) view but inline without the left menu
  452. *
  453. * @access public
  454. * @return void
  455. */
  456. function inline_items()
  457. {
  458. $this->items();
  459. }
  460. // --------------------------------------------------------------------
  461. /**
  462. * Processes the list view filters and returns an array of parameters
  463. *
  464. * @access protected
  465. * @return array
  466. */
  467. protected function _list_process()
  468. {
  469. $this->load->library('pagination');
  470. $this->load->helper('convert');
  471. $this->load->helper('cookie');
  472. /* PROCESS PARAMS BEGIN */
  473. $filters = array();
  474. $page_state = $this->fuel->admin->get_page_state($this->module_uri);
  475. unset($page_state['offset']);
  476. $defaults = array();
  477. $defaults['col'] = (!empty($this->default_col)) ? $this->default_col : $this->display_field;
  478. $defaults['order'] = (isset($this->default_order)) ? $this->default_order : 'asc';
  479. $defaults['offset'] = 0;
  480. $defaults['limit'] = key($this->limit_options);
  481. $defaults['search_term'] = '';
  482. $defaults['view_type'] = 'list';
  483. $defaults['extra_filters'] = array();
  484. $defaults['precedence'] = 0;
  485. //$defaults['language'] = '';
  486. // custom module filters defaults
  487. foreach($this->filters as $key => $val)
  488. {
  489. $defaults[$key] = (isset($val['default'])) ? $val['default'] : NULL;
  490. }
  491. $posted = array();
  492. if ( ! empty($_POST) OR !empty($_GET))
  493. {
  494. $posted['search_term'] = $this->input->get_post('search_term', TRUE);
  495. $posted_vars = array('col', 'order', 'limit', 'offset', 'precedence', 'view_type');
  496. foreach($posted_vars as $val)
  497. {
  498. if ($this->input->get_post($val)) $posted[$val] = $this->input->get_post($val, TRUE);
  499. }
  500. // custom module filters
  501. $extra_filters = array();
  502. foreach($this->filters as $key => $val)
  503. {
  504. if (isset($_POST[$key]) OR isset($_GET[$key]))
  505. {
  506. $posted[$key] = $this->input->get_post($key, TRUE);
  507. // get the raw key without the comparison operators that the model uses
  508. $raw_key = preg_replace(array('#_from$#', '#_fromequal$#', '#_to$#', '#_toequal$#', '#_equal$#'), '', $key);
  509. // manipulate the value if it's a date time field
  510. if (method_exists($this->model, 'field_type'))
  511. {
  512. $field_type = $this->model->field_type($raw_key);
  513. if (is_date_format($posted[$key]) AND $field_type == 'datetime' OR $field_type == 'date' AND (int) $posted[$key] !== 0)
  514. {
  515. $date = ($this->input->get_post($key) AND is_date_format($this->input->get_post($key))) ? current(explode(" ", $this->input->get_post($key))) : "";
  516. $hr = ($this->input->get_post($key.'_hour') AND (int)$this->input->get_post($key.'_hour') > 0 AND (int)$this->input->get_post($key.'_hour') < 24) ? $this->input->get_post($key.'_hour') : "";
  517. $min = ($this->input->get_post($key.'_min') AND is_numeric($this->input->get_post($key.'_min'))) ? $this->input->get_post($key.'_min') : "00";
  518. $ampm = ($this->input->get_post($key.'_am_pm') AND $hr AND $min) ? $this->input->get_post($key.'_am_pm') : "";
  519. if ( ! empty($ampm) AND !empty($hr) AND $hr > 12)
  520. {
  521. if ($hr > 24)
  522. {
  523. $hr = "00";
  524. }
  525. else
  526. {
  527. $hr = (int) $hr - 12;
  528. $ampm = "pm";
  529. }
  530. }
  531. $posted[$key] = $date;
  532. if ( ! empty($hr)) $posted[$key] .= " ".$hr.":".$min.$ampm;
  533. $posted[$key] = date('Y-m-d H:i:s', strtotime($posted[$key]));
  534. }
  535. }
  536. $this->filters[$key]['value'] = $posted[$key];
  537. $extra_filters[$key] = $posted[$key];
  538. }
  539. }
  540. $posted['extra_filters'] = $extra_filters;
  541. }
  542. $params = array_merge($defaults, $page_state, $posted);
  543. //$params = array_merge($defaults, $uri_params, $posted);
  544. if ($params['search_term'] == lang('label_search')) $params['search_term'] = NULL;
  545. /* PROCESS PARAMS END */
  546. return $params;
  547. }
  548. // --------------------------------------------------------------------
  549. /**
  550. * Adds filters to the model
  551. *
  552. * @access protected
  553. * @return void
  554. */
  555. function _filter_list($params)
  556. {
  557. // create search filter
  558. $search_key = !empty($this->search_field) ? $this->search_field : $this->display_field;
  559. $filters[$search_key] = trim($params['search_term']);
  560. // sort of hacky here... to make it easy for the model to just filter on the search term (like the users model)
  561. $this->model->filter_value = trim($params['search_term']);
  562. foreach($this->filters as $key => $val)
  563. {
  564. $filters[$key] = $params[$key];
  565. if ( ! empty($val['filter_join']))
  566. {
  567. if ( ! is_array($this->model->filter_join[$key]))
  568. {
  569. settype($this->model->filter_join, 'array');
  570. }
  571. $this->model->filter_join[$key] = $val['filter_join'];
  572. }
  573. }
  574. // set model filters before pagination and setting table data
  575. if (method_exists($this->model, 'add_filters'))
  576. {
  577. $this->model->add_filters($filters);
  578. }
  579. }
  580. // --------------------------------------------------------------------
  581. /**
  582. * Displays the tree view
  583. *
  584. * @access public
  585. * @return void
  586. */
  587. function items_tree()
  588. {
  589. // tree
  590. if (method_exists($this->model, 'tree') AND is_ajax())
  591. {
  592. $params = $this->_list_process();
  593. $this->load->library('menu');
  594. $this->menu->depth = NULL; // as deep as it goes
  595. $this->menu->use_titles = FALSE;
  596. $this->menu->root_value = 0;
  597. $this->model->add_filters($params['extra_filters']);
  598. $menu_items = $this->model->tree();
  599. if ( ! empty($menu_items))
  600. {
  601. $output = $this->menu->render($menu_items);
  602. }
  603. else
  604. {
  605. $output = '<div>'.lang('no_data').'</div>';
  606. }
  607. $this->output->set_output($output);
  608. }
  609. }
  610. // --------------------------------------------------------------------
  611. /**
  612. * Saves the precedence of fields
  613. *
  614. * @access public
  615. * @return void
  616. */
  617. function items_precedence()
  618. {
  619. if (is_ajax() AND ! empty($_POST['data_table']) AND ! empty($this->precedence_col))
  620. {
  621. if (is_array($_POST['data_table']))
  622. {
  623. $i = 0;
  624. foreach($_POST['data_table'] as $row)
  625. {
  626. if ( ! empty($row))
  627. {
  628. $values = array($this->precedence_col => $i);
  629. $where = array($this->model->key_field() => $row);
  630. $this->model->update($values, $where);
  631. }
  632. $i++;
  633. }
  634. // clear cache
  635. $this->_clear_cache();
  636. }
  637. }
  638. }
  639. // --------------------------------------------------------------------
  640. /**
  641. * Displays the fields to create a record (form view)
  642. *
  643. * @access public
  644. * @param string The name of a field, or fields separated by colon to display in the form (optional)
  645. * @param string Determines whether to redirect the page after save or not
  646. * @return void
  647. */
  648. function create($field = NULL, $redirect = TRUE)
  649. {
  650. $id = NULL;
  651. // check that the action even exists and if not, show a 404
  652. if ( ! $this->fuel->auth->module_has_action('save'))
  653. {
  654. show_404();
  655. }
  656. // check permissions
  657. if ( ! $this->fuel->auth->has_permission($this->module_obj->permission, 'create'))
  658. {
  659. show_error(lang('error_no_permissions', fuel_url()));
  660. }
  661. $inline = $this->fuel->admin->is_inline();
  662. if (isset($_POST[$this->model->key_field()])) // check for dupes
  663. {
  664. if ($id = $this->_process_create() AND !has_errors())
  665. {
  666. if ($inline === TRUE)
  667. {
  668. $url = fuel_uri($this->module_uri.'/inline_edit/'.$id.'/'.$field, TRUE);
  669. }
  670. else
  671. {
  672. $url = fuel_uri($this->module_uri.'/edit/'.$id.'/'.$field, TRUE);
  673. }
  674. // save any tab states
  675. $this->_save_tab_state($id);
  676. if ($redirect)
  677. {
  678. if ( ! $this->fuel->admin->has_notification(Fuel_admin::NOTIFICATION_SUCCESS))
  679. {
  680. $this->fuel->admin->set_notification(lang('data_saved'), Fuel_admin::NOTIFICATION_SUCCESS);
  681. }
  682. redirect($url);
  683. }
  684. }
  685. }
  686. $shell_vars = $this->_shell_vars($id);
  687. $get = (array) $this->input->get(NULL, TRUE);
  688. $post = (array) $this->input->post(NULL, TRUE);
  689. $passed_init_vars = array_filter(array_merge($get, $post));
  690. $form_vars = $this->_form_vars($id, $passed_init_vars, $field, $inline);
  691. $vars = array_merge($shell_vars, $form_vars);
  692. $vars['action'] = 'create';
  693. $vars['related_items'] = $this->model->related_items(array());
  694. $crumbs = array($this->module_uri => $this->module_name, lang('action_create'));
  695. $this->fuel->admin->set_titlebar($crumbs);
  696. $this->fuel->admin->set_inline($inline);
  697. if ( ! empty($field) AND strpos($field, ':') === FALSE)
  698. {
  699. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT_NO_ACTION);
  700. }
  701. else if ($inline === TRUE)
  702. {
  703. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT);
  704. }
  705. $vars['actions'] = $this->load->module_view(FUEL_FOLDER, '_blocks/module_inline_actions', $vars, TRUE);
  706. $this->fuel->admin->render($this->views['create_edit'], $vars, '', FUEL_FOLDER);
  707. return $id;
  708. }
  709. // --------------------------------------------------------------------
  710. /**
  711. * The same as the create method but does not show the left menu
  712. *
  713. * @access public
  714. * @param string The name of a field, or fields separated by colon to display in the form (optional)
  715. * @param string Determines whether to redirect the page after save or not
  716. * @return void
  717. */
  718. function inline_create($field = NULL)
  719. {
  720. $this->fuel->admin->set_inline(TRUE);
  721. $this->create($field);
  722. }
  723. // --------------------------------------------------------------------
  724. /**
  725. * Duplicates a record. Similar to edit but without the record ID attached.
  726. *
  727. * @access public
  728. * @return void
  729. */
  730. function duplicate()
  731. {
  732. $_POST[$this->model->key_field()] = 'dup';
  733. $this->create();
  734. }
  735. protected function _process_create()
  736. {
  737. // reset dup id
  738. if ($_POST[$this->model->key_field()] == 'dup')
  739. {
  740. $_POST[$this->model->key_field()] = '';
  741. // alter duplicate information if there is a hook
  742. $_POST = $this->model->on_duplicate($_POST);
  743. $this->load->library('form_builder');
  744. $fb = new Form_builder();
  745. $fb->load_custom_fields(APPPATH.'config/custom_fields.php');
  746. $fields = $this->model->form_fields($_POST);
  747. $fb->set_fields($fields);
  748. $fb->post_process_field_values();// manipulates the $_POST values directly
  749. }
  750. else
  751. {
  752. $this->model->on_before_post($this->input->post());
  753. $posted = $this->_process();
  754. // set publish status to no if you do not have the ability to publish
  755. if ( ! $this->fuel->auth->has_permission($this->permission, 'publish') AND ! $this->fuel->auth->has_permission($this->permission, 'activate'))
  756. {
  757. $posted['published'] = 'no';
  758. $posted['active'] = 'no';
  759. }
  760. $model = $this->model;
  761. // run before_create hook
  762. $this->_run_hook('before_create', $posted);
  763. // run before_save hook
  764. $this->_run_hook('before_save', $posted);
  765. // save the data
  766. $id = $this->model->save($posted);
  767. if (empty($id))
  768. {
  769. add_error(lang('error_invalid_record'));
  770. return FALSE;
  771. }
  772. // add id value to the posted array
  773. if ( ! is_array($this->model->key_field()))
  774. {
  775. $posted[$this->model->key_field()] = $id;
  776. // replace any {id} placeholder values
  777. foreach ($posted as $key => $val)
  778. {
  779. if (is_string($val))
  780. {
  781. $posted[$key] = str_replace('{'.$this->model->key_field().'}', $id, $val);
  782. }
  783. }
  784. }
  785. // process $_FILES
  786. if ( ! $this->_process_uploads($posted))
  787. {
  788. return FALSE;
  789. }
  790. $this->model->on_after_post($posted);
  791. if (!$this->model->is_valid())
  792. {
  793. add_errors($this->model->get_errors());
  794. }
  795. else
  796. {
  797. // archive data
  798. $archive_data = $this->model->cleaned_data();
  799. $archive_data[$this->model->key_field()] = $id;
  800. if ($this->archivable) $this->model->archive($id, $archive_data);
  801. $data = $this->model->find_one_array(array($this->model->table_name().'.'.$this->model->key_field() => $id));
  802. // run after_create hook
  803. $this->_run_hook('after_create', $data);
  804. // run after_save hook
  805. $this->_run_hook('after_save', $data);
  806. if ( ! empty($data))
  807. {
  808. $msg_data = $this->model->display_name($data);
  809. $msg = lang('module_edited', $this->module_name, $msg_data);
  810. $this->fuel->logs->write($msg);
  811. $this->_clear_cache();
  812. return $id;
  813. }
  814. }
  815. }
  816. }
  817. // --------------------------------------------------------------------
  818. /**
  819. * Displays the fields to edit a record (form view)
  820. *
  821. * @access public
  822. * @param int The ID value of the record to edit
  823. * @param string The name of a field, or fields separated by colon to display in the form (optional)
  824. * @param string Determines whether to redirect the page after save or not
  825. * @return void
  826. */
  827. function edit($id = NULL, $field = NULL, $redirect = TRUE)
  828. {
  829. // check that the action even exists and if not, show a 404
  830. if ( ! $this->fuel->auth->module_has_action('save') AND $this->displayonly === FALSE)
  831. {
  832. show_404();
  833. }
  834. // check permissions
  835. if ( ! $this->fuel->auth->has_permission($this->module_obj->permission, 'edit') AND ! $this->fuel->auth->has_permission($this->module_obj->permission, 'create'))
  836. {
  837. show_error(lang('error_no_permissions', fuel_url()));
  838. }
  839. $inline = $this->fuel->admin->is_inline();
  840. if ($this->input->post($this->model->key_field()))
  841. {
  842. if ($this->_process_edit($id) AND !has_errors())
  843. {
  844. if ($inline === TRUE)
  845. {
  846. $url = fuel_uri($this->module_uri.'/inline_edit/'.$id.'/'.$field, TRUE);
  847. }
  848. else
  849. {
  850. $url = fuel_uri($this->module_uri.'/edit/'.$id.'/'.$field, TRUE);
  851. }
  852. if ($redirect)
  853. {
  854. if ( ! $this->fuel->admin->has_notification(Fuel_admin::NOTIFICATION_SUCCESS))
  855. {
  856. $this->fuel->admin->set_notification(lang('data_saved'), Fuel_admin::NOTIFICATION_SUCCESS);
  857. }
  858. redirect($url);
  859. }
  860. }
  861. }
  862. //$vars = $this->_form($id);
  863. $data = $this->_saved_data($id);
  864. if (empty($data))
  865. {
  866. show_error(lang('error_invalid_record'));
  867. }
  868. $action = ( ! empty($data[$this->model->key_field()])) ? 'edit' : 'create';
  869. // check model first for preview path method
  870. if (method_exists($this->model, 'preview_path'))
  871. {
  872. $this->preview_path = $this->model->preview_path($data, $this->preview_path);
  873. }
  874. else
  875. {
  876. // otherwise, substitute data values into preview path
  877. $this->preview_path = $this->module_obj->url($data);
  878. }
  879. $shell_vars = $this->_shell_vars($id, $action, $data);
  880. $form_vars = $this->_form_vars($id, $data, $field, $inline);
  881. $vars = array_merge($shell_vars, $form_vars);
  882. $vars['data'] = $data;
  883. $vars['action'] = $action;
  884. $vars['related_items'] = $this->model->related_items($data);
  885. // active or publish fields
  886. if (isset($data['published']))
  887. {
  888. $vars['publish'] = ( ! empty($data['published']) AND is_true_val($data['published'])) ? 'unpublish' : 'publish';
  889. }
  890. if (isset($data['active']))
  891. {
  892. $vars['activate'] = ( ! empty($data['active']) AND is_true_val($data['active'])) ? 'deactivate' : 'activate';
  893. }
  894. if ( ! empty($field) AND strpos($field, ':') === FALSE)
  895. {
  896. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT_NO_ACTION);
  897. }
  898. else if ($inline === TRUE)
  899. {
  900. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT);
  901. }
  902. $crumbs = array($this->module_uri => $this->module_name);
  903. $msg_data = $this->model->display_name($data);
  904. if ( ! empty($msg_data))
  905. {
  906. $crumbs[''] = character_limiter(strip_tags($msg_data), 100);
  907. }
  908. $this->fuel->admin->set_titlebar($crumbs);
  909. $vars['actions'] = $this->load->module_view(FUEL_FOLDER, '_blocks/module_create_edit_actions', $vars, TRUE);
  910. $this->fuel->admin->render($this->views['create_edit'], $vars, '', FUEL_FOLDER);
  911. // do this after rendering so it doesn't render current page'
  912. if ( ! empty($msg_data) AND $inline !== TRUE)
  913. {
  914. $this->fuel->admin->add_recent_page($this->uri->uri_string(), $this->module_name.': '.$msg_data, $this->module);
  915. }
  916. }
  917. // --------------------------------------------------------------------
  918. /**
  919. * The same as the edit method but does not show the left menu
  920. *
  921. * @access public
  922. * @param int The ID value of the record to edit
  923. * @param string The name of a field, or fields separated by colon to display in the form (optional)
  924. * @return void
  925. */
  926. function inline_edit($id = NULL, $field = NULL)
  927. {
  928. if (empty($id))
  929. {
  930. show_404();
  931. }
  932. $this->fuel->admin->set_inline(TRUE);
  933. $this->edit($id, $field);
  934. }
  935. // --------------------------------------------------------------------
  936. /**
  937. * Processes the form data to save
  938. *
  939. * @access protected
  940. * @param int The ID value of the record to edit
  941. * @return boolean
  942. */
  943. protected function _process_edit($id)
  944. {
  945. $this->model->on_before_post($this->input->post());
  946. $posted = $this->_process();
  947. // run before_edit hook
  948. $this->_run_hook('before_edit', $posted);
  949. // run before_save hook
  950. $this->_run_hook('before_save', $posted);
  951. if ($this->model->save($posted))
  952. {
  953. // process $_FILES...
  954. if ( ! $this->_process_uploads($posted))
  955. {
  956. return FALSE;
  957. }
  958. $this->model->on_after_post($posted);
  959. if ( ! $this->model->is_valid())
  960. {
  961. add_errors($this->model->get_errors());
  962. }
  963. else
  964. {
  965. // archive data
  966. $archive_data = $this->model->cleaned_data();
  967. if ($this->archivable) $this->model->archive($id, $archive_data);
  968. $data = $this->model->find_one_array(array($this->model->table_name().'.'.$this->model->key_field() => $id));
  969. // run after_edit hook
  970. $this->_run_hook('after_edit', $data);
  971. // run after_save hook
  972. $this->_run_hook('after_save', $data);
  973. $msg_data = $this->model->display_name($data);
  974. $msg = lang('module_edited', $this->module_name, $msg_data);
  975. $this->fuel->logs->write($msg);
  976. $this->_clear_cache();
  977. return TRUE;
  978. }
  979. }
  980. return FALSE;
  981. }
  982. // --------------------------------------------------------------------
  983. /**
  984. * Sanitizes the input based on the module's settings
  985. *
  986. * @access protected
  987. * @param array The array of posted data to sanitize
  988. * @return array
  989. */
  990. protected function _sanitize($data)
  991. {
  992. $posted = $data;
  993. if ( ! empty($this->sanitize_input))
  994. {
  995. // functions that are valid for sanitizing
  996. $valid_funcs = $this->fuel->config('module_sanitize_funcs');
  997. if ($this->sanitize_input === TRUE)
  998. {
  999. foreach($data as $key => $val)
  1000. {
  1001. if ( ! empty($val))
  1002. {
  1003. $posted[$key] = xss_clean($val);
  1004. }
  1005. }
  1006. }
  1007. else
  1008. {
  1009. // force to array to normalize
  1010. $sanitize_input = (array) $this->sanitize_input;
  1011. if (is_array($data))
  1012. {
  1013. foreach($data as $key => $post)
  1014. {
  1015. if (is_array($post))
  1016. {
  1017. $posted[$key] = $this->_sanitize($data[$key]);
  1018. }
  1019. else
  1020. {
  1021. // loop through sanitization functions
  1022. foreach($sanitize_input as $func)
  1023. {
  1024. $func = (isset($valid_funcs[$func])) ? $valid_funcs[$func] : FALSE;
  1025. if ($func)
  1026. {
  1027. $posted[$key] = $func($posted[$key]);
  1028. }
  1029. }
  1030. }
  1031. }
  1032. }
  1033. else
  1034. {
  1035. // loop through sanitization functions
  1036. foreach($sanitize_input as $key => $val)
  1037. {
  1038. $func = (isset($valid_funcs[$val])) ? $valid_funcs[$val] : FALSE;
  1039. if ($func)
  1040. {
  1041. $posted = $func($posted);
  1042. }
  1043. }
  1044. }
  1045. }
  1046. }
  1047. return $posted;
  1048. }
  1049. // --------------------------------------------------------------------
  1050. /**
  1051. * Returns an array of shell variables to apply to the main area of the page
  1052. *
  1053. * @access protected
  1054. * @param int The ID value of the record to edit
  1055. * @param string The name of the action to apply to the main form element
  1056. * @param array An array of data information
  1057. * @return array
  1058. */
  1059. protected function _shell_vars($id = NULL, $action = 'create', $data = array())
  1060. {
  1061. $model = $this->model;
  1062. $this->js_controller_params['method'] = 'add_edit';
  1063. $this->js_controller_params['linked_fields'] = $this->model->linked_fields;
  1064. // other variables
  1065. if (method_exists($this->model, 'vars'))
  1066. {
  1067. $model_vars = $this->model->vars($data);
  1068. $this->load->vars($model_vars);
  1069. }
  1070. $vars['id'] = $id;
  1071. $vars['versions'] = ($this->displayonly === FALSE AND $this->archivable) ? $this->fuel_archives_model->options_list($id, $this->model->table_name()) : array();
  1072. $vars['others'] = $this->model->get_others($this->display_field, $id);
  1073. $vars['action'] = $action;
  1074. $vars['notifications'] = $this->load->module_view(FUEL_FOLDER, '_blocks/notifications', $vars, TRUE);
  1075. return $vars;
  1076. }
  1077. // --------------------------------------------------------------------
  1078. /**
  1079. * Returns an array of saved data based on the id value passed
  1080. *
  1081. * @access protected
  1082. * @param int The ID value of the record to edit
  1083. * @return array
  1084. */
  1085. protected function _saved_data($id)
  1086. {
  1087. if (empty($id)) return array();
  1088. $edit_method = $this->edit_method;
  1089. if ($edit_method != 'find_one_array')
  1090. {
  1091. $saved = $this->model->$edit_method($id);
  1092. }
  1093. else
  1094. {
  1095. $saved = $this->model->$edit_method(array($this->model->table_name().'.'.$this->model->key_field() => $id));
  1096. }
  1097. return $saved;
  1098. }
  1099. // separated to make it easier in subclasses to use the form without rendering the page
  1100. protected function _form_vars($id = NULL, $values = array(), $field = NULL, $inline = FALSE)
  1101. {
  1102. $this->load->library('form_builder');
  1103. // load custom fields
  1104. $this->form_builder->load_custom_fields(APPPATH.'config/custom_fields.php');
  1105. $model = $this->model;
  1106. $this->js_controller_params['method'] = 'add_edit';
  1107. $action = (!empty($values[$this->model->key_field()])) ? 'edit' : 'create';
  1108. // create fields... start with the table info and go from there
  1109. $fields = (!empty($values)) ? $this->model->form_fields($values) : $this->model->form_fields($_POST);
  1110. // if it's an object, then extract
  1111. if ($fields instanceof Base_model_fields)
  1112. {
  1113. $fields = $fields->get_fields();
  1114. }
  1115. // if field parameter is set, then we just display a single field
  1116. if ( ! empty($field) AND ! is_numeric($field))
  1117. {
  1118. // added per pierlo in Forum (http://www.getfuelcms.com/forums/discussion/673/fuel_helper-fuel_edit-markers)
  1119. $columns = explode(':', $field);
  1120. // special case if you use the word required
  1121. if (in_array('required', $columns))
  1122. {
  1123. $columns = array_merge($columns, $this->model->required);
  1124. }
  1125. // set them to hidden... just in case model hooks require the values to be passed on save
  1126. foreach($fields as $k => $f)
  1127. {
  1128. if ( ! in_array($k, $columns))
  1129. {
  1130. $fields[$k]['type'] = 'hidden';
  1131. }
  1132. if (count($columns) <= 1)
  1133. {
  1134. $fields[$k]['display_label'] = FALSE;
  1135. $fields[$k]['required'] = FALSE;
  1136. }
  1137. }
  1138. }
  1139. // set published/active to hidden since setting this is an buttton/action instead of a form field
  1140. $form = '';
  1141. if (is_array($fields))
  1142. {
  1143. $field_values = ( ! empty($_POST)) ? $_POST : $values;
  1144. $published_active = array(
  1145. 'publish' => 'published',
  1146. 'active' => 'activate'
  1147. );
  1148. foreach($published_active as $k => $v)
  1149. {
  1150. if ( ! $this->fuel->auth->has_permission($this->permission, $k))
  1151. {
  1152. unset($fields[$v]);
  1153. }
  1154. if (isset($fields[$v]) AND !empty($values[$v]))
  1155. {
  1156. $fields['published']['value'] = $values[$v];
  1157. }
  1158. }
  1159. $this->form_builder->set_validator($this->model->get_validation());
  1160. // add hidden field with the module name for convenience
  1161. $common_fields = $this->_common_fields($field_values);
  1162. $fields = array_merge($fields, $common_fields);
  1163. $fields['__fuel_inline_action__'] = array('type' => 'hidden');
  1164. $fields['__fuel_inline_action__']['class'] = '__fuel_inline_action__';
  1165. $fields['__fuel_inline_action__']['value'] = (empty($id)) ? 'create' : 'edit';
  1166. $fields['__fuel_inline__'] = array('type' => 'hidden');
  1167. $fields['__fuel_inline__']['value'] = ($inline) ? 1 : 0;
  1168. $this->form_builder->submit_value = lang('btn_save');
  1169. $this->form_builder->question_keys = array();
  1170. $this->form_builder->use_form_tag = FALSE;
  1171. if ($this->model->has_auto_increment())
  1172. {
  1173. $this->form_builder->hidden = (array) $this->model->key_field();
  1174. }
  1175. if ($this->config->item('date_format'))
  1176. {
  1177. $this->form_builder->date_format = $this->config->item('date_format');
  1178. }
  1179. if ($inline)
  1180. {
  1181. $this->form_builder->cancel_value = lang('viewpage_close');
  1182. }
  1183. else
  1184. {
  1185. $this->form_builder->cancel_value = lang('btn_cancel');
  1186. }
  1187. $this->form_builder->set_fields($fields);
  1188. $this->form_builder->display_errors = FALSE;
  1189. $this->form_builder->set_field_values($field_values);
  1190. // we will set this in the BaseFuelController.js file so that the jqx page variable is available upon execution of any form field js
  1191. //$this->form_builder->auto_execute_js = FALSE;
  1192. if (!isset($fields['__FORM_BUILDER__'], $fields['__FORM_BUILDER__']['displayonly']))
  1193. {
  1194. $this->form_builder->displayonly = $this->displayonly;
  1195. }
  1196. $this->_prep_csrf();
  1197. $form = $this->form_builder->render();
  1198. }
  1199. $action_uri = (!empty($id)) ? $action.'/'.$id.'/'.$field : $action.'/'.$field;
  1200. $vars['form_action'] = ($inline) ? $this->module_uri.'/inline_'.$action_uri.query_str() : $this->module_uri.'/'.$action_uri.query_str();
  1201. $vars['form'] = $form;
  1202. $vars['data'] = $values;
  1203. $vars['error'] = $this->model->get_errors();
  1204. $vars['notifications'] = $this->load->module_view(FUEL_FOLDER, '_blocks/notifications', $vars, TRUE);
  1205. $vars['instructions'] = (empty($field)) ? $this->instructions : '';
  1206. $vars['field'] = (!empty($field));
  1207. return $vars;
  1208. }
  1209. protected function _process()
  1210. {
  1211. $this->load->helper('security');
  1212. $this->load->library('form_builder');
  1213. // XSS key check
  1214. if (!$this->_is_valid_csrf())
  1215. {
  1216. add_error(lang('error_saving'));
  1217. }
  1218. $this->_orig_post = $_POST;
  1219. // filter placeholder $_POST values
  1220. $callback = function($matches){
  1221. if (isset($_POST[$matches["2"]]))
  1222. {
  1223. $str = $matches[1].$_POST[$matches["2"]].$matches[3];
  1224. }
  1225. else
  1226. {
  1227. $str = $matches[0];
  1228. }
  1229. return $str;
  1230. };
  1231. // first loop through and create simple non-namespaced $_POST values if they don't exist for convenience'
  1232. foreach($_POST as $key => $val)
  1233. {
  1234. $key_parts = explode('--', $key);
  1235. $tmp_key = end($key_parts);
  1236. $_POST[$tmp_key] = $val;
  1237. }
  1238. // now loop through and do any substitution
  1239. foreach($_POST as $key => $val)
  1240. {
  1241. if (is_string($val))
  1242. {
  1243. $_POST[$key] = preg_replace_callback('#(.*)\{(.+)\}(.*)#U', $callback, $val);
  1244. }
  1245. }
  1246. // set boolean fields
  1247. if ( ! empty($this->model->boolean_fields) AND is_array($this->model->boolean_fields))
  1248. {
  1249. foreach($this->model->boolean_fields as $val)
  1250. {
  1251. $_POST[$val] = (isset($_POST[$val])) ? $_POST[$val] : 0;
  1252. }
  1253. }
  1254. // if no permission to publish, then we revoke
  1255. if ( ! $this->fuel->auth->has_permission($this->permission, 'publish'))
  1256. {
  1257. unset($_POST['published']);
  1258. }
  1259. // set key_field if it is not id
  1260. if ( ! empty($_POST['id']) AND $this->model->key_field() != 'id')
  1261. {
  1262. $_POST[$this->model->key_field()] = $_POST['id'];
  1263. }
  1264. // run any form field post processing hooks
  1265. $this->load->library('form_builder');
  1266. // use a new instance to prevent problems when duplicating
  1267. $fb = new Form_builder();
  1268. $fb->load_custom_fields(APPPATH.'config/custom_fields.php');
  1269. // $fields = $this->model->form_fields($_POST);
  1270. $fields = $this->_block_processing($this->model->form_fields(), $_POST);
  1271. $fb->set_fields($fields);
  1272. $fb->post_process_field_values();// manipulates the $_POST values directly
  1273. // sanitize input if set in module configuration
  1274. $posted = $this->_sanitize($_POST);
  1275. return $posted;
  1276. }
  1277. public function _block_processing($fields, $posted)
  1278. {
  1279. // grab the fields array if using the form fields class
  1280. if (is_object($fields) && ($fields instanceof Base_model_fields)) {
  1281. $fields = $fields->get_fields();
  1282. }
  1283. // add in block fields
  1284. foreach($fields as $key => $val)
  1285. {
  1286. // check blocks for post processing of variables
  1287. if (isset($val['type']) AND $val['type'] == 'block' AND isset($posted[$key]['block_name']))
  1288. {
  1289. $block_layout = $this->fuel->layouts->get($posted[$key]['block_name'], 'block');
  1290. if ($block_layout)
  1291. {
  1292. $block_fields = $block_layout->fields();
  1293. $fields = array_merge($fields, $block_fields);
  1294. }
  1295. }
  1296. // check for template layouts that may have nested fields... this is really ugly
  1297. if ( ! empty($val['fields']) AND is_array($val['fields']))
  1298. {
  1299. //$fields = array_merge($fields, $val['fields']);
  1300. foreach($val['fields'] as $k => $v)
  1301. {
  1302. if (isset($v['type']) AND $v['type'] == 'block' AND isset($posted[$key]))
  1303. {
  1304. if (is_array($posted[$key]) AND is_int(key($posted[$key])))
  1305. {
  1306. foreach($posted[$key] as $a => $b)
  1307. {
  1308. if (is_array($b))
  1309. {
  1310. foreach($b as $c => $d)
  1311. {
  1312. if (isset($d['block_name']))
  1313. {
  1314. $block_layout = $this->fuel->layouts->get($d['block_name'], 'block');
  1315. if ($block_layout)
  1316. {
  1317. $block_fields = $block_layout->fields();
  1318. // now switch out the key to allow it to trigger the post_process_callback...
  1319. foreach($block_fields as $e => $f)
  1320. {
  1321. $block_fields[$e]['subkey'] = $k;
  1322. $block_fields[$e]['key'] = $key;
  1323. }
  1324. $fields = array_merge($fields, $block_fields);
  1325. }
  1326. }
  1327. }
  1328. }
  1329. }
  1330. }
  1331. }
  1332. }
  1333. }
  1334. }
  1335. return $fields;
  1336. }
  1337. function form($id = NULL, $field = NULL)
  1338. {
  1339. $saved = $this->_saved_data($id);
  1340. $vars = $this->_form_vars($id, $saved, $field);
  1341. $this->load->module_view(FUEL_FOLDER, '_layouts/module_form', $vars);
  1342. }
  1343. function delete($id = NULL)
  1344. {
  1345. // check that the action even exists and if not, show a 404
  1346. if ( ! $this->fuel->auth->module_has_action('delete')) show_404();
  1347. if ( ! $this->fuel->auth->has_permission($this->permission, 'delete'))
  1348. {
  1349. show_error(lang('error_no_permissions', fuel_url()));
  1350. }
  1351. $inline = $this->fuel->admin->is_inline();
  1352. if ( ! empty($_POST['id']))
  1353. {
  1354. $posted = explode('|', $this->input->post('id', TRUE));
  1355. // run before_delete hook
  1356. $this->_run_hook('before_delete', $posted);
  1357. // Flags
  1358. $any_success = $any_failure = FALSE;
  1359. foreach ($posted as $id)
  1360. {
  1361. if ($this->model->delete(array($this->model->table_name().'.'.$this->model->key_field() => $id)))
  1362. {
  1363. $any_success = TRUE;
  1364. }
  1365. else
  1366. {
  1367. $any_failure = TRUE;
  1368. }
  1369. }
  1370. // run after_delete hook
  1371. $this->_run_hook('after_delete', $posted);
  1372. $this->_clear_cache();
  1373. if (count($posted) > 1)
  1374. {
  1375. $this->fuel->logs->write(lang('module_multiple_deleted', $this->module));
  1376. }
  1377. else
  1378. {
  1379. $this->fuel->logs->write(lang('module_deleted', count($posted), $this->module));
  1380. }
  1381. if ($inline)
  1382. {
  1383. $vars['title'] = '';
  1384. $vars['id'] = '';
  1385. $vars['back_action'] = '';
  1386. $this->fuel->admin->render('modules/module_close_modal', $vars, '', FUEL_FOLDER);
  1387. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT_TITLEBAR);
  1388. $this->fuel->admin->render($this->views['delete'], $vars, '', FUEL_FOLDER);
  1389. }
  1390. else
  1391. {
  1392. // set a success delete message
  1393. if ($any_success)
  1394. {
  1395. if ( ! $this->session->flashdata('success'))
  1396. {
  1397. $this->fuel->admin->set_notification(lang('data_deleted'), Fuel_admin::NOTIFICATION_SUCCESS);
  1398. }
  1399. }
  1400. // set an error delete message
  1401. if ($any_failure)
  1402. {
  1403. // first try to get an error added in model by $this->add_error('...')
  1404. $msg = $this->model->get_validation()->get_last_error();
  1405. // if there is none like that, lets use default message
  1406. if (is_null($msg)) $msg = lang('data_not_deleted');
  1407. $this->fuel->admin->set_notification($msg, Fuel_admin::NOTIFICATION_ERROR);
  1408. }
  1409. $url = fuel_uri($this->module_uri);
  1410. redirect($url);
  1411. }
  1412. }
  1413. else
  1414. {
  1415. $this->js_controller_params['method'] = 'deleteItem';
  1416. $vars = array();
  1417. if ( ! empty($_POST['delete']) AND is_array($_POST['delete']))
  1418. {
  1419. $data = array();
  1420. foreach($this->input->post('delete') as $key => $val)
  1421. {
  1422. $d = $this->model->find_by_key($key, 'array');
  1423. if ( ! empty($d)) $data[] = $d[$this->display_field];
  1424. }
  1425. $vars['id'] = implode('|', array_keys($_POST['delete']));
  1426. $vars['title'] = implode(', ', $data);
  1427. }
  1428. else
  1429. {
  1430. $data = $this->model->find_by_key($id, 'array');
  1431. $vars['id'] = $id;
  1432. $vars['title'] = $this->model->display_name($data);
  1433. }
  1434. if (empty($data)) show_404();
  1435. $vars['error'] = $this->model->get_errors();
  1436. $crumbs = array($this->module_uri => $this->module_name);
  1437. $crumbs[''] = character_limiter(strip_tags(lang('action_delete').' '.$vars['title']), 100);
  1438. $this->fuel->admin->set_titlebar($crumbs);
  1439. if ($inline)
  1440. {
  1441. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_COMPACT_NO_ACTION);
  1442. $vars['back_action'] = fuel_url($this->module_uri.'/inline_edit/'.$id);
  1443. }
  1444. else
  1445. {
  1446. $this->fuel->admin->set_display_mode(Fuel_admin::DISPLAY_NO_ACTION);
  1447. $vars['back_action'] = fuel_url($this->module_uri.'/');
  1448. }
  1449. $action_uri = 'delete/'.$id;
  1450. $vars['form_action'] = ($inline) ? $this->module_uri.'/inline_'.$action_uri : $this->module_uri.'/'.$action_uri;
  1451. $this->fuel->admin->render($this->views['delete'], $vars, '', FUEL_FOLDER);
  1452. }
  1453. }
  1454. function inline_delete($id)
  1455. {
  1456. $this->fuel->admin->set_inline(TRUE);
  1457. $this->delete($id);
  1458. }
  1459. function restore()
  1460. {
  1461. if ( ! $this->fuel->auth->has_permission($this->permission, 'edit'))
  1462. {
  1463. show_error(lang('error_no_permissions', fuel_url()));
  1464. }
  1465. if ( ! empty($_POST['fuel_restore_version']) AND ! empty($_POST['fuel_restore_ref_id']))
  1466. {
  1467. if ( ! $this->model->restore($this->input->post('fuel_restore_ref_id'), $this->input->post('fuel_restore_version')))
  1468. {
  1469. $msg = lang('module_restored', $this->module_name);
  1470. $this->fuel->logs->write($msg);
  1471. $this->fuel->admin->set_notification($this->model->get_validation()->get_last_error(), Fuel_admin::NOTIFICATION_ERROR);
  1472. }
  1473. else
  1474. {
  1475. if ( ! $this->session->flashdata('success'))
  1476. {
  1477. $this->fuel->admin->set_notification(lang('module_restored_success'), Fuel_admin::NOTIFICATION_SUCCESS);
  1478. }
  1479. $this->_clear_cache();
  1480. }
  1481. redirect(fuel_uri($this->module_uri.'/edit/'.$this->input->post('fuel_restore_ref_id', TRUE)));
  1482. }
  1483. else
  1484. {
  1485. show_404();
  1486. }
  1487. }
  1488. function replace($id = NULL)
  1489. {
  1490. if (empty($id)) show_404();
  1491. if ( ! $this->fuel->auth->has_permission($this->permission, 'edit') OR ! $this->fuel->auth->has_permission($this->permission, 'delete'))
  1492. {
  1493. show_error(lang('error_no_permissions', fuel_url()));
  1494. }
  1495. $success = FALSE;
  1496. if ( ! empty($_POST))
  1497. {
  1498. if ( ! empty($_POST['fuel_replace_id']))
  1499. {
  1500. $replace_id = $this->input->post('fuel_replace_id');
  1501. //$delete = is_true_val($this->input->post('fuel_delete_replacement'));
  1502. $delete = TRUE;
  1503. if (!$this->model->replace($replace_id, $id, $delete))
  1504. {
  1505. add_error($this->model->get_validation()->get_last_error());
  1506. }
  1507. else
  1508. {
  1509. $this->fuel->admin->set_notification(lang('module_replaced_success'), Fuel_admin::NOTIFICATION_SUCCESS);
  1510. $success = TRUE;
  1511. $this->_clear_cache();
  1512. }
  1513. }
  1514. else
  1515. {
  1516. add_error(lang('error_select_replacement'));
  1517. }
  1518. //redirect(fuel_uri($this->module_uri.'/edit/'.$id));
  1519. }
  1520. $this->load->library('form_builder');
  1521. $fields = array();
  1522. $other_options = $this->model->get_others($this->display_field, $id);
  1523. $fields['fuel_replace_id'] = array('label' => 'Replace record:', 'type' => 'select', 'options' => $other_options, 'first_option' => 'Select record to replace...', 'style' => 'max-width: 400px', 'disabled_options' => array($id));
  1524. //$fields['fuel_delete_replacement'] = array('label' => 'Delete replacement', 'type' => 'checkbox', 'value' => 'yes');
  1525. if ($success)
  1526. {
  1527. $fields['new_fuel_replace_id'] = array('type' => 'hidden', 'value' => $replace_id);
  1528. }
  1529. //$this->form_builder->use_form_tag = FALSE;
  1530. $this->form_builder->set_fields($fields);
  1531. $this->form_builder->display_errors = FALSE;
  1532. //$this->form_builder->submit_value = NULL;
  1533. $vars['form'] = $this->form_builder->render();
  1534. $this->fuel->admin->set_inline(TRUE);
  1535. $crumbs = array('' => $this->module_name, lang('action_replace'));
  1536. $this->fuel->admin->set_titlebar($crumbs);
  1537. $this->fuel->admin->render('modules/module_replace', $vars, '', FUEL_FOLDER);
  1538. }
  1539. // displays the module's designated view'
  1540. function view($id = NULL)
  1541. {
  1542. if ( ! empty($this->preview_path) AND !empty($id))
  1543. {
  1544. $data = $this->model->find_one_array(array($this->model->table_name().'.'.$this->model->key_field() => $id));
  1545. $url = $this->module_obj->url($data);
  1546. // change the last page to be the referrer
  1547. $last_page = (isset($_SERVER['HTTP_REFERER'])) ? substr($_SERVER['HTTP_REFERER'], strlen(site_url())) : NULL;
  1548. $this->fuel->admin->set_last_page($last_page);
  1549. redirect($url);
  1550. }
  1551. else
  1552. {
  1553. show_error(lang('no_preview_path'));
  1554. }
  1555. }
  1556. // refreshes a single field
  1557. function refresh_field()
  1558. {
  1559. if ( ! empty($_POST))
  1560. {
  1561. $fields = $this->model->form_fields();
  1562. if (is_object($fields) AND $fields instanceof Base_model_fields)
  1563. {
  1564. $fields = $fields->get_fields();
  1565. }
  1566. $field = $this->input->post('field', TRUE);
  1567. if ( ! isset($fields[$field])) return;
  1568. $field_id = $this->input->post('field_id', TRUE);
  1569. $values = $this->input->post('values', TRUE);
  1570. $selected = $this->input->post('selected', TRUE);
  1571. $field_parts = explode('vars--', $field);
  1572. $field_key = end($field_parts);
  1573. $this->load->library('form_builder');
  1574. $this->form_builder->load_custom_fields(APPPATH.'config/custom_fields.php');
  1575. // for multi select
  1576. if (is_array($values))
  1577. {
  1578. $selectedValues = $values;
  1579. if ( ! in_array($selected, $selectedValues))
  1580. {
  1581. $selectedValues[] = $selected;
  1582. }
  1583. $selected = $selectedValues;
  1584. }
  1585. if ( ! empty($selected)) $fields[$field]['value'] = $selected;
  1586. $fields[$field]['name'] = $field_id;
  1587. $output = '';
  1588. // if template/nested field types, then we need to look at the sub field
  1589. if ($fields[$field_key]['type'] == 'template')
  1590. {
  1591. //$fields['return_fields'] = TRUE;
  1592. require_once(FUEL_PATH.'libraries/Fuel_custom_fields.php');
  1593. $fuel_cf = new Fuel_custom_fields();
  1594. $index = $this->input->get_post('index', TRUE);
  1595. $key = $this->input->get_post('key', TRUE);
  1596. $field_name = $this->input->get_post('field_name', TRUE);
  1597. $params = $fields[$field_key];
  1598. $params['index'] = $index;
  1599. $params['name'] = $field_name;
  1600. $params['key'] = $field_name;
  1601. $params['value'] = array();
  1602. $params['value'][0] = $selected;
  1603. $this->form_builder->name_array = $field_name;
  1604. //$fb->set_field_values();
  1605. $params['instance'] =& $this->form_builder;
  1606. $sub_fields = $fuel_cf->template($params, TRUE);
  1607. if ( ! empty($sub_fields[0][$key])) $output = $sub_fields[0][$key];
  1608. }
  1609. else
  1610. {
  1611. if ( ! empty($selected)) $fields[$field_key]['value'] = $selected;
  1612. $fields[$field_key]['name'] = $field_id;
  1613. // if the field is an ID, then we will do a select instead of a text field
  1614. if (isset($fields[$this->model->key_field()]))
  1615. {
  1616. $fields[$this->model->key_field()]['type'] = 'select';
  1617. $fields[$this->model->key_field()]['options'] = $this->model->options_list();
  1618. }
  1619. $output = $this->form_builder->create_field($fields[$field_key]);
  1620. }
  1621. $this->output->set_output($output);
  1622. }
  1623. }
  1624. // processes linked fields
  1625. function process_linked()
  1626. {
  1627. if ( ! empty($_POST))
  1628. {
  1629. $master_field = $this->input->post('master_field', FALSE);
  1630. $master_value = $this->input->post('master_value', FALSE);
  1631. $slave_field = $this->input->post('slave_field', FALSE);
  1632. $values = array(
  1633. $master_field => $master_value,
  1634. $slave_field => '' // blank so we can process
  1635. );
  1636. $processed = $this->model->process_linked($values);
  1637. if ( ! empty($processed[$slave_field]))
  1638. {
  1639. $this->output->set_output($processed[$slave_field]);
  1640. }
  1641. }
  1642. }
  1643. // automatically calls ajax methods on the model
  1644. function ajax($method = NULL)
  1645. {
  1646. // must not be empty and must start with find_ (... don't want to access methods like delete)
  1647. if (is_ajax())
  1648. {
  1649. // append ajax to the method name... to prevent any conflicts with default methods
  1650. $method = 'ajax_'.$method;
  1651. $get = (array) $this->input->get(NULL, TRUE);
  1652. $post = (array) $this->input->post(NULL, TRUE);
  1653. $params = array_filter(array_merge($get, $post));
  1654. if ( ! method_exists($this->model, $method))
  1655. {
  1656. show_error(lang('error_invalid_method'));
  1657. }
  1658. $results = $this->model->$method($params);
  1659. if (is_string($results))
  1660. {
  1661. $this->output->set_output($results);
  1662. }
  1663. else
  1664. {
  1665. $this->output->set_header('Cache-Control: no-cache, must-revalidate');
  1666. $this->output->set_header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
  1667. $this->output->set_header('Last-Modified: '. gmdate('D, d M Y H:i:s').'GMT');
  1668. $this->output->set_header('Content-type: application/json');
  1669. $output = json_encode($results);
  1670. echo $output;
  1671. }
  1672. }
  1673. }
  1674. // exports data to CSV
  1675. function export()
  1676. {
  1677. if (empty($this->exportable)) show_404();
  1678. if ( ! $this->fuel->auth->has_permission($this->permission, 'export'))
  1679. {
  1680. show_error(lang('error_no_permissions', fuel_url()));
  1681. }
  1682. if ( ! empty($_POST))
  1683. {
  1684. // load dbutils for convenience to use in custom methods on model
  1685. $this->load->dbutil();
  1686. $this->load->helper('download');
  1687. $filters = $this->model->filters($this->filters);
  1688. if (is_object($filters) && ($filters instanceof Base_model_fields)) {
  1689. $filters = $filters->get_fields();
  1690. }
  1691. $this->filters = array_merge($this->filters, $filters);
  1692. $filename = $this->module.'_'.date('Y-m-d').'.csv';
  1693. $params = $this->_list_process();
  1694. $this->_filter_list($params);
  1695. $data = $this->model->export_data($params);
  1696. force_download($filename, $data);
  1697. }
  1698. }
  1699. // used in list view to quickly unpublish (if they have permission)
  1700. function toggle_on($id = NULL, $field = 'published')
  1701. {
  1702. $this->_toggle($id, $field, 'on');
  1703. }
  1704. // used in list view to quickly publish (if they have permission)
  1705. function toggle_off($id = NULL, $field = 'published')
  1706. {
  1707. $this->_toggle($id, $field, 'off');
  1708. }
  1709. // reduce code by creating this shortcut function for the unpublish/publish
  1710. function _toggle($id, $field, $toggle)
  1711. {
  1712. if ( ! $this->fuel->auth->module_has_action('save') OR ($field == 'publish' AND !$this->fuel->auth->has_permission($this->permission, 'publish')))
  1713. {
  1714. return FALSE;
  1715. }
  1716. if (empty($id))
  1717. {
  1718. $id = $this->input->post($this->model->key_field());
  1719. }
  1720. if ($id)
  1721. {
  1722. $save = $this->model->find_by_key($id, 'array');
  1723. $field_info = $this->model->field_info($field);
  1724. if ( ! empty($save))
  1725. {
  1726. if ($toggle == 'on')
  1727. {
  1728. $save[$field] = ($field_info['type'] != 'enum') ? 1 : 'yes';
  1729. }
  1730. else
  1731. {
  1732. $save[$field] = ($field_info['type'] != 'enum') ? 0 : 'no';
  1733. }
  1734. // run before_edit hook
  1735. $this->_run_hook('before_edit', $save);
  1736. // run before_save hook
  1737. $this->_run_hook('before_save', $save);
  1738. $save = $this->model->clean($save);
  1739. $where[$this->model->key_field()] = $id;
  1740. // use update instead of save to avoid issue with has_many and belongs_to being removed
  1741. if ($this->model->update($save, $where))
  1742. {
  1743. // clear cache
  1744. $this->_clear_cache();
  1745. // log it
  1746. $data = $this->model->find_by_key($id, 'array');
  1747. // run after_edit hook
  1748. $this->_run_hook('after_edit', $data);
  1749. // run after_save hook
  1750. $this->_run_hook('after_save', $data);
  1751. $msg_data = $this->model->display_name($data);
  1752. $msg = lang('module_edited', $this->module_name, $msg_data);
  1753. $this->fuel->logs->write($msg);
  1754. }
  1755. else
  1756. {
  1757. $this->output->set_output(lang('error_saving'));
  1758. }
  1759. }
  1760. }
  1761. if (is_ajax())
  1762. {
  1763. $this->output->set_output($toggle);
  1764. }
  1765. else
  1766. {
  1767. $this->items();
  1768. }
  1769. }
  1770. protected function _clear_cache()
  1771. {
  1772. // reset cache for that page only
  1773. if ($this->clear_cache_on_save)
  1774. {
  1775. $this->fuel->cache->clear_pages();
  1776. }
  1777. }
  1778. protected function _allow_action($action)
  1779. {
  1780. return in_array($action, $this->item_actions);
  1781. }
  1782. protected function _common_fields($values)
  1783. {
  1784. $fields['__fuel_module__'] = array('type' => 'hidden');
  1785. $fields['__fuel_module__']['value'] = $this->module;
  1786. $fields['__fuel_module__']['class'] = '__fuel_module__';
  1787. $fields['__fuel_module_uri__'] = array('type' => 'hidden');
  1788. $fields['__fuel_module_uri__']['value'] = $this->module_uri;
  1789. $fields['__fuel_module_uri__']['class'] = '__fuel_module_uri__';
  1790. $fields['__fuel_id__'] = array('type' => 'hidden');
  1791. $fields['__fuel_id__']['value'] = (!empty($values[$this->model->key_field()])) ? $values[$this->model->key_field()] : '';
  1792. $fields['__fuel_id__']['class'] = '__fuel_id__';
  1793. return $fields;
  1794. }
  1795. protected function _save_tab_state($id)
  1796. {
  1797. // set tab
  1798. if (isset($_POST['__fuel_selected_tab__']))
  1799. {
  1800. if ( ! empty($_COOKIE['fuel_tabs']))
  1801. {
  1802. $tab_cookie = json_decode(urldecode($_COOKIE['fuel_tabs']), TRUE);
  1803. if ( ! empty($tab_cookie))
  1804. {
  1805. $tab_cookie[$this->module.'_edit_'.$id] = $_POST['__fuel_selected_tab__'];
  1806. $cookie_val = urlencode(json_encode($tab_cookie));
  1807. // set the cookie for viewing the live site with added FUEL capabilities
  1808. $config = array(
  1809. 'name' => 'fuel_tabs',
  1810. 'value' => $cookie_val,
  1811. 'expire' => 0,
  1812. //'path' => WEB_PATH
  1813. 'path' => $this->fuel->config('fuel_cookie_path')
  1814. );
  1815. set_cookie($config);
  1816. }
  1817. }
  1818. }
  1819. }
  1820. protected function _process_uploads($posted = NULL)
  1821. {
  1822. if (empty($posted)) $posted = $_POST;
  1823. $errors = FALSE;
  1824. if ( ! empty($_FILES))
  1825. {
  1826. $field_names = array();
  1827. // loop through uploaded files
  1828. foreach ($_FILES as $file => $file_info)
  1829. {
  1830. if ($file_info['error'] == 0)
  1831. {
  1832. $posted[$file] = $file_info['name'];
  1833. $file_tmp = current(explode('___', $file));
  1834. $field_name = $file_tmp;
  1835. // if there is a field with the suffix of _upload, then we will overwrite that posted value with this value
  1836. if (substr($file_tmp, strlen($file_tmp) - 7) == '_upload')
  1837. {
  1838. $field_name = substr($file_tmp, 0, strlen($file_tmp) - 7);
  1839. }
  1840. if (isset($posted[$file_tmp.'_file_name']) AND isset($this->_orig_post[$file_tmp.'_file_name']))
  1841. {
  1842. // get file extension
  1843. $path_info = pathinfo($file_info['name']);
  1844. $field_value = $this->_orig_post[$file_tmp.'_file_name'].'.'.$path_info['extension'];
  1845. }
  1846. else
  1847. {
  1848. $field_value = $file_info['name'];
  1849. }
  1850. // look for repeatable values that match
  1851. if (preg_match('#(.+)_(\d+)_(.+)#', $file_tmp, $matches))
  1852. {
  1853. if (isset($posted[$matches[1]][$matches[2]][$matches[3]]))
  1854. {
  1855. $posted[$matches[1]][$matches[2]][$matches[3]] = $posted[$file];
  1856. }
  1857. }
  1858. if (strpos($field_value, '{') !== FALSE )
  1859. {
  1860. //e modifier is deprecated so we have to do this
  1861. $callback = function($match){
  1862. $return = "";
  1863. if (!empty($match[2]))
  1864. {
  1865. $return = $match[1].$GLOBALS["__tmp_transient_posted__"][$match[2]].$match[3];
  1866. }
  1867. return $return;
  1868. };
  1869. // hacky but avoids 5.3 function syntax (which is nicer but doesn't work with 5.2)
  1870. $GLOBALS['__tmp_transient_posted__'] = $posted;
  1871. $field_value = preg_replace_callback('#^(.*)\{(.+)\}(.*)$#', $callback, $field_value);
  1872. }
  1873. // set both values for the namespaced and non-namespaced... make them underscored and lower cased
  1874. $field_name_parts = explode('--', $field_name);
  1875. $tmp_field_name = end($field_name_parts);
  1876. $file_name = pathinfo($field_value, PATHINFO_FILENAME);
  1877. $file_ext = pathinfo($field_value, PATHINFO_EXTENSION);
  1878. //$file_val = url_title($file_name, 'underscore', FALSE).'.'.$file_ext;
  1879. $file_val = $file_name.'.'.$file_ext;
  1880. $posted[$tmp_field_name] = $file_val;
  1881. $posted[$field_name] = $file_val;
  1882. $posted[$file_tmp.'_file_name'] = $file_val;
  1883. $field_names[$field_name] = $field_name;
  1884. }
  1885. }
  1886. // hacky cleanup to avoid using 5.3 syntax
  1887. if (isset($GLOBALS["__tmp_transient_posted__"]))
  1888. {
  1889. unset($GLOBALS["__tmp_transient_posted__"]);
  1890. }
  1891. $params['xss_clean'] = $this->sanitize_files;
  1892. $params['posted'] = $posted;
  1893. // UPLOAD!!!
  1894. if ( ! $this->fuel->assets->upload($params))
  1895. {
  1896. $errors = TRUE;
  1897. $msg = $this->fuel->assets->last_error();
  1898. add_error($msg);
  1899. $this->fuel->admin->set_notification($msg, Fuel_admin::NOTIFICATION_ERROR);
  1900. }
  1901. else
  1902. {
  1903. // do post processing of updating field values if they changed during upload due to overwrite being FALSE
  1904. $uploaded_data = $this->fuel->assets->uploaded_data();
  1905. // transfer uploaded data info to the model
  1906. $this->model->upload_data =& $uploaded_data;
  1907. // transfer uploaded data the controller object as well
  1908. $this->upload_data =& $uploaded_data;
  1909. // now process the data related to upload a file including translated path names
  1910. if ( ! isset($field_name)) $field_name = '';
  1911. $this->_process_upload_data($field_names, $uploaded_data, $posted);
  1912. }
  1913. }
  1914. return ! $errors;
  1915. }
  1916. protected function _process_upload_data($field_names, $uploaded_data, $posted)
  1917. {
  1918. foreach($uploaded_data as $key => $val)
  1919. {
  1920. if (!isset($field_names[$key]))
  1921. {
  1922. continue;
  1923. }
  1924. $field_name = $field_names[$key];
  1925. $field_name_parts = explode('--', $field_name);
  1926. $field_name = end($field_name_parts);
  1927. $save = FALSE;
  1928. $key_parts = explode('___', $key);
  1929. $file_tmp = current($key_parts);
  1930. // get the file name field
  1931. // if the file name field exists AND there is no specified hidden filename field to assign to it AND...
  1932. // the model does not have an array key field AND there is a key field value posted
  1933. if (isset($field_name) AND ! is_array($this->model->key_field()) AND isset($posted[$this->model->key_field()]))
  1934. {
  1935. $id = $posted[$this->model->key_field()];
  1936. $data = $this->model->find_one_array(array($this->model->table_name().'.'.$this->model->key_field() => $id));
  1937. // if there is a field with the suffix of _upload, then we will overwrite that posted value with this value
  1938. if (substr($file_tmp, strlen($file_tmp) - 7) == '_upload')
  1939. {
  1940. $field_name = substr($file_tmp, 0, strlen($file_tmp) - 7);
  1941. }
  1942. if (isset($posted[$field_name])) $save = TRUE;
  1943. // look for repeatable values that match
  1944. if (preg_match('#(.+)_(\d+)_(.+)#', $file_tmp, $matches))
  1945. {
  1946. if (isset($posted[$matches[1]][$matches[2]][$matches[3]]) AND isset($data[$matches[1]][$matches[2]][$matches[3]]))
  1947. {
  1948. //$data[$matches[1]][$matches[2]][$matches[3]] = $posted[$file_tmp];
  1949. $data[$matches[1]][$matches[2]][$matches[3]] = $val['file_name'];
  1950. $save = TRUE;
  1951. }
  1952. }
  1953. if ($save)
  1954. {
  1955. $data[$field_name] = $val['file_name'];
  1956. // reset any validation to prevent issues with saving again (e.g. unique fields and the is_new function is problematic)
  1957. $this->model->remove_all_validation($data);
  1958. $this->model->save($data);
  1959. }
  1960. }
  1961. }
  1962. }
  1963. protected function _run_hook($hook, $params = array())
  1964. {
  1965. // call module specific hook
  1966. $hook_name = $hook.'_'.$this->module;
  1967. $GLOBALS['EXT']->_call_hook($hook_name, $params);
  1968. // call global module hook if any
  1969. $hook_name = $hook.'_module';
  1970. $GLOBALS['EXT']->_call_hook($hook_name, $params);
  1971. }
  1972. }