PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/system/cms/modules/streams_core/libraries/Fields.php

https://github.com/gsake/pyrocms
PHP | 764 lines | 367 code | 125 blank | 272 comment | 52 complexity | 81d122795ca4caf0be0389b790599033 MD5 | raw file
Possible License(s): CC-BY-3.0, BSD-3-Clause, CC0-1.0, MIT
  1. <?php defined('BASEPATH') or exit('No direct script access allowed');
  2. /**
  3. * PyroStreams Core Fields Library
  4. *
  5. * Handles forms and other field form logic.
  6. *
  7. * @package PyroCMS\Core\Modules\Streams Core\Libraries
  8. * @author Parse19
  9. * @copyright Copyright (c) 2011 - 2012, Parse19
  10. * @license http://parse19.com/pyrostreams/docs/license
  11. * @link http://parse19.com/pyrostreams
  12. */
  13. class Fields
  14. {
  15. function __construct()
  16. {
  17. $this->CI = get_instance();
  18. $this->CI->load->helper('form');
  19. }
  20. // --------------------------------------------------------------------------
  21. /**
  22. * Build form input
  23. *
  24. * Builds an individual form input from the
  25. * type object
  26. *
  27. * @access public
  28. * @param obj
  29. * @param bool
  30. * @return string
  31. */
  32. public function build_form_input($field, $value = null, $row_id = null, $plugin = false)
  33. {
  34. $form_data['form_slug'] = $field->field_slug;
  35. $form_data['custom'] = $field->field_data;
  36. $form_data['value'] = $value;
  37. $form_data['max_length'] = (isset($field->field_data['max_length'])) ? $field->field_data['max_length'] : null;
  38. // If this is for a plugin, this relies on a function that
  39. // many field types will not have
  40. if ($plugin)
  41. {
  42. if (method_exists($this->CI->type->types->{$field->field_type}, 'form_output_plugin'))
  43. {
  44. return $this->CI->type->types->{$field->field_type}->form_output_plugin($form_data, $row_id, $field);
  45. }
  46. else
  47. {
  48. return false;
  49. }
  50. }
  51. else
  52. {
  53. return $this->CI->type->types->{$field->field_type}->form_output($form_data, $row_id, $field);
  54. }
  55. }
  56. // --------------------------------------------------------------------------
  57. /**
  58. * Build the form validation rules and the actual output.
  59. *
  60. * Based on the type of application we need it for, it will
  61. * either return a full form or an array of elements.
  62. *
  63. * @access public
  64. * @param obj
  65. * @param string
  66. * @param mixed - false or row object
  67. * @param bool - is this a plugin call?
  68. * @param bool - are we using reCAPTCHA?
  69. * @param array - all the skips
  70. * @param array - extra data:
  71. *
  72. * - email_notifications
  73. * - return
  74. * - success_message
  75. * - failure_message
  76. * - error_start
  77. * - error_end
  78. * - required
  79. *
  80. * @return array - fields
  81. */
  82. public function build_form($stream, $method, $row = false, $plugin = false, $recaptcha = false, $skips = array(), $extra = array())
  83. {
  84. $this->CI->load->helper(array('form', 'url'));
  85. // -------------------------------------
  86. // Set default extras
  87. // -------------------------------------
  88. $default_extras = array(
  89. 'email_notifications' => NULL,
  90. 'return' => current_url(),
  91. 'error_start' => NULL,
  92. 'error_end' => NULL,
  93. 'required' => '<span>*</span>'
  94. );
  95. $this->CI->load->language('streams_core/pyrostreams');
  96. if ($method == 'new')
  97. {
  98. $default_extras['success_message'] = 'lang:streams.new_entry_success';
  99. $default_extras['failure_message'] = 'lang:streams.new_entry_error';
  100. }
  101. else
  102. {
  103. $default_extras['success_message'] = 'lang:streams.edit_entry_success';
  104. $default_extras['failure_message'] = 'lang:streams.edit_entry_error';
  105. }
  106. foreach($default_extras as $key => $value)
  107. {
  108. if( ! isset($extra[$key])) $extra[$key] = $value;
  109. }
  110. extract($extra);
  111. // -------------------------------------
  112. // Get Stream Fields
  113. // -------------------------------------
  114. $stream_fields = $this->CI->streams_m->get_stream_fields($stream->id);
  115. // Can't do nothing if we don't have any fields
  116. if ($stream_fields === false) return false;
  117. // -------------------------------------
  118. // Run Type Events
  119. // -------------------------------------
  120. $events_called = $this->run_field_events($stream_fields, $skips);
  121. // -------------------------------------
  122. // Set Validation Rules
  123. // -------------------------------------
  124. $row_id = ($method == 'edit') ? $row->id : null;
  125. $this->set_rules($stream_fields, $method, $skips, false, $row_id);
  126. // -------------------------------------
  127. // Set Error Delimns
  128. // -------------------------------------
  129. $this->CI->form_validation->set_error_delimiters($extra['error_start'], $extra['error_end']);
  130. // -------------------------------------
  131. // Set reCAPTCHA
  132. // -------------------------------------
  133. if ($recaptcha)
  134. {
  135. $this->CI->config->load('streams_core/recaptcha');
  136. $this->CI->load->library('streams_core/Recaptcha');
  137. $this->CI->validation->set_rules('recaptcha_response_field', 'lang:recaptcha_field_name', 'required|check_captcha');
  138. }
  139. // -------------------------------------
  140. // Set Values
  141. // -------------------------------------
  142. $values = $this->set_values($stream_fields, $row, $method, $skips);
  143. // -------------------------------------
  144. // Validation
  145. // -------------------------------------
  146. $result_id = '';
  147. if ($this->CI->form_validation->run() === TRUE)
  148. {
  149. if ($method == 'new')
  150. {
  151. if ( ! $result_id = $this->CI->row_m->insert_entry($_POST, $stream_fields, $stream, $skips))
  152. {
  153. $this->CI->session->set_flashdata('notice', $this->CI->fields->translate_label($failure_message));
  154. }
  155. else
  156. {
  157. // -------------------------------------
  158. // Send Emails
  159. // -------------------------------------
  160. if ($plugin and (isset($email_notifications) and $email_notifications))
  161. {
  162. foreach ($email_notifications as $notify)
  163. {
  164. $this->send_email($notify, $result_id, $method = 'new', $stream);
  165. }
  166. }
  167. // -------------------------------------
  168. $this->CI->session->set_flashdata('success', $this->CI->fields->translate_label($extra['success_message']));
  169. }
  170. }
  171. else
  172. {
  173. if ( ! $result_id = $this->CI->row_m->update_entry(
  174. $stream_fields,
  175. $stream,
  176. $row->id,
  177. $this->CI->input->post(),
  178. $skips
  179. ))
  180. {
  181. $this->CI->session->set_flashdata('notice', $this->CI->fields->translate_label($extra['failure_message']));
  182. }
  183. else
  184. {
  185. // -------------------------------------
  186. // Send Emails
  187. // -------------------------------------
  188. if ($plugin AND (isset($extra['email_notifications']) AND is_array($extra['email_notifications'])))
  189. {
  190. foreach($extra['email_notifications'] as $notify)
  191. {
  192. $this->send_email($notify, $result_id, $method = 'update', $stream);
  193. }
  194. }
  195. // -------------------------------------
  196. $this->CI->session->set_flashdata('success', $this->CI->fields->translate_label($extra['success_message']));
  197. }
  198. }
  199. // Redirect and replace -id- with the result ID
  200. redirect(str_replace('-id-', $result_id, $extra['return']));
  201. }
  202. // -------------------------------------
  203. // Set Fields & Return Them
  204. // -------------------------------------
  205. return $this->build_fields($stream_fields, $values, $row, $method, $skips, $extra['required']);
  206. }
  207. // --------------------------------------------------------------------------
  208. /**
  209. * Run Field Events
  210. *
  211. * Runs all the event() functions for some
  212. * stream fields. The event() functions usually
  213. * have field asset loads.
  214. *
  215. * @access public
  216. * @param obj - stream fields
  217. * @param [array - skips]
  218. * @return array
  219. */
  220. public function run_field_events($stream_fields, $skips = array())
  221. {
  222. $events_called = array();
  223. foreach ($stream_fields as $field)
  224. {
  225. if ( ! in_array($field->field_slug, $skips))
  226. {
  227. // If we haven't called it (for dupes),
  228. // then call it already.
  229. if ( ! in_array($field->field_type, $events_called))
  230. {
  231. if(method_exists($this->CI->type->types->{$field->field_type}, 'event'))
  232. {
  233. $this->CI->type->types->{$field->field_type}->event($field);
  234. }
  235. $events_called[] = $field->field_type;
  236. }
  237. }
  238. }
  239. return $events_called;
  240. }
  241. // --------------------------------------------------------------------------
  242. /**
  243. * Gather values into an array
  244. * for a form
  245. *
  246. * @access public
  247. * @param object - stream_fields
  248. * @param object - row
  249. * @param string - edit or new
  250. * @param array
  251. * @return array
  252. */
  253. public function set_values($stream_fields, $row, $mode, $skips)
  254. {
  255. $values = array();
  256. foreach ($stream_fields as $stream_field)
  257. {
  258. if ( ! in_array($stream_field->field_slug, $skips))
  259. {
  260. if ( ! isset($_POST[$stream_field->field_slug]) and ! isset($_POST[$stream_field->field_slug.'[]']))
  261. {
  262. // If this is a new entry and there is no post data,
  263. // we see if:
  264. // a - there is data from the DB to show
  265. // b - there is a default value to show
  266. // Otherwise, it's just null
  267. if (isset($row->{$stream_field->field_slug}))
  268. {
  269. $values[$stream_field->field_slug] = $row->{$stream_field->field_slug};
  270. }
  271. else
  272. {
  273. $values[$stream_field->field_slug] = (isset($stream_field->field_data['default_value'])) ? $stream_field->field_data['default_value'] : null;
  274. }
  275. }
  276. else
  277. {
  278. // Post Data - we always show
  279. // post data above any other data that
  280. // might be sitting around.
  281. // There is the possibility that this could be an array
  282. // post value, so we check for that as well.
  283. if (isset($_POST[$stream_field->field_slug]))
  284. {
  285. $values[$stream_field->field_slug] = $this->CI->input->post($stream_field->field_slug);
  286. }
  287. elseif (isset($_POST[$stream_field->field_slug.'[]']))
  288. {
  289. $values[$stream_field->field_slug] = $this->CI->input->post($stream_field->field_slug.'[]');
  290. }
  291. else
  292. {
  293. // Last ditch.
  294. $values[$stream_field->field_slug] = null;
  295. }
  296. }
  297. }
  298. }
  299. return $values;
  300. }
  301. // --------------------------------------------------------------------------
  302. /**
  303. * Build Fields
  304. *
  305. * Builds fields (no validation)
  306. *
  307. */
  308. public function build_fields($stream_fields, $values = array(), $row = null, $method = 'new', $skips = array(), $required = '<span>*</span>')
  309. {
  310. $fields = array();
  311. $count = 0;
  312. foreach($stream_fields as $slug => $field)
  313. {
  314. if ( ! in_array($field->field_slug, $skips))
  315. {
  316. $fields[$count]['input_title'] = $field->field_name;
  317. $fields[$count]['input_slug'] = $field->field_slug;
  318. $fields[$count]['instructions'] = $field->instructions;
  319. // The default default value is null.
  320. if ( ! isset($field->field_data['default_value']))
  321. {
  322. $field->field_data['default_value'] = null;
  323. }
  324. // Set the value. In the odd case it isn't set,
  325. // jst set it to null.
  326. $value = (isset($values[$field->field_slug])) ? $values[$field->field_slug] : null;
  327. // Return the raw value as well - can be useful
  328. $fields[$count]['value'] = $value;
  329. // Get the acutal form input
  330. if ($method == 'edit')
  331. {
  332. $fields[$count]['input'] = $this->build_form_input($field, $value, $row->id);
  333. $fields[$count]['input_parts'] = $this->build_form_input($field, $value, $row->id, true);
  334. }
  335. else
  336. {
  337. $fields[$count]['input'] = $this->build_form_input($field, $value, null);
  338. $fields[$count]['input_parts'] = $this->build_form_input($field, $value, null, true);
  339. }
  340. // Set the error if there is one
  341. $fields[$count]['error_raw'] = $this->CI->form_validation->error($field->field_slug);
  342. // Format tht error
  343. if ($fields[$count]['error_raw'])
  344. {
  345. $fields[$count]['error'] = $this->CI->form_validation->format_error($fields[$count]['error_raw']);
  346. }
  347. else
  348. {
  349. $fields[$count]['error'] = null;
  350. }
  351. // Set the required string
  352. $fields[$count]['required'] = ($field->is_required == 'yes') ? $required : null;
  353. // Set even/odd
  354. $fields[$count]['odd_even'] = (($count+1)%2 == 0) ? 'even' : 'odd';
  355. $count++;
  356. }
  357. }
  358. return $fields;
  359. }
  360. // --------------------------------------------------------------------------
  361. /**
  362. * Set Rules
  363. *
  364. * Set the rules from the stream fields
  365. *
  366. * @access public
  367. * @param obj - fields to set rules for
  368. * @param string - method - edit or new
  369. * @param array - fields to skip
  370. * @param bool - return the array or set the validation
  371. * @param mixed - array or true
  372. */
  373. public function set_rules($stream_fields, $method, $skips = array(), $return_array = false, $row_id = null)
  374. {
  375. $validation_rules = array();
  376. // -------------------------------------
  377. // Loop through and set the rules
  378. // -------------------------------------
  379. foreach ($stream_fields as $stream_field)
  380. {
  381. if ( ! in_array($stream_field->field_slug, $skips))
  382. {
  383. $rules = array();
  384. $type = $this->CI->type->types->{$stream_field->field_type};
  385. // -------------------------------------
  386. // Pre Validation Event
  387. // -------------------------------------
  388. if (method_exists($type, 'pre_validation_compile'))
  389. {
  390. $type->pre_validation_compile($stream_field);
  391. }
  392. // -------------------------------------
  393. // Set required if necessary
  394. // -------------------------------------
  395. if ($stream_field->is_required == 'yes')
  396. {
  397. if (isset($type->input_is_file) && $type->input_is_file === TRUE)
  398. {
  399. $rules[] = 'streams_file_required['.$stream_field->field_slug.']';
  400. }
  401. else
  402. {
  403. $rules[] = 'required';
  404. }
  405. }
  406. // -------------------------------------
  407. // Validation Function
  408. // -------------------------------------
  409. // We are using a generic streams validation
  410. // function to use a validate() function
  411. // in the field type itself.
  412. // -------------------------------------
  413. if (method_exists($type, 'validate'))
  414. {
  415. $rules[] = "streams_field_validation[{$stream_field->field_id}:{$method}]";
  416. }
  417. // -------------------------------------
  418. // Set unique if necessary
  419. // -------------------------------------
  420. if ($stream_field->is_unique == 'yes')
  421. {
  422. $rules[] = 'streams_unique['.$stream_field->field_slug.':'.$method.':'.$stream_field->stream_id.':'.$row_id.']';
  423. }
  424. // -------------------------------------
  425. // Set extra validation
  426. // -------------------------------------
  427. if (isset($type->extra_validation))
  428. {
  429. if (is_string($type->extra_validation))
  430. {
  431. $extra_rules = explode('|', $type->extra_validation);
  432. $rules = array_merge($rules, $extra_rules);
  433. unset($extra_rules);
  434. }
  435. elseif (is_array($type->extra_validation))
  436. {
  437. $rules = array_merge($rules, $type->extra_validation);
  438. }
  439. }
  440. // -------------------------------------
  441. // Remove duplicate rule values
  442. // -------------------------------------
  443. $rules = array_unique($rules);
  444. // -------------------------------------
  445. // Add to validation rules array
  446. // and unset $rules
  447. // -------------------------------------
  448. $validation_rules[] = array(
  449. 'field' => $stream_field->field_slug,
  450. 'label' => $stream_field->field_name,
  451. 'rules' => implode('|', $rules)
  452. );
  453. unset($rules);
  454. }
  455. }
  456. // -------------------------------------
  457. // Set the rules or return them
  458. // -------------------------------------
  459. if ($return_array)
  460. {
  461. return $validation_rules;
  462. }
  463. else
  464. {
  465. $this->CI->form_validation->set_rules($validation_rules);
  466. return true;
  467. }
  468. }
  469. // --------------------------------------------------------------------------
  470. /**
  471. * Run Field Setup Event Functions
  472. *
  473. * This allows field types to add custom CSS/JS
  474. * to the field setup (edit/delete screen).
  475. *
  476. * @access public
  477. * @param [obj - stream]
  478. * @param [string - method - new or edit]
  479. * @param [obj or null (for new fields) - field]
  480. * @return
  481. */
  482. public function run_field_setup_events($stream = null, $method = 'new', $field = null)
  483. {
  484. foreach ($this->CI->type->types as $ft)
  485. {
  486. if (method_exists($ft, 'field_setup_event'))
  487. {
  488. $ft->field_setup_event($stream, $method, $field);
  489. }
  490. }
  491. }
  492. // --------------------------------------------------------------------------
  493. /**
  494. * Translate a label.
  495. *
  496. * If it has the label: before it, then we can
  497. * look for a language line.
  498. *
  499. * This is partially from the CodeIgniter Form Validation
  500. * library but it protected so we need to replicate the
  501. * functionality here.
  502. *
  503. * @access public
  504. * @param string - the field label
  505. * @return string - translated or original label
  506. */
  507. public function translate_label($label)
  508. {
  509. // Look for lang
  510. if (substr($label, 0, 5) === 'lang:')
  511. {
  512. $line = substr($label, 5);
  513. if (($label = $this->CI->lang->line($line)) === false)
  514. {
  515. return $line;
  516. }
  517. }
  518. return $label;
  519. }
  520. // --------------------------------------------------------------------------
  521. /**
  522. * Send Emails
  523. *
  524. * Sends emails for a notify group
  525. *
  526. * @access public
  527. * @param string - a or b
  528. * @param int - the entry id
  529. * @param string - method - update or new
  530. * @param obj - the stream
  531. * @return void
  532. */
  533. public function send_email($notify, $entry_id, $method, $stream)
  534. {
  535. extract($notify);
  536. // We accept a null to/from, as these can be
  537. // created automatically.
  538. if ( ! isset($notify) AND ! $notify) return NULL;
  539. if ( ! isset($template) AND ! $template) return NULL;
  540. // -------------------------------------
  541. // Get e-mails. Forget if there are none
  542. // -------------------------------------
  543. $emails = explode("|", $notify);
  544. if (empty($emails)) return NULL;
  545. foreach($emails as $key => $piece)
  546. {
  547. $emails[$key] = $this->_process_email_address($piece);
  548. }
  549. // -------------------------------------
  550. // Parse Email Template
  551. // -------------------------------------
  552. // Get the email template from
  553. // the database and create some
  554. // special vars to pass off.
  555. // -------------------------------------
  556. $layout = $this->CI->db
  557. ->limit(1)
  558. ->where('slug', $template)
  559. ->get('email_templates')
  560. ->row();
  561. if ( ! $layout) return null;
  562. // -------------------------------------
  563. // Get some basic sender data
  564. // -------------------------------------
  565. $this->CI->load->library('user_agent');
  566. $data = array(
  567. 'sender_ip' => $this->CI->input->ip_address(),
  568. 'sender_os' => $this->CI->agent->platform(),
  569. 'sender_agent' => $this->CI->agent->agent_string()
  570. );
  571. // -------------------------------------
  572. // Get the entry to pass to the template.
  573. // -------------------------------------
  574. $params = array(
  575. 'id' => $entry_id,
  576. 'stream' => $stream->stream_slug);
  577. $rows = $this->CI->row_m->get_rows($params, $this->CI->streams_m->get_stream_fields($stream->id), $stream);
  578. $data['entry'] = $rows['rows'];
  579. // -------------------------------------
  580. // Parse the body and subject
  581. // -------------------------------------
  582. $layout->body = html_entity_decode($this->CI->parser->parse_string(str_replace(array('&quot;', '&#39;'), array('"', "'"), $layout->body), $data, true));
  583. $layout->subject = html_entity_decode($this->CI->parser->parse_string(str_replace(array('&quot;', '&#39;'), array('"', "'"), $layout->subject), $data, true));
  584. // -------------------------------------
  585. // Set From
  586. // -------------------------------------
  587. // We accept an email address from or
  588. // a name/email separated by a pipe (|).
  589. // -------------------------------------
  590. $this->CI->load->library('Email');
  591. if (isset($from) AND $from)
  592. {
  593. $email_pieces = explode("|", $from);
  594. if (count($email_pieces) == 2)
  595. {
  596. $this->CI->email->from($this->_process_email_address($email_pieces[0]), $email_pieces[1]);
  597. }
  598. else
  599. {
  600. $this->CI->email->from($email_pieces[0]);
  601. }
  602. }
  603. else
  604. {
  605. // Hmm. No from address. We'll just use the site setting.
  606. $this->CI->email->from($this->CI->settings->get('server_email'), $this->CI->settings->get('site_name'));
  607. }
  608. // -------------------------------------
  609. // Set Email Data
  610. // -------------------------------------
  611. $this->CI->email->to($emails);
  612. $this->CI->email->subject($layout->subject);
  613. $this->CI->email->message($layout->body);
  614. // -------------------------------------
  615. // Send, Log & Clear
  616. // -------------------------------------
  617. $return = $this->CI->email->send();
  618. $this->CI->email->clear();
  619. return $return;
  620. }
  621. // --------------------------------------------------------------------------
  622. /**
  623. * Process an email address - if it is not
  624. * an email address, pull it from post data.
  625. *
  626. * @access private
  627. * @param email
  628. * @return string
  629. */
  630. private function _process_email_address($email)
  631. {
  632. if (strpos($email, '@') === FALSE AND $this->CI->input->post($email))
  633. {
  634. return $this->CI->input->post($email);
  635. }
  636. return $email;
  637. }
  638. }