PageRenderTime 37ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/system/expressionengine/modules/safecracker/libraries/safecracker_lib.php

https://bitbucket.org/studiobreakfast/sync
PHP | 2941 lines | 1986 code | 509 blank | 446 comment | 355 complexity | 3094cd04c9f30ab50f02ad212d6b7c97 MD5 | raw file
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * ExpressionEngine - by EllisLab
  4. *
  5. * @package ExpressionEngine
  6. * @author EllisLab Dev Team,
  7. * - Original Development by Barrett Newton -- http://barrettnewton.com
  8. * @copyright Copyright (c) 2003 - 2012, EllisLab, Inc.
  9. * @license http://expressionengine.com/user_guide/license.html
  10. * @link http://expressionengine.com
  11. * @since Version 2.0
  12. * @filesource
  13. */
  14. // ------------------------------------------------------------------------
  15. /**
  16. * ExpressionEngine SafeCracker Module Library
  17. *
  18. * @package ExpressionEngine
  19. * @subpackage Libraries
  20. * @category Modules
  21. * @author EllisLab Dev Team
  22. * @link http://expressionengine.com
  23. */
  24. class Safecracker_lib
  25. {
  26. public $initialized = FALSE;
  27. public $form_error = FALSE;
  28. public $site_id;
  29. public $categories;
  30. public $channel;
  31. public $checkboxes;
  32. public $custom_field_conditional_names;
  33. public $custom_fields;
  34. public $custom_option_fields;
  35. public $date_fields;
  36. public $datepicker;
  37. public $default_fields;
  38. public $edit;
  39. public $entry;
  40. public $error_handling;
  41. public $errors;
  42. public $field_errors;
  43. public $file;
  44. public $file_fields;
  45. public $form_validation_methods;
  46. public $head;
  47. public $json;
  48. public $logged_out_member_id;
  49. public $logged_out_group_id;
  50. public $native_option_fields;
  51. public $native_variables;
  52. public $option_fields;
  53. public $parse_variables;
  54. public $pre_save;
  55. public $preserve_checkboxes;
  56. public $post_error_callbacks;
  57. public $require_save_call;
  58. public $settings;
  59. public $skip_xss_fieldtypes;
  60. public $skip_xss_field_ids;
  61. public $statuses;
  62. public $show_fields;
  63. public $title_fields;
  64. public $valid_callbacks;
  65. public $lang, $api_channel_fields, $form_validation;
  66. /**
  67. * constructor
  68. *
  69. * @return void
  70. */
  71. public function __construct()
  72. {
  73. $this->EE =& get_instance();
  74. //set a global object
  75. $this->EE->safecracker = $this;
  76. }
  77. // --------------------------------------------------------------------
  78. /**
  79. * Creates the entry form
  80. *
  81. * @return string
  82. */
  83. public function entry_form()
  84. {
  85. //can't be used in a form action
  86. if ( ! isset($this->EE->TMPL))
  87. {
  88. return '';
  89. }
  90. $this->EE->lang->loadfile('safecracker');
  91. if ( ! isset($this->EE->extensions->extensions['form_declaration_modify_data'][10]['Safecracker_ext']))
  92. {
  93. return $this->EE->output->show_user_error(FALSE, lang('safecracker_extension_not_installed'));
  94. }
  95. // -------------------------------------------
  96. // 'safecracker_entry_form_tagdata_start' hook.
  97. // - Developers, if you want to modify the $this object remember
  98. // to use a reference on func call.
  99. //
  100. if ($this->EE->extensions->active_hook('safecracker_entry_form_absolute_start') === TRUE)
  101. {
  102. $this->EE->extensions->call('safecracker_entry_form_absolute_start');
  103. if ($this->EE->extensions->end_script === TRUE) return;
  104. }
  105. $this->fetch_site($this->EE->TMPL->fetch_param('site'));
  106. $this->initialize(empty($this->form_error));
  107. $this->EE->load->helper('form');
  108. $this->EE->router->set_class('cp');
  109. $this->EE->load->library('cp');
  110. $this->EE->router->set_class('ee');
  111. $this->EE->load->library('javascript');
  112. $this->EE->load->library('api');
  113. $this->EE->load->library('form_validation');
  114. $this->EE->api->instantiate('channel_fields');
  115. $this->load_channel_standalone();
  116. $this->EE->lang->loadfile('content');
  117. $this->EE->lang->loadfile('upload');
  118. $this->EE->javascript->output('if (typeof SafeCracker == "undefined" || ! SafeCracker) { var SafeCracker = {markItUpFields:{}};}');
  119. // Figure out what channel we're working with
  120. $this->fetch_channel($this->EE->TMPL->fetch_param('channel_id'), $this->EE->TMPL->fetch_param('channel'));
  121. if ( ! $this->channel)
  122. {
  123. return $this->EE->output->show_user_error('submission', lang('safecracker_no_channel'));
  124. }
  125. //get the entry data, if an entry was specified
  126. $this->fetch_entry($this->EE->TMPL->fetch_param('entry_id'), $this->EE->TMPL->fetch_param('url_title'));
  127. // Whoa there big conditional, what's going on here?
  128. // We want to make sure no one's being tricky here and supplying
  129. // an invalid entry_id or url_title via a segment, so we need to
  130. // check to see if either exists and if it does make sure that the
  131. // passed in version is the same as what we find in the database.
  132. // If they are different (most likely it wasn't found in the
  133. // database) then don't show them the form
  134. if (
  135. ($this->EE->TMPL->fetch_param('entry_id') != '' AND
  136. $this->entry('entry_id') != $this->EE->TMPL->fetch_param('entry_id')) OR
  137. ($this->EE->TMPL->fetch_param('url_title') != '' AND
  138. $this->entry('url_title') != $this->EE->TMPL->fetch_param('url_title'))
  139. )
  140. {
  141. if ($this->EE->TMPL->no_results())
  142. {
  143. return $this->EE->TMPL->no_results();
  144. }
  145. return $this->EE->output->show_user_error(FALSE, lang('safecracker_require_entry'));
  146. }
  147. // @added rev 57
  148. if ( ! $this->entry('entry_id') && $this->bool_string($this->EE->TMPL->fetch_param('require_entry')))
  149. {
  150. if ($this->EE->TMPL->no_results())
  151. {
  152. return $this->EE->TMPL->no_results();
  153. }
  154. return $this->EE->output->show_user_error(FALSE, lang('safecracker_require_entry'));
  155. }
  156. if ($this->entry('entry_id') && ! $this->form_error)
  157. {
  158. $this->edit = TRUE;
  159. }
  160. // @added rev 57
  161. if ($this->edit && $this->bool_string($this->EE->TMPL->fetch_param('author_only')) && $this->entry('author_id') != $this->EE->session->userdata('member_id'))
  162. {
  163. return $this->EE->output->show_user_error(FALSE, lang('safecracker_author_only'));
  164. }
  165. if (is_array($this->entry('category')))
  166. {
  167. $this->entry['categories'] = $this->entry('category');
  168. }
  169. //add hidden field data
  170. $this->form_hidden(
  171. array(
  172. 'ACT' => $this->EE->functions->fetch_action_id('Safecracker', 'submit_entry'),
  173. 'site_id' => $this->site_id,
  174. 'return' => ($this->EE->TMPL->fetch_param('return_'.$this->EE->session->userdata('group_id'))) ? $this->EE->TMPL->fetch_param('return_'.$this->EE->session->userdata('group_id')) : $this->EE->TMPL->fetch_param('return'),
  175. 'json' => $this->bool_string($this->EE->TMPL->fetch_param('json')) ? 1 : FALSE,
  176. 'dynamic_title' => ($this->EE->TMPL->fetch_param('dynamic_title')) ? base64_encode($this->EE->TMPL->fetch_param('dynamic_title')) : FALSE,
  177. 'error_handling' => ($this->EE->TMPL->fetch_param('error_handling')) ? $this->EE->TMPL->fetch_param('error_handling') : FALSE,
  178. 'preserve_checkboxes' => ($this->EE->TMPL->fetch_param('preserve_checkboxes')) ? $this->EE->TMPL->fetch_param('preserve_checkboxes') : FALSE,
  179. 'secure_return' => $this->bool_string($this->EE->TMPL->fetch_param('secure_return')) ? 1 : FALSE,
  180. 'allow_comments' => $this->bool_string($this->EE->TMPL->fetch_param('allow_comments'), $this->channel['comment_system_enabled']) == 'y' ? 'y' : 'n'
  181. )
  182. );
  183. unset($this->EE->TMPL->tagparams['allow_comments']);
  184. //add form attributes
  185. $this->form_attribute(
  186. array(
  187. 'onsubmit' => $this->EE->TMPL->fetch_param('onsubmit'),
  188. 'name' => $this->EE->TMPL->fetch_param('name'),
  189. 'class' => $this->EE->TMPL->fetch_param('class'),
  190. 'id' => $this->EE->TMPL->fetch_param('id')
  191. )
  192. );
  193. if ($this->EE->TMPL->fetch_param('datepicker'))
  194. {
  195. $this->datepicker = $this->bool_string($this->EE->TMPL->fetch_param('datepicker'), $this->datepicker);
  196. }
  197. if ($this->datepicker)
  198. {
  199. $this->EE->javascript->output('$.datepicker.setDefaults({dateFormat:$.datepicker.W3C+EE.date_obj_time});');
  200. }
  201. foreach ($this->EE->TMPL->tagparams as $key => $value)
  202. {
  203. if (preg_match('/^rules:(.+)/', $key, $match))
  204. {
  205. $this->form_hidden('rules['.$match[1].']', $this->encrypt_input($value));
  206. }
  207. }
  208. //decide which fields to show, based on pipe delimited list of field id's and/or field short names
  209. if ($this->EE->TMPL->fetch_param('show_fields'))
  210. {
  211. if (preg_match('/not (.*)/', $this->EE->TMPL->fetch_param('show_fields'), $match))
  212. {
  213. foreach ($this->custom_fields as $field_name => $field)
  214. {
  215. $this->show_fields[] = $field_name;
  216. }
  217. foreach (explode('|', $match[1]) as $field_name)
  218. {
  219. if (is_numeric($field_name))
  220. {
  221. $field_name = $this->get_field_name($field_name);
  222. }
  223. $index = ($field_name !== FALSE) ? array_search($field_name, $this->show_fields) : FALSE;
  224. if ($index !== FALSE)
  225. {
  226. unset($this->show_fields[$index]);
  227. }
  228. }
  229. }
  230. else
  231. {
  232. foreach (explode('|', $this->EE->TMPL->fetch_param('show_fields')) as $field_name)
  233. {
  234. if (is_numeric($field_name))
  235. {
  236. $field_name = $this->get_field_name($field_name);
  237. }
  238. if ($field_name)
  239. {
  240. $this->show_fields[] = $field_name;
  241. }
  242. }
  243. }
  244. }
  245. // -------------------------------------------
  246. // 'safecracker_entry_form_tagdata_start' hook.
  247. // - Developers, if you want to modify the $this object remember
  248. // to use a reference on func call.
  249. //
  250. if ($this->EE->extensions->active_hook('safecracker_entry_form_tagdata_start') === TRUE)
  251. {
  252. $this->EE->TMPL->tagdata = $this->EE->extensions->call('safecracker_entry_form_tagdata_start', $this->EE->TMPL->tagdata, $this);
  253. if ($this->EE->extensions->end_script === TRUE) return;
  254. }
  255. // build custom field variables
  256. $custom_field_variables = array();
  257. foreach ($this->custom_fields as $field_name => $field)
  258. {
  259. /*
  260. if ($this->EE->TMPL->fetch_param($field_name))
  261. {
  262. $this->form_hidden($field_name, $this->EE->TMPL->fetch_param($field_name));
  263. }
  264. */
  265. // standard vars/conditionals
  266. $custom_field_variables_row = array(
  267. 'required' => ($field['field_required'] == 'n') ? 0 : 1,
  268. 'text_direction' => $field['field_text_direction'],
  269. 'field_data' => $this->entry($field_name),
  270. 'rows' => $field['field_ta_rows'],
  271. 'maxlength' => $field['field_maxl'],
  272. 'formatting_buttons' => '',
  273. 'field_show_formatting_btns' => (isset($field['field_show_formatting_btns']) && $field['field_show_formatting_btns'] == 'y') ? 1 : 0,
  274. 'textinput' => 0,
  275. 'pulldown' => 0,
  276. 'checkbox' => 0,
  277. 'relationship' => 0,
  278. 'multiselect' => 0,
  279. 'date' => 0,
  280. 'radio' => 0,
  281. 'display_field' => '',
  282. 'options' => $this->get_field_options($field_name),
  283. 'error' => ( ! empty($this->field_errors[$field['field_name']])) ? lang($this->field_errors[$field['field_name']]) : ''
  284. );
  285. $custom_field_variables_row = array_merge($field, $custom_field_variables_row);
  286. $fieldtypes = $this->EE->api_channel_fields->fetch_installed_fieldtypes();
  287. //add a negative conditional based on fieldtype
  288. foreach ($fieldtypes as $type => $fieldtype)
  289. {
  290. $custom_field_variables_row[$type] = 0;
  291. }
  292. // fieldtype conditionals
  293. foreach ($this->custom_fields as $f_name => $f)
  294. {
  295. $custom_field_variables_row[$f['field_type']] = $custom_field_variables_row[$f_name] = ($field['field_type'] == $f['field_type']) ? 1 : 0;
  296. }
  297. if (array_key_exists($field['field_type'], $this->custom_field_conditional_names))
  298. {
  299. $custom_field_variables_row[$this->custom_field_conditional_names[$field['field_type']]] = 1;
  300. }
  301. if ($field['field_type'] == 'date')
  302. {
  303. if ($this->datepicker)
  304. {
  305. $default_date = (($this->entry($field_name)) ? $this->entry($field_name) : $this->EE->localize->now) * 1000;
  306. $this->EE->javascript->output('
  307. $(\'input[name="'.$field_name.'"]\').datepicker({
  308. constrainInput: false,
  309. defaultDate: new Date('.$default_date.')
  310. });
  311. ');
  312. }
  313. $custom_field_variables_row['field_data'] = $this->EE->localize->set_human_time($this->entry($field_name));
  314. }
  315. $custom_field_variables[$field_name] = $custom_field_variables_row;
  316. }
  317. // parse custom fields loop
  318. if (preg_match('/'.LD.'custom_fields'.RD.'(.*)'.LD.'\/custom_fields'.RD.'/s', $this->EE->TMPL->tagdata, $match))
  319. {
  320. $custom_field_output = '';
  321. $tagdata = $match[1];
  322. $formatting_buttons = (strpos($tagdata, LD.'formatting_buttons'.RD) !== FALSE);
  323. foreach ($custom_field_variables as $field_name => $custom_field_variables_row)
  324. {
  325. if ($this->show_fields && ! in_array($field_name, $this->show_fields))
  326. {
  327. continue;
  328. }
  329. if ($formatting_buttons && $custom_field_variables_row['field_show_formatting_btns'])
  330. {
  331. $this->markitup = TRUE;
  332. $this->EE->javascript->output('SafeCracker.markItUpFields["'.$field_name.'"] = '.$custom_field_variables_row['field_id'].';');
  333. }
  334. $temp = $tagdata;
  335. //parse conditionals
  336. //$temp = $this->swap_conditionals($temp, $custom_field_variables_row);
  337. $embed_vars = $this->EE->TMPL->embed_vars;
  338. $this->EE->TMPL->embed_vars = array_merge($this->EE->TMPL->embed_vars, $custom_field_variables_row);
  339. $temp = $this->EE->TMPL->advanced_conditionals($temp);
  340. $this->EE->TMPL->embed_vars = $embed_vars;
  341. if (strpos($temp, LD.'display_field'.RD) !== FALSE)
  342. {
  343. $custom_field_variables_row['display_field'] = $this->display_field($field_name);
  344. if ($custom_field_variables_row['field_type'] == 'file')
  345. {
  346. $custom_field_variables_row['display_field'] = '<div class="publish_field">'.$custom_field_variables_row['display_field'].'</div>';
  347. }
  348. }
  349. foreach ($custom_field_variables_row as $key => $value)
  350. {
  351. if (is_array($value))
  352. {
  353. $temp = $this->swap_var_pair($key, $value, $temp);
  354. }
  355. // don't use our conditionals as vars
  356. else if ( ! is_int($value))
  357. {
  358. $temp = $this->EE->TMPL->swap_var_single($key, $value, $temp);
  359. }
  360. }
  361. if ($custom_field_variables_row['field_type'] === 'catchall')
  362. {
  363. $temp = $this->replace_tag($field_name, $this->entry($field_name), array(), $temp);
  364. }
  365. $custom_field_output .= $temp;
  366. }
  367. $this->EE->TMPL->tagdata = str_replace($match[0], $custom_field_output, $this->EE->TMPL->tagdata);
  368. }
  369. if ( ! empty($this->markitup))
  370. {
  371. $this->EE->javascript->output('$.each(SafeCracker.markItUpFields,function(a){$("#"+a).markItUp(mySettings);});');
  372. }
  373. // We'll store all checkbox fieldnames in here, so that in case one
  374. // has preserve_checkboxes set to "yes" but still needs to edit
  375. // checkboxes that have the potential to be blank, the field can be
  376. // updated while preserving the checkboxes that aren't on screen
  377. $checkbox_fields = array();
  378. foreach ($this->EE->TMPL->var_pair as $tag_pair_open => $tagparams)
  379. {
  380. $tag_name = current(preg_split('/\s/', $tag_pair_open));
  381. if ($tag_name == 'categories')
  382. {
  383. $this->EE->TMPL->tagdata = $this->swap_var_pair($tag_pair_open, $this->categories($tagparams), $this->EE->TMPL->tagdata, $tag_name, ! empty($tagparams['backspace']) ? $tagparams['backspace'] : FALSE);
  384. //$this->parse_variables['categories'] = $this->categories($tagparams);
  385. }
  386. else if ($tag_name == 'statuses')
  387. {
  388. $this->fetch_statuses();
  389. $this->parse_variables['statuses'] = $this->statuses;
  390. }
  391. //custom field pair parsing with replace_tag
  392. else if (isset($this->custom_fields[$tag_name]))
  393. {
  394. if (preg_match_all('/'.LD.preg_quote($tag_pair_open).RD.'(.*)'.LD.'\/'.$tag_name.RD.'/s', $this->EE->TMPL->tagdata, $matches))
  395. {
  396. foreach ($matches[1] as $match_index => $var_pair_tagdata)
  397. {
  398. $this->EE->TMPL->tagdata = str_replace($matches[0][$match_index], $this->replace_tag($tag_name, $this->entry($tag_name), $tagparams, $var_pair_tagdata), $this->EE->TMPL->tagdata);
  399. }
  400. }
  401. }
  402. //options:field_name tag pair parsing
  403. else if (preg_match('/^options:(.*)/', $tag_name, $match) && in_array($this->get_field_type($match[1]), $this->option_fields))
  404. {
  405. $checkbox_fields[] = $match[1];
  406. $this->parse_variables[$match[0]] = (isset($custom_field_variables[$match[1]]['options'])) ? $custom_field_variables[$match[1]]['options'] : '';
  407. }
  408. //parse category menu
  409. else if ($tag_name == 'category_menu')
  410. {
  411. $this->channel_standalone->_category_tree_form($this->channel('cat_group'), 'edit', '', $this->entry('categories'));
  412. $this->parse_variables['category_menu'] = array(array('select_options' => implode("\n", $this->channel_standalone->categories)));
  413. }
  414. //parse status menu
  415. else if ($tag_name = 'status_menu')
  416. {
  417. $this->fetch_statuses();
  418. $select_options = '';
  419. foreach ($this->statuses as $status)
  420. {
  421. $status['selected'] = ($this->entry('status') == $status['status']) ? ' selected="selected"' : '';
  422. $status['checked'] = ($this->entry('status') == $status['status']) ? ' checked="checked"' : '';
  423. $status['name'] = (in_array($status['status'], array('open', 'closed'))) ? lang($status['status']) : $status['status'];
  424. $select_options .= '<option value="'.$status['status'].'"'.$status['selected'].'>'.$status['name'].'</option>'."\n";
  425. }
  426. $this->parse_variables['status_menu'] = array(array('select_options' => $select_options));
  427. }
  428. }
  429. $this->form_hidden('checkbox_fields', implode('|', $checkbox_fields));
  430. //edit form
  431. if ($this->entry)
  432. {
  433. //not necessary for edit forms
  434. $this->EE->TMPL->tagparams['use_live_url'] = 'no';
  435. $expiration_date = ($this->entry('expiration_date')) ? $this->entry('expiration_date')*1000 : $this->EE->localize->now*1000;
  436. $comment_expiration_date = ($this->entry('comment_expiration_date')) ? $this->entry('comment_expiration_date')*1000 : $this->EE->localize->now*1000;
  437. if ($this->datepicker)
  438. {
  439. if (strpos($this->EE->TMPL->tagdata, 'entry_date') !== FALSE)
  440. {
  441. $this->EE->javascript->output('$("input[name=entry_date]").datepicker({defaultDate: new Date('.($this->entry('entry_date')*1000).')});');
  442. }
  443. if (strpos($this->EE->TMPL->tagdata, 'expiration_date') !== FALSE)
  444. {
  445. $this->EE->javascript->output('$("input[name=expiration_date]").datepicker({defaultDate: new Date('.$expiration_date.')});');
  446. }
  447. if (strpos($this->EE->TMPL->tagdata, 'comment_expiration_date') !== FALSE)
  448. {
  449. $this->EE->javascript->output('$("input[name=comment_expiration_date]").datepicker({defaultDate: new Date('.$comment_expiration_date.')});');
  450. }
  451. }
  452. foreach ($this->EE->TMPL->var_single as $key)
  453. {
  454. if ($this->entry($key) !== FALSE)
  455. {
  456. if (in_array($key, $this->date_fields) || $this->get_field_type($key) == 'date')
  457. {
  458. $this->parse_variables[$key] = ($this->entry($key)) ? $this->EE->localize->set_human_time($this->entry($key)) : '';
  459. }
  460. elseif (in_array($key, $this->checkboxes))
  461. {
  462. $this->parse_variables[$key] = ($this->entry($key) == 'y') ? 'checked="checked"' : '';
  463. }
  464. else
  465. {
  466. $this->parse_variables[$key] = form_prep($this->entry($key), $key);
  467. }
  468. }
  469. else if (preg_match('/entry_id_path=([\042\047])?([^\042\047]*)[\042\047]?/', $key, $match))
  470. {
  471. $this->parse_variables[$match[0]] = $this->EE->functions->create_url($match[2].'/'.$this->entry('entry_id'));
  472. }
  473. else if (preg_match('/(url_title_path|title_permalink)=[\042\047]?([^\042\047]*)[\042\047]?/', $key, $match))
  474. {
  475. $this->parse_variables[$match[0]] = $this->EE->functions->create_url($match[2].'/'.$this->entry('url_title'));
  476. }
  477. // use fieldtype display_field method
  478. else if (preg_match('/^field:(.*)$/', $key, $match))
  479. {
  480. $this->parse_variables[$match[0]] = (array_key_exists($match[1], $this->custom_fields)) ? $this->display_field($match[1]) : '';
  481. }
  482. else if (preg_match('/^label:(.*)$/', $key, $match))
  483. {
  484. $this->parse_variables[$match[0]] = (array_key_exists($match[1], $this->custom_fields)) ? $this->custom_fields[$match[1]]['field_label'] : '';
  485. }
  486. else if (preg_match('/^selected_option:(.*?)(:label)?$/', $key, $match) && in_array($this->get_field_type($match[1]), $this->option_fields))
  487. {
  488. $options = (isset($custom_field_variables[$match[1]]['options'])) ? $custom_field_variables[$match[1]]['options'] : array();
  489. $selected_option = '';
  490. foreach ($options as $option)
  491. {
  492. if ($option['option_value'] == $this->entry($match[1]))
  493. {
  494. $selected_option = ( ! empty($match[2])) ? $option['option_name'] : $option['option_value'];
  495. }
  496. }
  497. $this->parse_variables[$match[0]] = $selected_option;
  498. }
  499. else if (preg_match('/^instructions:(.*)$/', $key, $match))
  500. {
  501. $this->parse_variables[$match[0]] = (array_key_exists($match[1], $this->custom_fields)) ? $this->custom_fields[$match[1]]['field_instructions'] : '';
  502. }
  503. else if (preg_match('/^error:(.*)$/', $key, $match))
  504. {
  505. $this->parse_variables[$match[0]] = ( ! empty($this->field_errors[$match[1]])) ? $this->field_errors[$match[1]] : '';
  506. }
  507. }
  508. $this->form_hidden(
  509. array(
  510. 'entry_id' => $this->entry('entry_id'),
  511. 'unique_url_title' => ($this->bool_string($this->EE->TMPL->fetch_param('unique_url_title'))) ? '1' : '',
  512. 'author_id'=> $this->entry('author_id')
  513. )
  514. );
  515. }
  516. elseif ($this->channel('channel_id'))
  517. {
  518. $this->parse_variables['entry_date'] = $this->EE->localize->set_human_time();
  519. if ($this->datepicker)
  520. {
  521. $this->EE->javascript->output('$.datepicker.setDefaults({defaultDate: new Date('.($this->EE->localize->now*1000).')});');
  522. if (strpos($this->EE->TMPL->tagdata, 'entry_date') !== FALSE)
  523. {
  524. $this->EE->javascript->output('$("input[name=entry_date]").datepicker();');
  525. }
  526. if (strpos($this->EE->TMPL->tagdata, 'expiration_date') !== FALSE)
  527. {
  528. $this->EE->javascript->output('$("input[name=expiration_date]").datepicker();');
  529. }
  530. if (strpos($this->EE->TMPL->tagdata, 'comment_expiration_date') !== FALSE)
  531. {
  532. $this->EE->javascript->output('$("input[name=comment_expiration_date]").datepicker();');
  533. }
  534. }
  535. foreach ($this->custom_fields as $field)
  536. {
  537. foreach ($this->EE->TMPL->var_pair as $tag_pair_open => $tagparams)
  538. {
  539. $tag_name = current(preg_split('/\s/', $tag_pair_open));
  540. if ($tag_name == $field['field_name'])
  541. {
  542. //special parsing here for catchall fieldtype, pls keep this in
  543. if ($field['field_type'] === 'catchall')
  544. {
  545. if (preg_match_all('/'.LD.$tag_pair_open.RD.'(.*)'.LD.'\/'.$field['field_name'].RD.'/s', $this->EE->TMPL->tagdata, $matches))
  546. {
  547. foreach ($matches[1] as $match_index => $var_pair_tagdata)
  548. {
  549. if (preg_match_all('/'.LD.'([^\s]*)'.RD.'(.*)'.LD.'\/'.'\1'.RD.'/s', $var_pair_tagdata, $submatches))
  550. {
  551. foreach ($submatches[2] as $submatch_index => $sub_var_pair_tagdata)
  552. {
  553. $var_pair_tagdata = str_replace($submatches[0][$submatch_index], $sub_var_pair_tagdata, $var_pair_tagdata);
  554. }
  555. }
  556. $var_pair_tagdata = preg_replace('/'.LD.'([^\s]*)'.RD.'/s', '', $var_pair_tagdata);
  557. $this->EE->TMPL->tagdata = str_replace($matches[0][$match_index], $var_pair_tagdata, $this->EE->TMPL->tagdata);
  558. }
  559. }
  560. }
  561. else
  562. {
  563. $this->parse_variables[$field['field_name']] = '';
  564. }
  565. }
  566. else if ($tag_name == 'options:'.$field['field_name'] && in_array($this->get_field_type($field['field_name']), $this->option_fields))
  567. {
  568. $this->parse_variables['options:'.$field['field_name']] = (isset($custom_field_variables[$field['field_name']]['options'])) ? $custom_field_variables[$field['field_name']]['options'] : '';
  569. }
  570. }
  571. $this->parse_variables[$field['field_name']] = '';
  572. $this->parse_variables['label:'.$field['field_name']] = $field['field_label'];
  573. $this->parse_variables['selected_option:'.$field['field_name'].':label'] = '';
  574. $this->parse_variables['selected_option:'.$field['field_name']] = '';
  575. $this->parse_variables['label:'.$field['field_name']] = $field['field_label'];
  576. $this->parse_variables['instructions:'.$field['field_name']] = $field['field_instructions'];
  577. $this->parse_variables['error:'.$field['field_name']] = ( ! empty($this->field_errors[$field['field_name']])) ? $this->field_errors[$field['field_name']] : '';
  578. //let's not needlessly call this, otherwise we could get duplicate fields rendering
  579. if (strpos($this->EE->TMPL->tagdata, LD.'field:'.$field['field_name'].RD) !== FALSE)
  580. {
  581. $this->parse_variables['field:'.$field['field_name']] = (array_key_exists($field['field_name'], $this->custom_fields)) ? $this->display_field($field['field_name']) : '';
  582. }
  583. }
  584. }
  585. foreach ($this->title_fields as $field)
  586. {
  587. if (isset($this->EE->TMPL->var_single['error:'.$field]))
  588. {
  589. $this->parse_variables['error:'.$field] = ( ! empty($this->field_errors[$field])) ? $this->field_errors[$field] : '';
  590. }
  591. }
  592. // Add global errors
  593. if (count($this->errors) === 0)
  594. {
  595. $this->parse_variables['global_errors'] = array(array());
  596. }
  597. else
  598. {
  599. $this->parse_variables['global_errors'] = array();
  600. foreach ($this->errors as $error)
  601. {
  602. $this->parse_variables['global_errors'][] = array('error' => $error);
  603. }
  604. }
  605. $this->parse_variables['global_errors:count'] = count($this->errors);
  606. // Add field errors
  607. if (count($this->field_errors) === 0)
  608. {
  609. $this->parse_variables['field_errors'] = array(array());
  610. }
  611. else
  612. {
  613. $this->parse_variables['field_errors'] = array();
  614. foreach ($this->field_errors as $field => $error)
  615. {
  616. $this->parse_variables['field_errors'][] = array('field' => $field, 'error' => $error);
  617. }
  618. }
  619. $this->parse_variables['field_errors:count'] = count($this->field_errors);
  620. // Add field errors to conditional parsing
  621. $conditional_errors = $this->parse_variables;
  622. if ( ! empty($conditional_errors['field_errors'][0]))
  623. {
  624. foreach ($conditional_errors['field_errors'] as $error)
  625. {
  626. $conditional_errors['error:' . $error['field']] = $error['error'];
  627. }
  628. unset($conditional_errors['field_errors']);
  629. }
  630. //load member data for logged out member
  631. $this->fetch_logged_out_member($this->EE->TMPL->fetch_param('logged_out_member_id'));
  632. // Parse captcha conditional
  633. $captcha_conditional = array(
  634. 'captcha' => ($this->channel('channel_id') && $this->logged_out_member_id && ! empty($this->settings['require_captcha'][$this->EE->config->item('site_id')][$this->channel('channel_id')]))
  635. );
  636. // Parse conditionals
  637. // $this->parse_variables['error:title'] = TRUE;
  638. $this->EE->TMPL->tagdata = $this->EE->functions->prep_conditionals(
  639. $this->EE->TMPL->tagdata,
  640. array_merge($conditional_errors, $captcha_conditional)
  641. );
  642. // Make sure {captcha_word} is blank
  643. $this->EE->TMPL->tagdata = $this->EE->TMPL->swap_var_single('captcha_word', '', $this->EE->TMPL->tagdata);
  644. // Replace {captcha} with actual captcha
  645. $this->EE->TMPL->tagdata = $this->EE->TMPL->swap_var_single('captcha', $this->EE->functions->create_captcha(), $this->EE->TMPL->tagdata);
  646. // Parse the variables
  647. if ($this->parse_variables)
  648. {
  649. $this->EE->TMPL->tagdata = $this->EE->TMPL->parse_variables($this->EE->TMPL->tagdata, array($this->parse_variables));
  650. }
  651. if ($this->file)
  652. {
  653. $this->EE->session->cache['safecracker']['enctype'] = 'enctype="multipart/form-data"';
  654. }
  655. //add encrypted member_id to form
  656. if ($this->EE->TMPL->fetch_param('logged_out_member_id') && $this->logged_out_member_id)
  657. {
  658. $this->form_hidden('logged_out_member_id', $this->encrypt_input($this->logged_out_member_id));
  659. }
  660. //add class to form
  661. if ($this->EE->TMPL->fetch_param('class'))
  662. {
  663. $this->EE->TMPL->tagparams['form_class'] = $this->EE->TMPL->fetch_param('class');
  664. }
  665. $this->load_session_override();
  666. //set group-based return url
  667. $this->form_hidden('return', ($this->EE->TMPL->fetch_param('return_'.$this->EE->session->userdata['group_id'])) ? $this->EE->TMPL->fetch_param('return_'.$this->EE->session->userdata['group_id']) : $this->EE->TMPL->fetch_param('return'));
  668. //get rid of the saef_javascript variable, we don't want that parsing in channel_standalone
  669. if (($array_search = array_search('saef_javascript', $this->EE->TMPL->var_single)) !== FALSE)
  670. {
  671. unset($this->EE->TMPL->var_single[$array_search]);
  672. }
  673. $this->EE->session->cache['safecracker']['form_declaration'] = TRUE;
  674. //temporarily set the site_id for cross-site saef
  675. $current_site_id = $this->EE->config->item('site_id');
  676. $this->EE->config->set_item('site_id', $this->site_id);
  677. $include_jquery = $this->EE->TMPL->fetch_param('include_jquery');
  678. //force include to no, for channel_standalone parsing
  679. $this->EE->TMPL->tagparams['include_jquery'] = 'no';
  680. $return = $this->channel_standalone->entry_form(TRUE, $this->EE->functions->cached_captcha);
  681. $this->EE->config->set_item('site_id', $current_site_id);
  682. if (isset($this->EE->session->cache['safecracker']['channel_standalone_output_js']))
  683. {
  684. $this->head .= '<script type="text/javascript" charset="utf-8">// <![CDATA[ '."\n";
  685. foreach ($this->EE->session->cache['safecracker']['channel_standalone_output_js']['json'] as $key => $value)
  686. {
  687. if ($key == 'EE')
  688. {
  689. $value['XID'] = '{XID_HASH}';
  690. $this->head .= 'if (typeof EE == "undefined" || ! EE) { '."\n".'var EE = '.$this->EE->javascript->generate_json($value, TRUE).';}'."\n";
  691. }
  692. else
  693. {
  694. $this->head .= $key.' = '.$this->EE->javascript->generate_json($value, TRUE).';'."\n";
  695. }
  696. $first = FALSE;
  697. }
  698. $this->head .= "\n".' // ]]>'."\n".'</script>';
  699. }
  700. $js_defaults = array(
  701. 'ui' => array('core', 'widget', 'button', 'dialog'),
  702. 'plugin' => array('scrollable', 'scrollable.navigator', 'markitup', 'thickbox'),
  703. );
  704. if (version_compare(APP_VER, '2.1.3', '>'))
  705. {
  706. $js_defaults['plugin'][] = 'toolbox.expose';
  707. $js_defaults['plugin'][] = 'overlay';
  708. $js_defaults['plugin'][] = 'tmpl';
  709. }
  710. if ($this->datepicker)
  711. {
  712. $js_defaults['ui'][] = 'datepicker';
  713. }
  714. foreach ($js_defaults as $type => $files)
  715. {
  716. foreach ($files as $file)
  717. {
  718. if ( ! isset($this->EE->cp->js_files[$type]))
  719. {
  720. $this->EE->cp->js_files[$type] = array();
  721. }
  722. else if (is_string($this->EE->cp->js_files[$type]))
  723. {
  724. $this->EE->cp->js_files[$type] = explode(',', $this->EE->cp->js_files[$type]);
  725. }
  726. if ( ! in_array($file, $this->EE->cp->js_files[$type]))
  727. {
  728. $this->EE->cp->js_files[$type][] = $file;
  729. }
  730. }
  731. }
  732. $ui = array(
  733. 'core' => FALSE,
  734. 'widget' => array('core'),
  735. 'mouse' => array('core', 'widget'),
  736. 'position' => array('core'),
  737. 'draggable' => array('core', 'widget', 'mouse'),
  738. 'droppable' => array('core', 'widget', 'mouse', 'draggable'),
  739. 'resizable' => array('core', 'widget', 'mouse'),
  740. 'selectable' => array('core', 'widget', 'mouse'),
  741. 'sortable' => array('core', 'widget', 'mouse'),
  742. 'accordion' => array('core', 'widget'),
  743. 'autocomplete' => array('core'),
  744. 'button' => array('core', 'widget', 'position'),
  745. 'dialog' => array('core', 'widget', 'mouse', 'position', 'draggable', 'resizable', 'button'),
  746. 'slider' => array('core', 'widget', 'mouse'),
  747. 'tabs' => array('core', 'widget'),
  748. 'datepicker' => array('core'),
  749. 'progressbar' => array('core', 'widget'),
  750. 'effects' => array('core'),
  751. );
  752. foreach ($this->EE->cp->js_files as $type => $files)
  753. {
  754. //let's get the order right
  755. if ($type == 'ui')
  756. {
  757. $temp = array();
  758. foreach ($files as $file)
  759. {
  760. $temp[] = $file;
  761. if (is_array($ui[$file]))
  762. {
  763. $temp = array_merge($ui[$file], $temp);
  764. }
  765. }
  766. $files = array();
  767. foreach (array_keys($ui) as $file)
  768. {
  769. if (in_array($file, $temp))
  770. {
  771. $files[] = $file;
  772. }
  773. }
  774. }
  775. if (empty($files))
  776. {
  777. unset($this->EE->cp->js_files[$type]);
  778. }
  779. else
  780. {
  781. $mtime[] = $this->EE->cp->_get_js_mtime($type, $files);
  782. $this->EE->cp->js_files[$type] = implode(',', $files);
  783. }
  784. }
  785. if (empty($mtime))
  786. {
  787. $mtime = array($this->EE->localize->now);
  788. }
  789. $use_live_url = ($this->bool_string($this->EE->TMPL->fetch_param('use_live_url'), TRUE)) ? '&use_live_url=y' : '';
  790. $include_jquery = ($this->bool_string($include_jquery, TRUE)) ? '&include_jquery=y' : '';
  791. // RTE Selector parameter?
  792. $rte_selector = $this->EE->TMPL->fetch_param('rte_selector');
  793. if ($rte_selector)
  794. {
  795. // toolset id specified?
  796. $rte_toolset_id = (int)$this->EE->TMPL->fetch_param('rte_toolset_id');
  797. $js_url = $this->EE->functions->fetch_site_index().QUERY_MARKER
  798. .'ACT='.$this->EE->functions->fetch_action_id('Rte', 'get_js')
  799. .'&toolset_id='.$rte_toolset_id
  800. .'&selector='.urlencode($rte_selector)
  801. .'&include=jquery_ui';
  802. $this->head .= '<script type="text/javascript" src="'.$js_url.'"></script>'."\n";
  803. }
  804. $this->head .= '<script type="text/javascript" charset="utf-8" src="'.$this->EE->functions->fetch_site_index().QUERY_MARKER.'ACT='.$this->EE->functions->fetch_action_id('Safecracker', 'combo_loader').'&'.str_replace('%2C', ',', http_build_query($this->EE->cp->js_files)).'&v='.max($mtime).$use_live_url.$include_jquery.'"></script>'."\n";
  805. //add fieldtype styles
  806. foreach ($this->EE->cp->its_all_in_your_head as $item)
  807. {
  808. $this->head .= $item."\n";
  809. }
  810. //add fieldtype scripts
  811. foreach ($this->EE->cp->footer_item as $item)
  812. {
  813. $this->head .= $item."\n";
  814. }
  815. $this->unload_session_override();
  816. //add loaded JS
  817. $this->EE->javascript->compile();
  818. if ( ! empty($this->EE->jquery->jquery_code_for_compile))
  819. {
  820. $script = '$(document).ready(function() {' . "\n";
  821. $script .= implode('', $this->EE->jquery->jquery_code_for_compile);
  822. $script .= '});';
  823. $script = preg_replace('/\s*eeSpell\.init\(\);\s*/', '', $script);
  824. $this->head .= $this->EE->javascript->inline($script);
  825. $this->EE->jquery->jquery_code_for_compile = array();
  826. }
  827. //if (isset($this->EE->load->_ci_cached_vars['script_foot']))
  828. //{
  829. //$script = $this->EE->load->_ci_cached_vars['script_foot'];
  830. //$script = preg_replace('/\s*eeSpell\.init\(\);\s*/', '', $script);
  831. //$this->head .= $script;
  832. //}
  833. //add datepicker class
  834. if ($this->datepicker)
  835. {
  836. $date_fmt = $this->EE->session->userdata('time_format');
  837. $date_fmt = $date_fmt ? $date_fmt : $this->EE->config->item('time_format');
  838. $this->head .= '<style type="text/css">.hasDatepicker{background:#fff url('.$this->EE->config->item('theme_folder_url').'cp_themes/default/images/calendar_bg.gif) no-repeat 98% 2px;background-repeat:no-repeat;background-position:99%;}</style>';
  839. $this->head .= trim('
  840. <script type="text/javascript">
  841. $.createDatepickerTime=function(){
  842. date = new Date();
  843. hours = date.getHours();
  844. minutes = date.getMinutes();
  845. suffix = "";
  846. format = "'.$date_fmt.'";
  847. if (minutes < 10) {
  848. minutes = "0" + minutes;
  849. }
  850. if (format == "us") {
  851. if (hours > 12) {
  852. hours -= 12;
  853. suffix = " PM";
  854. } else if (hours == 12) {
  855. suffix = " PM";
  856. } else {
  857. suffix = " AM";
  858. }
  859. }
  860. return " \'" + hours + ":" + minutes + suffix + "\'";
  861. }
  862. EE.date_obj_time = $.createDatepickerTime();
  863. </script>');
  864. }
  865. //make head appear by default
  866. if (preg_match('/'.LD.'safecracker_head'.RD.'/', $return))
  867. {
  868. $return = $this->EE->TMPL->swap_var_single('safecracker_head', $this->head, $return);
  869. }
  870. // Head should only be there if the param is there and there is a valid member_id
  871. else if (
  872. $this->bool_string($this->EE->TMPL->fetch_param('safecracker_head'), TRUE) AND
  873. ($this->logged_out_member_id OR $this->EE->session->userdata('member_id'))
  874. )
  875. {
  876. $return .= $this->head;
  877. }
  878. //added in 1.0.3
  879. if ($this->bool_string($this->EE->TMPL->fetch_param('secure_action')))
  880. {
  881. $return = preg_replace('/(<form.*?action=")http:/', '\\1https:', $return);
  882. }
  883. $return = $this->EE->functions->insert_action_ids($return);
  884. // -------------------------------------------
  885. // 'safecracker_entry_form_tagdata_end' hook.
  886. // - Developers, if you want to modify the $this object remember
  887. // to use a reference on func call.
  888. //
  889. if ($this->EE->extensions->active_hook('safecracker_entry_form_tagdata_end') === TRUE)
  890. {
  891. $return = $this->EE->extensions->call('safecracker_entry_form_tagdata_end', $return, $this);
  892. if ($this->EE->extensions->end_script === TRUE) return;
  893. }
  894. return $return;
  895. }
  896. // --------------------------------------------------------------------
  897. /**
  898. * Creates or edits an entry
  899. *
  900. * @return void
  901. */
  902. public function submit_entry()
  903. {
  904. $this->initialize();
  905. $this->fetch_site(FALSE, $this->EE->input->post('site_id', TRUE));
  906. $this->fetch_channel($this->EE->input->post('channel_id', TRUE));
  907. $this->EE->load->helper(array('url', 'form'));
  908. $this->EE->load->library('api');
  909. $this->EE->api->instantiate('channel_fields');
  910. $this->EE->load->library('filemanager');
  911. $this->EE->load->library('form_validation');
  912. $this->EE->load->library('localize');
  913. $this->EE->load->model(array('field_model', 'tools_model'));
  914. $this->EE->filemanager->_initialize(array());
  915. $this->EE->lang->loadfile('content');
  916. $this->EE->lang->loadfile('form_validation');
  917. $this->EE->lang->loadfile('safecracker');
  918. $this->EE->router->set_class('cp');
  919. $this->EE->load->library('cp');
  920. $this->EE->router->set_class('ee');
  921. $rules = $this->EE->input->post('rules');
  922. //just to prevent any errors
  923. if ( ! defined('BASE'))
  924. {
  925. $s = ($this->EE->config->item('admin_session_type') != 'c') ? $this->EE->session->userdata('session_id') : 0;
  926. define('BASE', SELF.'?S='.$s.'&amp;D=cp');
  927. }
  928. $this->json = $this->EE->input->post('json');
  929. $this->error_handling = $this->EE->input->post('error_handling');
  930. // -------------------------------------------
  931. // 'safecracker_submit_entry_start' hook.
  932. // - Developers, if you want to modify the $this object remember
  933. // to use a reference on func call.
  934. //
  935. if ($this->EE->extensions->active_hook('safecracker_submit_entry_start') === TRUE)
  936. {
  937. $this->EE->extensions->call('safecracker_submit_entry_start', $this);
  938. if ($this->EE->extensions->end_script === TRUE) return;
  939. }
  940. $logged_out_member_id = FALSE;
  941. if ( ! $this->EE->session->userdata('member_id') && $this->EE->input->post('logged_out_member_id'))
  942. {
  943. if ($logged_out_member_id = $this->decrypt_input($this->EE->input->post('logged_out_member_id')))
  944. {
  945. $this->fetch_logged_out_member($logged_out_member_id);
  946. }
  947. }
  948. else if ($this->channel('channel_id') && ! $this->EE->session->userdata('member_id') && ! empty($this->settings['logged_out_member_id'][$this->EE->config->item('site_id')][$this->channel('channel_id')]))
  949. {
  950. $this->fetch_logged_out_member($this->settings['logged_out_member_id'][$this->EE->config->item('site_id')][$this->channel('channel_id')]);
  951. }
  952. //captcha check
  953. if ($this->channel('channel_id') && ! empty($this->logged_out_member_id) && ! empty($this->settings['require_captcha'][$this->EE->config->item('site_id')][$this->EE->input->post('channel_id', TRUE)]))
  954. {
  955. if ( ! $this->EE->input->post('captcha'))
  956. {
  957. $this->errors[] = lang('captcha_required');
  958. }
  959. $this->EE->db->where('word', $this->EE->input->post('captcha', TRUE));
  960. $this->EE->db->where('ip_address', $this->EE->input->ip_address());
  961. $this->EE->db->where('date > ', '(UNIX_TIMESTAMP()-7200)', FALSE);
  962. if ( ! $this->EE->db->count_all_results('captcha'))
  963. {
  964. $this->errors[] = lang('captcha_incorrect');
  965. }
  966. $this->EE->db->where('word', $this->EE->input->post('captcha', TRUE));
  967. $this->EE->db->where('ip_address', $this->EE->input->ip_address());
  968. $this->EE->db->where('date < ', '(UNIX_TIMESTAMP()-7200)', FALSE);
  969. $this->EE->db->delete('captcha');
  970. }
  971. //is an edit form?
  972. if ($this->EE->input->post('entry_id'))
  973. {
  974. $this->edit = TRUE;
  975. $this->fetch_entry($this->EE->input->post('entry_id', TRUE));
  976. if ($this->EE->input->post('category') === FALSE && $this->entry('categories'))
  977. {
  978. $_POST['category'] = $this->entry('categories');
  979. }
  980. }
  981. else
  982. {
  983. if ($this->EE->input->post('unique_url_title', TRUE))
  984. {
  985. $_POST['url_title'] = uniqid($this->EE->input->post('url_title', TRUE) ? $this->EE->input->post('url_title', TRUE) : url_title($this->EE->input->post('title', TRUE)), TRUE);
  986. }
  987. $this->entry['dst_enabled'] = $this->EE->input->post('dst_enabled');
  988. }
  989. $this->preserve_checkboxes = $this->bool_string($this->EE->input->post('preserve_checkboxes'), FALSE);
  990. // If any checkbox fields are missing from the POST array,
  991. // add them in as blank values for form validation to catch
  992. foreach (explode('|', $_POST['checkbox_fields']) as $checkbox)
  993. {
  994. if ( ! isset($_POST[$checkbox]))
  995. {
  996. $_POST[$checkbox] = '';
  997. }
  998. }
  999. foreach ($this->custom_fields as $i => $field)
  1000. {
  1001. $isset = (isset($_POST['field_id_'.$field['field_id']]) || isset($_POST[$field['field_name']]) || (((isset($_FILES['field_id_'.$field['field_id']]) && $_FILES['field_id_'.$field['field_id']]['error'] != 4) || (isset($_FILES[$field['field_name']]) && $_FILES[$field['field_name']]['error'] != 4)) && in_array($field['field_type'], $this->file_fields)));
  1002. // If file exists, add it to the POST array for validation
  1003. if (isset($_FILES[$field['field_name']]['name']))
  1004. {
  1005. $_POST[$field['field_name']] = $_FILES[$field['field_name']]['name'];
  1006. }
  1007. $this->custom_fields[$i]['isset'] = $isset;
  1008. if ( ! $this->edit || $isset)
  1009. {
  1010. $field_rules = array();
  1011. if ( ! empty($rules[$field['field_name']]))
  1012. {
  1013. if (($rules_decrypted = $this->decrypt_input($rules[$field['field_name']])) === FALSE)
  1014. {
  1015. $this->EE->output->show_user_error(FALSE, lang('form_decryption_failed'));
  1016. }
  1017. $field_rules = explode('|', $rules_decrypted);
  1018. }
  1019. if ( ! in_array('call_field_validation['.$field['field_id'].']', $field_rules))
  1020. {
  1021. array_unshift($field_rules, 'call_field_validation['.$field['field_id'].']');
  1022. }
  1023. if ($field['field_required'] == 'y' && ! in_array('required', $field_rules))
  1024. {
  1025. array_unshift($field_rules, 'required');
  1026. }
  1027. $this->EE->form_validation->set_rules($field['field_name'], $field['field_label'], implode('|', $field_rules));
  1028. }
  1029. else
  1030. {
  1031. if ($field['field_type'] == 'date')
  1032. {
  1033. $_POST['field_id_'.$field['field_id']] = $_POST[$field['field_name']] = $this->EE->localize->set_human_time($this->entry($field['field_name']));
  1034. }
  1035. else if ($field['field_required'] == 'y')
  1036. {
  1037. //add a dummy value to be removed later
  1038. //to get around _check_data_for_errors, a redundant check
  1039. $_POST['field_id_'.$field['field_id']] = '1';
  1040. }
  1041. }
  1042. //$this->EE->form_validation->set_rules($field['field_name'], $field['field_label'], implode('|', $field_rules));
  1043. foreach ($_POST as $key => $value)
  1044. {
  1045. //change field_name'd POSTed keys to field_id's
  1046. if ($key == $field['field_name'])
  1047. {
  1048. //@TODO what to do about xss_clean and "naughty" html
  1049. //for now you can crack open this file and manually add fields_ids and/or field types to the respective arrays
  1050. //to prevent xss_clean
  1051. //i had some people complain about not being able to submit <object>'s
  1052. $xss_clean = ( ! in_array($field['field_id'], $this->skip_xss_field_ids) && ! in_array($field['field_type'], $this->skip_xss_fieldtypes));
  1053. $_POST['field_id_'.$field['field_id']] = $this->EE->input->post($key, $xss_clean);
  1054. //auto set format if not POSTed
  1055. $fmt = $field['field_fmt'];
  1056. if ($this->EE->input->post('field_ft_'.$field['field_id']) !== FALSE)
  1057. {
  1058. $fmt = $this->EE->input->post('field_ft_'.$field['field_id'], TRUE);
  1059. }
  1060. elseif ($this->EE->input->post($field['field_name'].'_ft') !== FALSE)
  1061. {
  1062. $fmt = $this->EE->input->post($field['field_name'].'_ft', TRUE);
  1063. }
  1064. $_POST['field_ft_'.$field['field_id']] = $fmt;
  1065. }
  1066. else if (preg_match('/^'.$field['field_name'].'_(.+)/', $key, $match))
  1067. {
  1068. //also change utility POST fields, ie my_field_field_directory to field_id_X_directory
  1069. $_POST['field_id_'.$field['field_id'].'_'.$match[1]] = $this->EE->input->post($key, TRUE);
  1070. }
  1071. }
  1072. if (in_array($field['field_type'], $this->file_fields) || $field['field_type'] == 'matrix')
  1073. {
  1074. //change field_name'd POSTed files to field_id's
  1075. foreach ($_FILES as $key => $value)
  1076. {
  1077. if ($key == $field['field_name'])
  1078. {
  1079. $_FILES['field_id_'.$field['field_id']] = $value;
  1080. unset($_FILES[$key]);
  1081. // Check to see if a file was actually selected
  1082. if ($_POST[$field['field_name']] === 'NULL')
  1083. {
  1084. if ( ! empty($_POST[$field['field_name'].'_existing']))
  1085. {
  1086. $_POST[$field['field_name']] = $_POST[$field['field_name'].'_existing'];
  1087. }
  1088. else if ( ! empty($_POST[$field['field_name'].'_hidden']))
  1089. {
  1090. $_POST[$field['field_name']] = $_POST[$field['field_name'].'_hidden'];
  1091. }
  1092. else
  1093. {
  1094. $_POST[$field['field_name']] = '';
  1095. }
  1096. }
  1097. }
  1098. else if (preg_match('/^'.$field['field_name'].'_(.+)/', $key, $match))
  1099. {
  1100. $_FILES['field_id_'.$field['field_id'].'_'.$match[1]] = $value;
  1101. unset($_FILES[$key]);
  1102. }
  1103. }
  1104. }
  1105. }
  1106. foreach ($this->title_fields as $field)
  1107. {
  1108. if (isset($this->default_fields[$field]))
  1109. {
  1110. $this->EE->api_channel_fields->set_settings($field, $this->default_fields[$field]);
  1111. $this->EE->form_validation->set_rules($field, $this->default_fields[$field]['field_label'], $this->default_fields[$field]['rules']);
  1112. }
  1113. if ($this->EE->input->post($field) !== FALSE)
  1114. {
  1115. $_POST[$field] = $this->EE->input->post($field, TRUE);
  1116. }
  1117. else
  1118. {
  1119. if ($field == 'entry_date')
  1120. {
  1121. if ($this->entry($field))
  1122. {
  1123. $_POST[$field] = $this->EE->localize->set_human_time($this->entry($field));
  1124. }
  1125. else
  1126. {
  1127. $_POST[$field] = $this->EE->localize->set_human_time($this->EE->localize->now);
  1128. }
  1129. }
  1130. else
  1131. {
  1132. if ($this->entry($field) !== FALSE)
  1133. {
  1134. if ( ! in_array($field, $this->checkboxes) || $this->preserve_checkboxes)
  1135. {
  1136. $_POST[$field] = $this->entry($field);
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. //don't override status on edit, only on publish
  1143. if ( ! $this->edit && ! empty($this->settings['override_status'][$this->EE->config->item('site_id')][$this->EE->input->post('channel_id')]))
  1144. {
  1145. $_POST['status'] = $this->settings['override_status'][$this->EE->config->item('site_id')][$this->EE->input->post('channel_id')];
  1146. }
  1147. $_POST['ping_servers'] = (is_array($this->EE->input->post('ping'))) ? $this->EE->input->post('ping', TRUE) : array();
  1148. $_POST['ping_errors'] = FALSE;
  1149. $_POST['revision_post'] = $_POST;
  1150. $this->load_session_override();
  1151. //added for EE2.1.2
  1152. $this->EE->api->instantiate(array('channel_categories'));
  1153. $this->EE->load->library('api/api_sc_channel_entries');
  1154. foreach ($this->form_validation_methods as $method)
  1155. {
  1156. $this->EE->form_validation->set_message($method, lang('safecracker_'.$method));
  1157. }
  1158. if ($this->EE->input->post('dynamic_title'))
  1159. {
  1160. $dynamic_title = base64_decode($this->EE->input->post('dynamic_title'));
  1161. foreach ($_POST as $key => $value)
  1162. {
  1163. if (is_string($value) && strstr($dynamic_title, '['.$key.']') !== FALSE)
  1164. {
  1165. $dynamic_title = str_replace('['.$key.']', $value, $dynamic_title);
  1166. }
  1167. }
  1168. $_POST['title'] = $dynamic_title;
  1169. }
  1170. foreach ($this->EE->api_channel_fields->settings as $field_id => $settings)
  1171. {
  1172. $settings['field_name'] = 'field_id_'.$field_id;
  1173. if (isset($settings['field_settings']))
  1174. {
  1175. $settings = array_merge($settings, $this->unserialize($settings['field_settings'], TRUE));
  1176. }
  1177. $this->EE->api_channel_fields->settings[$field_id] = $this->EE->session->cache['safecracker']['field_settings'][$field_id] = $settings;
  1178. }
  1179. //moved to before custom field processing,
  1180. //since we are now using the call_field_validation rule
  1181. if ( ! $this->EE->form_validation->run())
  1182. {
  1183. $this->field_errors = $this->EE->form_validation->_error_array;
  1184. }
  1185. if ( ! $this->EE->security->check_xid($this->EE->input->post('XID')))
  1186. {
  1187. $this->EE->functions->redirect(stripslashes($this->EE->input->post('RET')));
  1188. }
  1189. if (empty($this->field_errors) && empty($this->errors))
  1190. {
  1191. //temporarily change site_id for cross-site forms
  1192. //channel_entries api doesn't allow you to specifically set site_id
  1193. $current_site_id = $this->EE->config->item('site_id');
  1194. $this->EE->config->set_item('site_id', $this->site_id);
  1195. if (in_array($this->channel('channel_id'), $this->EE->functions->fetch_assigned_channels()))
  1196. {
  1197. if ($this->entry('entry_id'))
  1198. {
  1199. $submit = $this->EE->api_sc_channel_entries->update_entry($this->entry('entry_id'), $_POST);
  1200. }
  1201. else
  1202. {
  1203. $submit = $this->EE->api_sc_channel_entries->submit_new_entry($this->channel('channel_id'), $_POST);
  1204. }
  1205. if ( ! $submit)
  1206. {
  1207. $this->errors = $this->EE->api_sc_channel_entries->errors;
  1208. }
  1209. }
  1210. else
  1211. {
  1212. $this->errors[] = lang('unauthorized_for_this_channel');
  1213. }
  1214. $this->EE->config->set_item('site_id', $current_site_id);
  1215. $this->clear_entry();
  1216. //load the just created entry into memory
  1217. $this->fetch_entry($this->EE->api_sc_channel_entries->entry_id);
  1218. }
  1219. $this->unload_session_override();
  1220. // -------------------------------------------
  1221. // 'safecracker_submit_entry_end' hook.
  1222. // - Developers, if you want to modify the $this object remember
  1223. // to use a reference on func call.
  1224. //
  1225. if ($this->EE->extensions->active_hook('safecracker_submit_entry_end') === TRUE)
  1226. {
  1227. $edata = $this->EE->extensions->call('safecracker_submit_entry_end', $this);
  1228. if ($this->EE->extensions->end_script === TRUE) return;
  1229. }
  1230. if (is_array($this->errors))
  1231. {
  1232. //add the field name to custom_field_empty errors
  1233. foreach ($this->errors as $field_name => $error)
  1234. {
  1235. if ($error == lang('custom_field_empty'))
  1236. {
  1237. $this->errors[$field_name] = $error.' '.$field_name;
  1238. }
  1239. }
  1240. }
  1241. if ( ! $this->json && ($this->errors || $this->field_errors) && $this->error_handling == 'inline')
  1242. {
  1243. $this->entry = $_POST;
  1244. $this->form_error = TRUE;
  1245. foreach($this->post_error_callbacks as $field_type => $callbacks)
  1246. {
  1247. $callbacks = explode('|', $callbacks);
  1248. foreach ($this->custom_fields as $field)
  1249. {
  1250. if ($field['field_type'] == $field_type)
  1251. {
  1252. foreach ($callbacks as $callback)
  1253. {
  1254. if (in_array($callback, $this->valid_callbacks))
  1255. {
  1256. $this->entry[$field['field_name']] = $this->entry['field_id_'.$field['field_id']] = call_user_func($callback, $this->entry($field['field_name']));
  1257. }
  1258. }
  1259. }
  1260. }
  1261. }
  1262. foreach ($this->date_fields as $field)
  1263. {
  1264. if ($this->entry($field) && ! is_numeric($this->entry($field)))
  1265. {
  1266. $this->entry[$field] = $this->EE->localize->string_to_timestamp($this->entry($field));
  1267. }
  1268. }
  1269. if (version_compare(APP_VER, '2.1.3', '>'))
  1270. {
  1271. $this->EE->core->generate_page();
  1272. }
  1273. else
  1274. {
  1275. $this->EE->core->_generate_page();
  1276. }
  1277. return;
  1278. }
  1279. if ($this->json)
  1280. {
  1281. return $this->send_ajax_response(
  1282. array(
  1283. 'success' => (empty($this->errors) && empty($this->field_errors)) ? 1 : 0,
  1284. 'errors' => (empty($this->errors)) ? array() : $this->errors,
  1285. 'field_errors' => (empty($this->field_errors)) ? array() : $this->field_errors,
  1286. 'entry_id' => $this->entry('entry_id'),
  1287. 'url_title' => $this->entry('url_title'),
  1288. 'channel_id' => $this->entry('channel_id'),
  1289. )
  1290. );
  1291. }
  1292. if ($this->errors OR $this->field_errors)
  1293. {
  1294. return $this->EE->output->show_user_error(FALSE, array_merge($this->errors, $this->field_errors));
  1295. }
  1296. if ( ! AJAX_REQUEST)
  1297. {
  1298. $this->EE->security->delete_xid($this->EE->input->post('XID'));
  1299. }
  1300. $return = ($this->EE->input->post('return')) ? $this->EE->functions->create_url($this->EE->input->post('return', TRUE)) : $this->EE->functions->fetch_site_index();
  1301. if (strpos($return, 'ENTRY_ID') !== FALSE)
  1302. {
  1303. $return = str_replace('ENTRY_ID', $this->entry('entry_id'), $return);
  1304. }
  1305. if (strpos($return, 'URL_TITLE') !== FALSE)
  1306. {
  1307. $return = str_replace('URL_TITLE', $this->entry('url_title'), $return);
  1308. }
  1309. if ($hook_return = $this->EE->api_sc_channel_entries->trigger_hook('entry_submission_redirect', $return))
  1310. {
  1311. $return = $hook_return;
  1312. }
  1313. if ($this->EE->input->post('secure_return'))
  1314. {
  1315. $return = preg_replace('/^http:/', 'https:', $return);
  1316. }
  1317. $this->EE->functions->redirect($return);
  1318. }
  1319. // --------------------------------------------------------------------
  1320. /**
  1321. * Converts text-based template parameter to boolean
  1322. *
  1323. * @param string $string
  1324. * @param bool $default = FALSE
  1325. * @return bool
  1326. */
  1327. public function bool_string($string, $default = FALSE)
  1328. {
  1329. if (preg_match('/true|t|yes|y|on|1/i', $string))
  1330. {
  1331. return TRUE;
  1332. }
  1333. if (preg_match('/false|f|no|n|off|0/i', $string))
  1334. {
  1335. return FALSE;
  1336. }
  1337. return $default;
  1338. }
  1339. // --------------------------------------------------------------------
  1340. /**
  1341. * Filters and sorts the categories
  1342. *
  1343. * @param array $params
  1344. * @return array
  1345. */
  1346. public function categories($params)
  1347. {
  1348. $this->fetch_categories();
  1349. $this->EE->load->library('data_sorter');
  1350. if ( ! $categories = $this->categories)
  1351. {
  1352. return array();
  1353. }
  1354. if ( ! $params)
  1355. {
  1356. return $categories;
  1357. }
  1358. if ( ! empty($params['group_id']))
  1359. {
  1360. $this->EE->data_sorter->filter($categories, 'category_group_id', $params['group_id'], 'in_array');
  1361. }
  1362. if ( ! empty($params['order_by']))
  1363. {
  1364. $this->EE->data_sorter->sort($categories, $params['order_by'], @$params['sort']);
  1365. }
  1366. //reset array indices
  1367. return array_merge($categories);
  1368. }
  1369. // --------------------------------------------------------------------
  1370. /**
  1371. * Retrieves current channel data
  1372. *
  1373. * @param mixed $key
  1374. * @return mixed
  1375. */
  1376. public function channel($key)
  1377. {
  1378. return (isset($this->channel[$key])) ? $this->channel[$key] : FALSE;
  1379. }
  1380. // --------------------------------------------------------------------
  1381. /**
  1382. * Clears the library's entry
  1383. *
  1384. * @return void
  1385. */
  1386. public function clear_entry()
  1387. {
  1388. $this->entry = FALSE;
  1389. }
  1390. // --------------------------------------------------------------------
  1391. /**
  1392. * Decrypts a form input
  1393. *
  1394. * @param mixed $input
  1395. * @return void
  1396. */
  1397. public function decrypt_input($input, $xss_clean = TRUE)
  1398. {
  1399. //$this->EE->load->library('encrypt');
  1400. //return $this->EE->encrypt->decode($input, $this->EE->session->sess_crypt_key);
  1401. if (function_exists('mcrypt_encrypt'))
  1402. {
  1403. $decoded = rtrim(
  1404. mcrypt_decrypt(
  1405. MCRYPT_RIJNDAEL_256,
  1406. md5($this->EE->session->sess_crypt_key),
  1407. base64_decode($input),
  1408. MCRYPT_MODE_ECB,
  1409. mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)
  1410. ),
  1411. "\0"
  1412. );
  1413. }
  1414. else
  1415. {
  1416. $raw = base64_decode($input);
  1417. $decoded = substr($raw, 0, -32);
  1418. if (substr($raw, -32) !== md5($this->EE->session->sess_crypt_key.$decoded))
  1419. {
  1420. return FALSE;
  1421. }
  1422. }
  1423. return ($xss_clean) ? $this->EE->security->xss_clean($decoded) : $decoded;
  1424. }
  1425. // --------------------------------------------------------------------
  1426. /**
  1427. * Display a custom field
  1428. *
  1429. * @param mixed $field_name
  1430. * @return void
  1431. */
  1432. public function display_field($field_name)
  1433. {
  1434. $this->EE->load->library('api');
  1435. $this->EE->load->helper('custom_field');
  1436. $this->EE->load->model('tools_model');
  1437. $this->EE->load->library('javascript');
  1438. if (isset($this->extra_js[$this->get_field_type($field_name)]))
  1439. {
  1440. $this->EE->javascript->output($this->extra_js[$this->get_field_type($field_name)]);
  1441. }
  1442. $this->EE->api->instantiate('channel_fields');
  1443. $this->EE->api_channel_fields->field_type = $this->get_field_type($field_name);
  1444. $this->EE->api_channel_fields->field_types[$this->EE->api_channel_fields->field_type]->field_name = $field_name;
  1445. $this->EE->api_channel_fields->field_types[$this->EE->api_channel_fields->field_type]->field_id = $this->get_field_id($field_name);
  1446. $this->EE->api_channel_fields->field_types[$this->EE->api_channel_fields->field_type]->settings = array_merge($this->get_field_settings($field_name), $this->get_field_data($field_name), $this->EE->api_channel_fields->get_global_settings($this->EE->api_channel_fields->field_type));
  1447. if ($this->EE->api_channel_fields->field_type == 'date')
  1448. {
  1449. $this->EE->api_channel_fields->field_types[$this->EE->api_channel_fields->field_type]->settings['dst_enabled'] = $this->entry($field_name);
  1450. }
  1451. $_GET['entry_id'] = $this->entry('entry_id');
  1452. $_GET['channel_id'] = $this->entry('channel_id');
  1453. return $this->EE->api_channel_fields->apply('display_field', array('data' => $this->entry($field_name)));
  1454. }
  1455. // --------------------------------------------------------------------
  1456. /**
  1457. * Encrypts a form input
  1458. *
  1459. * @param mixed $input
  1460. * @return void
  1461. */
  1462. public function encrypt_input($input)
  1463. {
  1464. //$this->EE->load->library('encrypt');
  1465. //return $this->EE->encrypt->encode($input, $this->EE->session->sess_crypt_key);
  1466. if ( ! function_exists('mcrypt_encrypt'))
  1467. {
  1468. return base64_encode($input.md5($this->EE->session->sess_crypt_key.$input));
  1469. }
  1470. return base64_encode(mcrypt_encrypt(
  1471. MCRYPT_RIJNDAEL_256,
  1472. md5($this->EE->session->sess_crypt_key),
  1473. $input,
  1474. MCRYPT_MODE_ECB,
  1475. mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)
  1476. ));
  1477. }
  1478. // --------------------------------------------------------------------
  1479. /**
  1480. * Retrieves current entry data
  1481. *
  1482. * @param mixed $key
  1483. * @return void
  1484. */
  1485. public function entry($key, $force_string = FALSE)
  1486. {
  1487. if (isset($this->entry[$key]))
  1488. {
  1489. return $this->entry[$key];
  1490. }
  1491. return ($force_string) ? '' : FALSE;
  1492. }
  1493. // --------------------------------------------------------------------
  1494. /**
  1495. * Load categories
  1496. *
  1497. * @return void
  1498. */
  1499. public function fetch_categories()
  1500. {
  1501. //exit if already loaded, or if there is no category group
  1502. if ($this->categories || ! $this->channel('cat_group'))
  1503. {
  1504. return;
  1505. }
  1506. // Load up the library and figure out what belongs and what's selected
  1507. $this->EE->load->library(array('api', 'file_field'));
  1508. $this->EE->api->instantiate('channel_categories');
  1509. $category_list = $this->EE->api_channel_categories->category_tree(
  1510. $this->channel('cat_group'),
  1511. $this->entry('categories')
  1512. );
  1513. $categories = array();
  1514. foreach ($category_list as $category_id => $category_info)
  1515. {
  1516. // Indent category names
  1517. if ($category_info[5] > 1) {
  1518. $category_info[1] = str_repeat(NBS.NBS.NBS.NBS, $category_info[5] - 1) . $category_info[1];
  1519. }
  1520. $selected = ($category_info[4] === TRUE) ? ' selected="selected"' : '';
  1521. $checked = ($category_info[4] === TRUE) ? ' checked="checked"' : '';
  1522. $category_image = $this->EE->file_field->parse_field($category_info[7]);
  1523. // Translate response from API to something parse variables can understand
  1524. $categories[$category_id] = array(
  1525. 'category_id' => $category_info[0],
  1526. 'category_name' => $category_info[1],
  1527. 'category_group_id' => $category_info[2],
  1528. 'category_group' => $category_info[3],
  1529. 'category_parent' => $category_info[6],
  1530. 'category_depth' => $category_info[5],
  1531. 'category_image' => (isset($category_image['url'])) ? $category_image['url'] : '',
  1532. 'category_description' => $category_info[8],
  1533. 'selected' => $selected,
  1534. 'checked' => $checked
  1535. );
  1536. }
  1537. $this->categories = $categories;
  1538. }
  1539. // --------------------------------------------------------------------
  1540. /**
  1541. * Load channel
  1542. *
  1543. * @param int $channel_id
  1544. * @param mixed $channel_name
  1545. * @param mixed $entry_id
  1546. * @param mixed $url_title = FALSE
  1547. * @return void
  1548. */
  1549. public function fetch_channel($channel_id, $channel_name = FALSE, $entry_id = FALSE, $url_title = FALSE)
  1550. {
  1551. //exit if already loaded
  1552. if ($this->channel('channel_id'))
  1553. {
  1554. return;
  1555. }
  1556. if ($channel_id)
  1557. {
  1558. $this->EE->db->where('exp_channels.channel_id', $this->EE->security->xss_clean($channel_id));
  1559. }
  1560. elseif ($channel_name)
  1561. {
  1562. $this->EE->db->where('exp_channels.channel_name', $this->EE->security->xss_clean($channel_name));
  1563. }
  1564. elseif ($entry_id)
  1565. {
  1566. $this->EE->db->join('exp_channel_titles', 'exp_channel_titles.channel_id = exp_channels.channel_id');
  1567. $this->EE->db->where('exp_channel_titles.entry_id', $this->EE->security->xss_clean($entry_id));
  1568. }
  1569. elseif ($url_title)
  1570. {
  1571. $this->EE->db->join('exp_channel_titles', 'exp_channel_titles.channel_id = exp_channels.channel_id');
  1572. $this->EE->db->where('exp_channel_titles.url_title', $this->EE->security->xss_clean($url_title));
  1573. }
  1574. else
  1575. {
  1576. return;
  1577. }
  1578. //get field group and limit
  1579. $this->EE->db->where('channels.site_id', $this->site_id);
  1580. $this->EE->db->limit(1);
  1581. $query = $this->EE->db->get('channels');
  1582. if ( ! $query->num_rows())
  1583. {
  1584. return;
  1585. }
  1586. $this->channel = $query->row_array();
  1587. if ( ! empty($this->EE->TMPL))
  1588. {
  1589. $this->EE->TMPL->tagparams['channel'] = $this->channel('channel_name');
  1590. }
  1591. $this->fetch_custom_fields();
  1592. }
  1593. // --------------------------------------------------------------------
  1594. /**
  1595. * Load custom fields
  1596. *
  1597. * @return void
  1598. */
  1599. public function fetch_custom_fields()
  1600. {
  1601. //exit if already loaded, or if there is no field group
  1602. if ($this->custom_fields || ! $this->channel('field_group'))
  1603. {
  1604. return;
  1605. }
  1606. $this->EE->load->model('channel_model');
  1607. $query = $this->EE->channel_model->get_channel_fields($this->channel('field_group'));
  1608. foreach ($query->result_array() as $row)
  1609. {
  1610. $this->custom_fields[$row['field_name']] = $row;
  1611. foreach ($this->unserialize($row['field_settings'], TRUE) as $key => $value)
  1612. {
  1613. $this->custom_fields[$row['field_name']][$key] = $value;
  1614. }
  1615. $this->custom_field_names[$row['field_id']] = $row['field_name'];
  1616. if (in_array($row['field_type'], $this->file_fields))
  1617. {
  1618. $this->file = TRUE;
  1619. }
  1620. }
  1621. //prepare the channel fields api
  1622. //which is use to trigger fieldtype methods,
  1623. //namely save and display_field
  1624. $this->EE->load->library('api');
  1625. $this->EE->api->instantiate(array('channel_fields'));
  1626. foreach ($this->custom_fields as $field)
  1627. {
  1628. if ( ! array_key_exists($field['field_type'], $this->EE->api_channel_fields->field_types))
  1629. {
  1630. $this->EE->api_channel_fields->field_types[$field['field_type']] = $this->EE->api_channel_fields->include_handler($field['field_type']);
  1631. }
  1632. $this->EE->api_channel_fields->custom_fields[$field['field_id']] = $field['field_type'];
  1633. $this->EE->api_channel_fields->set_settings($field['field_id'], $field);
  1634. $this->EE->api_channel_fields->setup_handler($field['field_id']);
  1635. }
  1636. }
  1637. // --------------------------------------------------------------------
  1638. /**
  1639. * Load entry
  1640. *
  1641. * @param mixed $entry_id
  1642. * @param mixed $url_title
  1643. * @return void
  1644. */
  1645. public function fetch_entry($entry_id, $url_title = FALSE)
  1646. {
  1647. //exit if already loaded, or no entry_id/url_title
  1648. if ($this->entry || ( ! $entry_id && ! $url_title))
  1649. {
  1650. return;
  1651. }
  1652. //fetch channel data, including custom fields
  1653. if ( ! $this->channel('channel_id'))
  1654. {
  1655. $this->fetch_channel(NULL, NULL, $entry_id, $url_title);
  1656. }
  1657. //get an array with entry title data, custom field data (with field_id_X AND short name keys)
  1658. $select = 'exp_channel_titles.*, exp_channel_data.*';
  1659. foreach ($this->custom_fields as $field)
  1660. {
  1661. $select .= ', exp_channel_data.`field_id_'.$field['field_id'].'` as `'.$field['field_name'].'`';
  1662. }
  1663. $this->EE->db->select($select, FALSE);
  1664. $this->EE->db->from('exp_channel_titles');
  1665. $this->EE->db->join('exp_channel_data', 'exp_channel_titles.entry_id = exp_channel_data.entry_id');
  1666. $this->EE->db->where('exp_channel_titles.site_id', $this->site_id);
  1667. $this->EE->db->where('exp_channel_titles.'.(($entry_id) ? 'entry_id' : 'url_title'), $this->EE->security->xss_clean(($entry_id) ? $entry_id : $url_title));
  1668. $this->EE->db->where('exp_channel_data.channel_id', $this->channel('channel_id'));
  1669. $this->EE->db->limit(1);
  1670. $query = $this->EE->db->get();
  1671. if ($query->num_rows())
  1672. {
  1673. $row = $query->row_array();
  1674. $row['categories'] = array();
  1675. $this->EE->db->select('cat_id');
  1676. $this->EE->db->where('entry_id', $row['entry_id']);
  1677. $cat_query = $this->EE->db->get('exp_category_posts');
  1678. foreach ($cat_query->result_array() as $cat_row)
  1679. {
  1680. $row['categories'][] = $cat_row['cat_id'];
  1681. }
  1682. $this->EE->api->instantiate('channel_fields');
  1683. foreach ($this->custom_fields as $field=>$definition)
  1684. {
  1685. if($definition['field_type'] == 'text') {
  1686. $this->EE->api_channel_fields->include_handler($definition['field_type']);
  1687. $handler = $this->EE->api_channel_fields->setup_handler($definition['field_type'], TRUE);
  1688. $row[$field] = $handler->_format_number($row[$field], $definition['field_content_type']);
  1689. }
  1690. }
  1691. $this->entry = $row;
  1692. }
  1693. unset($query);
  1694. }
  1695. // --------------------------------------------------------------------
  1696. /**
  1697. * Load logged out member data
  1698. *
  1699. * @param mixed $logged_out_member_id
  1700. * @return void
  1701. */
  1702. public function fetch_logged_out_member($logged_out_member_id = FALSE)
  1703. {
  1704. if ($this->EE->session->userdata('member_id') || $this->logged_out_member_id)
  1705. {
  1706. return;
  1707. }
  1708. if ( ! $logged_out_member_id && $this->channel('channel_id') && ! empty($this->settings['allow_guests'][$this->EE->config->item('site_id')][$this->channel('channel_id')]) && ! empty($this->settings['logged_out_member_id'][$this->EE->config->item('site_id')][$this->channel('channel_id')]))
  1709. {
  1710. $logged_out_member_id = $this->settings['logged_out_member_id'][$this->EE->config->item('site_id')][$this->channel('channel_id')];
  1711. }
  1712. $logged_out_member_id = $this->sanitize_int($logged_out_member_id);
  1713. if ($logged_out_member_id)
  1714. {
  1715. $this->EE->db->select('member_id, group_id');
  1716. $this->EE->db->where('member_id', $logged_out_member_id);
  1717. $query = $this->EE->db->get('members');
  1718. if ($query->num_rows() == 0)
  1719. {
  1720. // Invalid guest member id was specified
  1721. return $this->EE->output->show_user_error('general', lang('safecracker_invalid_guest_member_id'));
  1722. }
  1723. $this->logged_out_member_id = $query->row('member_id');
  1724. $this->logged_out_group_id = $query->row('group_id');
  1725. }
  1726. }
  1727. // --------------------------------------------------------------------
  1728. /**
  1729. * Load settings
  1730. *
  1731. * @return void
  1732. */
  1733. public function fetch_settings()
  1734. {
  1735. if ($this->settings === NULL)
  1736. {
  1737. $this->EE->db->select('settings');
  1738. $this->EE->db->where('class', 'Safecracker_ext');
  1739. $this->EE->db->limit(1);
  1740. $query = $this->EE->db->get('extensions');
  1741. $this->settings = ($query->row('settings')) ?
  1742. $this->unserialize($query->row('settings')) : FALSE;
  1743. }
  1744. }
  1745. // --------------------------------------------------------------------
  1746. /**
  1747. * Load site
  1748. *
  1749. * @return void
  1750. */
  1751. public function fetch_site($site_name = FALSE, $site_id = FALSE)
  1752. {
  1753. if ($site_name)
  1754. {
  1755. $query = $this->EE->db->select('site_id')->from('sites')->where('site_name', $site_name)->limit(1)->get();
  1756. $this->site_id = ($query->num_rows()) ? $query->row('site_id') : $this->EE->config->item('site_id');
  1757. }
  1758. else
  1759. {
  1760. $this->site_id = ($site_id) ? $site_id : $this->EE->config->item('site_id');
  1761. }
  1762. }
  1763. // --------------------------------------------------------------------
  1764. /**
  1765. * Load statuses
  1766. *
  1767. * @return void
  1768. */
  1769. public function fetch_statuses()
  1770. {
  1771. //exit if already loaded, or if there is no status group
  1772. if ($this->statuses || ! $this->channel('status_group'))
  1773. {
  1774. return;
  1775. }
  1776. $this->EE->load->model('channel_model');
  1777. $query = $this->EE->channel_model->get_channel_statuses($this->channel('status_group'));
  1778. $this->statuses = $query->result_array();
  1779. $this->EE->lang->loadfile('content');
  1780. foreach ($this->statuses as $index => $status)
  1781. {
  1782. $this->statuses[$index]['name'] = lang($status['status']);
  1783. $this->statuses[$index]['selected'] = ($status['status'] == $this->entry('status')) ? ' selected="selected"' : '';
  1784. $this->statuses[$index]['checked'] = ($status['status'] == $this->entry('status')) ? ' checked="checked"' : '';
  1785. }
  1786. // Remove statuses the member does not have access to.
  1787. // hat tip to @litzinger for the fix.
  1788. if ($this->EE->session->userdata('member_id') != 0)
  1789. {
  1790. $member_group_id = $this->EE->session->userdata('group_id');
  1791. }
  1792. // In the event the person isn't logged in, figure out what group_id
  1793. // we're supposed to be using
  1794. else
  1795. {
  1796. $this->fetch_logged_out_member();
  1797. $member_group_id = $this->logged_out_group_id;
  1798. }
  1799. $no_access = $this->EE->db->where('member_group', $member_group_id)
  1800. ->get('status_no_access')
  1801. ->result_array();
  1802. $remove = array();
  1803. foreach ($no_access as $no)
  1804. {
  1805. $remove[] = $no['status_id'];
  1806. }
  1807. foreach ($this->statuses as $idx => $status)
  1808. {
  1809. if (in_array($status['status_id'], $remove))
  1810. {
  1811. unset($this->statuses[$idx]);
  1812. }
  1813. }
  1814. }
  1815. // --------------------------------------------------------------------
  1816. /**
  1817. * Add a form attribute to entry form
  1818. *
  1819. * @param mixed $name
  1820. * @param mixed $value
  1821. * @return void
  1822. */
  1823. public function form_attribute($name, $value = '')
  1824. {
  1825. if (is_array($name))
  1826. {
  1827. foreach ($name as $key => $value)
  1828. {
  1829. $this->form_attribute($key, $value);
  1830. }
  1831. return;
  1832. }
  1833. if ($value === FALSE || $value === '')
  1834. {
  1835. return;
  1836. }
  1837. $this->EE->session->cache['safecracker']['form_declaration_data'][$name] = $value;
  1838. }
  1839. // --------------------------------------------------------------------
  1840. /**
  1841. * Add a hidden field to entry form
  1842. *
  1843. * @param mixed $name
  1844. * @param mixed $value
  1845. * @return void
  1846. */
  1847. public function form_hidden($name, $value = '')
  1848. {
  1849. if (is_array($name))
  1850. {
  1851. foreach ($name as $key => $value)
  1852. {
  1853. $this->form_hidden($key, $value);
  1854. }
  1855. return;
  1856. }
  1857. if ($value === FALSE || $value === '')
  1858. {
  1859. return;
  1860. }
  1861. $this->EE->session->cache['safecracker']['form_declaration_hidden_fields'][$name] = $value;
  1862. }
  1863. // --------------------------------------------------------------------
  1864. /**
  1865. * Retrieve field data
  1866. * Returns array of all field data if no key specified
  1867. *
  1868. * @param mixed $field_name
  1869. * @param mixed $key
  1870. * @return void
  1871. */
  1872. public function get_field_data($field_name, $key = FALSE)
  1873. {
  1874. if (in_array($field_name, $this->title_fields))
  1875. {
  1876. return array();
  1877. }
  1878. if (isset($this->custom_fields[$field_name]))
  1879. {
  1880. if ($key)
  1881. {
  1882. return (isset($this->custom_fields[$field_name][$key])) ? $this->custom_fields[$field_name][$key] : array();
  1883. }
  1884. else
  1885. {
  1886. return $this->custom_fields[$field_name];
  1887. }
  1888. }
  1889. return array();
  1890. }
  1891. // --------------------------------------------------------------------
  1892. /**
  1893. * Gets the field id of a field
  1894. *
  1895. * @param mixed $field_name
  1896. * @return void
  1897. */
  1898. public function get_field_id($field_name)
  1899. {
  1900. return $this->get_field_data($field_name, 'field_id');
  1901. }
  1902. // --------------------------------------------------------------------
  1903. /**
  1904. * Gets the field name of a field
  1905. *
  1906. * @param mixed $field_id
  1907. * @return void
  1908. */
  1909. public function get_field_name($field_id)
  1910. {
  1911. return (isset($this->custom_field_names[$field_id])) ? $this->custom_field_names[$field_id] : FALSE;
  1912. }
  1913. // --------------------------------------------------------------------
  1914. /**
  1915. * Gets a field's options
  1916. *
  1917. * @param mixed $field_name
  1918. * @return void
  1919. */
  1920. public function get_field_options($field_name)
  1921. {
  1922. $field = $this->get_field_data($field_name);
  1923. $options = array();
  1924. if (in_array($field['field_type'], $this->option_fields))
  1925. {
  1926. if ($field['field_pre_populate'] == 'y')
  1927. {
  1928. $query = $this->EE->db->select('field_id_'.$field['field_pre_field_id'])
  1929. ->distinct()
  1930. ->from('channel_data')
  1931. ->where('channel_id', $field['field_pre_channel_id'])
  1932. ->where('field_id_'.$field['field_pre_field_id'].' !=', '')
  1933. ->get();
  1934. $current = explode('|', $this->entry($field['field_name']));
  1935. foreach ($query->result_array() as $row)
  1936. {
  1937. $options[] = array(
  1938. 'option_value' => $row['field_id_'.$field['field_pre_field_id']],
  1939. 'option_name' => str_replace(array("\r\n", "\r", "\n", "\t"), ' ' , substr($row['field_id_'.$field['field_pre_field_id']], 0, 110)),
  1940. 'selected' => (in_array($row['field_id_'.$field['field_pre_field_id']], $current)) ? ' selected="selected"' : '',
  1941. 'checked' => (in_array($row['field_id_'.$field['field_pre_field_id']], $current)) ? ' checked="checked"' : '',
  1942. );
  1943. }
  1944. }
  1945. else if ($field['field_list_items'])
  1946. {
  1947. foreach (preg_split('/[\r\n]+/', $field['field_list_items']) as $row)
  1948. {
  1949. $row = trim($row);
  1950. if ( ! $row)
  1951. {
  1952. continue;
  1953. }
  1954. $field_data = (is_array($this->entry($field_name))) ? $this->entry($field_name) : explode('|', $this->entry($field_name));
  1955. $options[] = array(
  1956. 'option_value' => $row,
  1957. 'option_name' => $row,
  1958. 'selected' => (in_array($row, $field_data)) ? ' selected="selected"' : '',
  1959. 'checked' => (in_array($row, $field_data)) ? ' checked="checked"' : '',
  1960. );
  1961. }
  1962. }
  1963. else if ( ! in_array($field['field_type'], $this->native_option_fields))
  1964. {
  1965. $field_settings = $this->unserialize($field['field_settings'], TRUE);
  1966. if ( ! empty($field_settings['options']))
  1967. {
  1968. foreach ($field_settings['options'] as $option_value => $option_name)
  1969. {
  1970. $field_data = (is_array($this->entry($field_name))) ? $this->entry($field_name) : preg_split('/[\r\n]+/', $this->entry($field_name));
  1971. $options[] = array(
  1972. 'option_value' => $option_value,
  1973. 'option_name' => $option_name,
  1974. 'selected' => (in_array($option_value, $field_data)) ? ' selected="selected"' : '',
  1975. 'checked' => (in_array($option_value, $field_data)) ? ' checked="checked"' : ''
  1976. );
  1977. }
  1978. }
  1979. }
  1980. }
  1981. else if ($field['field_type'] == 'rel')
  1982. {
  1983. $rel_child_id = '';
  1984. if ($this->entry($field_name))
  1985. {
  1986. if ($this->form_error)
  1987. {
  1988. $rel_child_id = $this->entry($field_name);
  1989. }
  1990. else
  1991. {
  1992. $this->EE->db->select('rel_child_id');
  1993. $this->EE->db->where('rel_id', $this->entry($field_name));
  1994. $this->EE->db->where('rel_parent_id', $this->entry('entry_id'));
  1995. $query = $this->EE->db->get('relationships');
  1996. $rel_child_id = $query->row('rel_child_id');
  1997. }
  1998. }
  1999. $orderby = $this->get_field_data($field_name, 'field_related_orderby');
  2000. if ($orderby == 'date')
  2001. {
  2002. $orderby = 'entry_'.$orderby;
  2003. }
  2004. $this->EE->db->select('entry_id, title');
  2005. $this->EE->db->where('channel_id', $this->get_field_data($field_name, 'field_related_id'));
  2006. $this->EE->db->order_by($orderby, $this->get_field_data($field_name, 'field_related_sort'));
  2007. if ($this->get_field_data($field_name, 'field_related_max'))
  2008. {
  2009. $this->EE->db->limit($this->get_field_data($field_name, 'field_related_max'));
  2010. }
  2011. $query = $this->EE->db->get('channel_titles');
  2012. foreach ($query->result() as $row)
  2013. {
  2014. $options[] = array(
  2015. 'option_value' => $row->entry_id,
  2016. 'option_name' => $row->title,
  2017. 'selected' => ($row->entry_id == $rel_child_id) ? ' selected="selected"' : '',
  2018. 'checked' => ($row->entry_id == $rel_child_id) ? ' checked="checked"' : '',
  2019. );
  2020. }
  2021. }
  2022. return $options;
  2023. }
  2024. // --------------------------------------------------------------------
  2025. /**
  2026. * Gets a field's settings
  2027. *
  2028. * @param mixed $field_name
  2029. * @param mixed $unserialize = TRUE
  2030. * @return void
  2031. */
  2032. public function get_field_settings($field_name, $unserialize = TRUE)
  2033. {
  2034. if ( ! $field_settings = $this->get_field_data($field_name, 'field_settings'))
  2035. {
  2036. return array();
  2037. }
  2038. return ($unserialize) ? $this->unserialize($field_settings, TRUE) : $field_settings;
  2039. }
  2040. // --------------------------------------------------------------------
  2041. /**
  2042. * Gets the type of a field
  2043. *
  2044. * @param mixed $field_name
  2045. * @return void
  2046. */
  2047. public function get_field_type($field_name)
  2048. {
  2049. return $this->get_field_data($field_name, 'field_type');
  2050. }
  2051. // --------------------------------------------------------------------
  2052. /**
  2053. * Initialize the library properties
  2054. *
  2055. * @return void
  2056. */
  2057. public function initialize($reinitialize = FALSE)
  2058. {
  2059. if ($this->initialized && ! $reinitialize)
  2060. {
  2061. return;
  2062. }
  2063. $this->initialized = TRUE;
  2064. $this->categories = array();
  2065. $this->channel = array();
  2066. $this->checkboxes = array(
  2067. 'sticky',
  2068. 'dst_enabled',
  2069. 'allow_comments'
  2070. );
  2071. $this->custom_field_conditional_names = array(
  2072. 'rel' => 'relationship',
  2073. 'text' => 'textinput',
  2074. 'select' => 'pulldown',
  2075. 'checkboxes' => 'checkbox',
  2076. 'multi_select' => 'multiselect'
  2077. );
  2078. $this->custom_fields = array();
  2079. $this->custom_option_fields = array();
  2080. $this->date_fields = array(
  2081. 'comment_expiration_date',
  2082. 'expiration_date',
  2083. 'entry_date',
  2084. 'edit_date',
  2085. 'recent_comment_date',
  2086. 'recent_trackback_date'
  2087. );
  2088. $this->datepicker = TRUE;
  2089. $this->default_fields = array(
  2090. 'title' => array(
  2091. 'field_name' => 'title',
  2092. 'field_label' => 'lang:title',
  2093. 'field_type' => 'text',
  2094. 'rules' => 'required|call_field_validation[title]'
  2095. ),
  2096. 'url_title' => array(
  2097. 'field_name' => 'url_title',
  2098. 'field_label' => 'lang:url_title',
  2099. 'field_type' => 'text',
  2100. 'rules' => 'call_field_validation[url_title]'
  2101. ),
  2102. 'entry_date' => array(
  2103. 'field_name' => 'entry_date',
  2104. 'field_label' => 'lang:entry_date',
  2105. 'field_type' => 'date',
  2106. 'rules' => 'required|call_field_validation[entry_date]|callback_valid_ee_date'
  2107. ),
  2108. 'expiration_date' => array(
  2109. 'field_name' => 'expiration_date',
  2110. 'field_label' => 'lang:expiration_date',
  2111. 'field_type' => 'date',
  2112. 'rules' => 'call_field_validation[expiration_date]'
  2113. ),
  2114. 'comment_expiration_date' => array(
  2115. 'field_name' => 'comment_expiration_date',
  2116. 'field_label' => 'lang:comment_expiration_date',
  2117. 'field_type' => 'date',
  2118. 'rules' => 'call_field_validation[comment_expiration_date]'
  2119. )
  2120. );
  2121. $this->edit = FALSE;
  2122. $this->entry = array();
  2123. $this->error_handling = 'message';
  2124. $this->errors = array();
  2125. $this->field_errors = array();
  2126. $this->file = FALSE;
  2127. $this->file_fields = array(
  2128. // @todo: As of EE 2.2 (or earlier), SafeCracker doesn't fully work with standard file fields, only SafeCracker File
  2129. //'file',
  2130. 'safecracker_file'
  2131. );
  2132. $this->form_validation_methods = array(
  2133. 'valid_ee_date'
  2134. );
  2135. $this->head = '';
  2136. $this->json = FALSE;
  2137. $this->logged_out_member_id = FALSE;
  2138. $this->logged_out_group_id = FALSE;
  2139. $this->native_option_fields = array(
  2140. 'multi_select',
  2141. 'select',
  2142. 'radio',
  2143. 'checkboxes'
  2144. );
  2145. $this->native_variables = array(
  2146. 'comment_expiration_date' => 'date',
  2147. 'expiration_date' => 'date',
  2148. 'entry_date' => 'date',
  2149. 'url_title' => 'text',
  2150. 'sticky' => FALSE,
  2151. 'dst_enabled' => FALSE,
  2152. 'allow_comments' => FALSE,
  2153. 'title' => 'text'
  2154. );
  2155. $this->option_fields = array();
  2156. $this->parse_variables = array();
  2157. $this->pre_save = array(
  2158. 'matrix'
  2159. );
  2160. $this->preserve_checkboxes = FALSE;
  2161. $this->post_error_callbacks = array();
  2162. $this->require_save_call = array();
  2163. $this->skip_xss_fieldtypes = array();
  2164. $this->skip_xss_field_ids = array();
  2165. $this->statuses = array();
  2166. $this->show_fields = array();
  2167. $this->title_fields = array(
  2168. 'entry_id',
  2169. 'site_id',
  2170. 'channel_id',
  2171. 'author_id',
  2172. 'pentry_id',
  2173. 'forum_topic_id',
  2174. 'ip_address',
  2175. 'title',
  2176. 'url_title',
  2177. 'status',
  2178. 'versioning_enabled',
  2179. 'view_count_one',
  2180. 'view_count_two',
  2181. 'view_count_three',
  2182. 'view_count_four',
  2183. 'allow_comments',
  2184. 'sticky',
  2185. 'entry_date',
  2186. 'dst_enabled',
  2187. 'year',
  2188. 'month',
  2189. 'day',
  2190. 'expiration_date',
  2191. 'comment_expiration_date',
  2192. 'edit_date',
  2193. 'recent_comment_date',
  2194. 'comment_total',
  2195. );
  2196. $this->valid_callbacks = array(
  2197. 'html_entity_decode',
  2198. 'htmlentities'
  2199. );
  2200. $this->fetch_settings();
  2201. $this->option_fields = $this->native_option_fields;
  2202. $this->EE->config->load('config');
  2203. if (is_array($this->EE->config->item('safecracker_option_fields')))
  2204. {
  2205. $this->custom_option_fields = $this->EE->config->item('safecracker_option_fields');
  2206. $this->option_fields = array_merge($this->option_fields, $this->custom_option_fields);
  2207. }
  2208. if (is_array($this->EE->config->item('safecracker_post_error_callbacks')))
  2209. {
  2210. $this->post_error_callbacks = array_merge($this->post_error_callbacks, $this->EE->config->item('safecracker_post_error_callbacks'));
  2211. }
  2212. if (is_array($this->EE->config->item('safecracker_file_fields')))
  2213. {
  2214. $this->file_fields = array_merge($this->file_fields, $this->EE->config->item('safecracker_file_fields'));
  2215. }
  2216. if (is_array($this->EE->config->item('safecracker_require_save_call')))
  2217. {
  2218. $this->require_save_call = $this->EE->config->item('safecracker_require_save_call');
  2219. }
  2220. if (is_array($this->EE->config->item('safecracker_field_extra_js')))
  2221. {
  2222. $this->extra_js = $this->EE->config->item('safecracker_field_extra_js');
  2223. }
  2224. }
  2225. // --------------------------------------------------------------------
  2226. /**
  2227. * Loads the channel standalone module
  2228. *
  2229. * @return void
  2230. */
  2231. public function load_channel_standalone()
  2232. {
  2233. require_once(PATH_MOD.'channel/mod.channel.php');
  2234. require_once(PATH_MOD.'channel/mod.channel_standalone.php');
  2235. $this->channel_standalone = new Channel_standalone();
  2236. }
  2237. // --------------------------------------------------------------------
  2238. /**
  2239. * Loads the session override library
  2240. *
  2241. * @return void
  2242. */
  2243. public function load_session_override()
  2244. {
  2245. if (empty($this->logged_out_member_id))
  2246. {
  2247. return;
  2248. }
  2249. $this->temp_session = $this->EE->session;
  2250. if ( ! class_exists('SC_Session'))
  2251. {
  2252. require_once PATH_MOD.'safecracker/libraries/SC_Session.php';
  2253. }
  2254. $this->EE->session = new SC_Session(array(
  2255. 'session_object' => $this->temp_session,
  2256. 'logged_out_member_id' => $this->logged_out_member_id,
  2257. 'logged_out_group_id' => $this->logged_out_group_id
  2258. ));
  2259. }
  2260. // --------------------------------------------------------------------
  2261. /**
  2262. * Replaces a tag
  2263. *
  2264. * @param mixed $field_name
  2265. * @param mixed $data
  2266. * @param mixed $params = array()
  2267. * @param mixed $tagdata = FALSE
  2268. * @return void
  2269. */
  2270. public function replace_tag($field_name, $data, $params = array(), $tagdata = FALSE)
  2271. {
  2272. if ( ! $params)
  2273. {
  2274. $params = array();
  2275. }
  2276. if ( ! isset($this->custom_fields[$field_name]))
  2277. {
  2278. return $tagdata;
  2279. }
  2280. $this->EE->load->library('api');
  2281. $this->EE->load->helper('custom_field');
  2282. $this->EE->api->instantiate('channel_fields');
  2283. $this->EE->api_channel_fields->field_type = $this->get_field_type($field_name);
  2284. $this->EE->api_channel_fields->field_types[$this->EE->api_channel_fields->field_type]->settings = array_merge($this->get_field_settings($field_name), $this->get_field_data($field_name), $this->EE->api_channel_fields->get_global_settings($this->EE->api_channel_fields->field_type));
  2285. $_GET['entry_id'] = $this->entry('entry_id');
  2286. $this->EE->api_channel_fields->apply('_init', array(array('row' => $this->entry)));
  2287. $data = $this->EE->api_channel_fields->apply('pre_process', array($data));
  2288. return $this->EE->api_channel_fields->apply('replace_tag', array('data' => $data, 'params' => $params, 'tagdata' => $tagdata));
  2289. }
  2290. // --------------------------------------------------------------------
  2291. /**
  2292. * Clean an ID
  2293. *
  2294. * @param mixed $id
  2295. * @return mixed
  2296. */
  2297. public function sanitize_int($data)
  2298. {
  2299. if (is_int($data + 0))
  2300. {
  2301. return $data;
  2302. }
  2303. $data = preg_replace('/[^\d]/', '', $data);
  2304. return ($data) ? $data : FALSE;
  2305. }
  2306. // --------------------------------------------------------------------
  2307. public function send_ajax_response($msg, $error = FALSE)
  2308. {
  2309. if ($this->EE->config->item('send_headers') == 'y')
  2310. {
  2311. //so the output class doesn't try to send any headers
  2312. //we are taking over
  2313. $this->EE->config->config['send_headers'] = NULL;
  2314. $this->EE->load->library('user_agent', array(), 'user_agent');
  2315. //many browsers do not consistently like this content type
  2316. //array('Firefox', 'Mozilla', 'Netscape', 'Camino', 'Firebird')
  2317. if (is_array($msg) && in_array($this->EE->user_agent->browser(), array('Safari', 'Chrome')))
  2318. {
  2319. @header('Content-Type: application/json');
  2320. }
  2321. else
  2322. {
  2323. @header('Content-Type: text/html; charset=UTF-8');
  2324. }
  2325. }
  2326. $this->EE->output->send_ajax_response($msg, $error);
  2327. }
  2328. // --------------------------------------------------------------------
  2329. /**
  2330. * swap_conditionals
  2331. *
  2332. * @param mixed $tagdata
  2333. * @param mixed $conditionals
  2334. * @return void
  2335. */
  2336. public function swap_conditionals($tagdata, $conditionals)
  2337. {
  2338. $tagdata = $this->EE->functions->prep_conditionals($tagdata, $conditionals);
  2339. $tagdata = preg_replace('/\{if\s+[\042\047]*0[\042\047]*\}(.+?)\{\/if\}/si', '', $tagdata);
  2340. $tagdata = preg_replace('/\{if\s+[\042\047]*1[\042\047]*\}(.+?)\{\/if\}/si', '\\1', $tagdata);
  2341. return $tagdata;
  2342. }
  2343. // --------------------------------------------------------------------
  2344. /**
  2345. * swap_var_pair
  2346. *
  2347. * @param mixed $key
  2348. * @param mixed $rows
  2349. * @param mixed $tagdata
  2350. * @param mixed $close_key = ''
  2351. * @param mixed $backspace = FALSE
  2352. * @return void
  2353. */
  2354. public function swap_var_pair($key, $rows, $tagdata, $close_key = '', $backspace = FALSE)
  2355. {
  2356. $close_key = ($close_key) ? $close_key : $key;
  2357. if (preg_match_all('/'.LD.$key.RD.'(.*?)'.LD.'\/'.$close_key.RD.'/s', $tagdata, $matches))
  2358. {
  2359. foreach ($matches[1] as $match_index => $var_pair_tagdata)
  2360. {
  2361. $output = '';
  2362. foreach ($rows as $row)
  2363. {
  2364. $row_output = $var_pair_tagdata;
  2365. foreach ($row as $k => $v)
  2366. {
  2367. $row_output = $this->EE->TMPL->swap_var_single($k, $v, $row_output);
  2368. }
  2369. $output .= $row_output."\n";
  2370. }
  2371. if ($backspace && is_numeric($backspace))
  2372. {
  2373. $output = substr($output, 0, -1*($backspace+1));
  2374. }
  2375. $tagdata = str_replace($matches[0][$match_index], $output, $tagdata);
  2376. }
  2377. }
  2378. return $tagdata;
  2379. }
  2380. // --------------------------------------------------------------------
  2381. /**
  2382. * unload_session_override
  2383. *
  2384. * @return void
  2385. */
  2386. public function unload_session_override()
  2387. {
  2388. if (empty($this->logged_out_member_id))
  2389. {
  2390. return;
  2391. }
  2392. $this->EE->session = $this->temp_session;
  2393. unset($this->temp_session);
  2394. }
  2395. // --------------------------------------------------------------------
  2396. /**
  2397. * unserialize
  2398. *
  2399. * @param mixed $data
  2400. * @param mixed $base64_decode = FALSE
  2401. * @return void
  2402. */
  2403. public function unserialize($data, $base64_decode = FALSE)
  2404. {
  2405. if ($base64_decode)
  2406. {
  2407. $data = base64_decode($data);
  2408. }
  2409. $data = @unserialize($data);
  2410. return (is_array($data)) ? $data : array();
  2411. }
  2412. // --------------------------------------------------------------------
  2413. /**
  2414. * Get relationship data
  2415. *
  2416. * @param array $rel_ids
  2417. * @return object
  2418. */
  2419. public function api_safe_rel_ids($rel_ids)
  2420. {
  2421. $this->EE->db->select('rel_id, rel_parent_id, rel_child_id');
  2422. $this->EE->db->where_in('rel_id', $rel_ids);
  2423. $query = $this->EE->db->get('relationships');
  2424. return $query;
  2425. }
  2426. }
  2427. /* End of file safecracker_lib.php */
  2428. /* Location: ./system/expressionengine/modules/safecracker/libraries/safecracker_lib.php */