PageRenderTime 31ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 1ms

/extensions/ext.fieldframe.php

https://github.com/pixelandtonic/fieldframe
PHP | 3181 lines | 2197 code | 341 blank | 643 comment | 238 complexity | ec07d647ec7facfa36ff39cf6d7c940c MD5 | raw file

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

  1. <?php
  2. if ( ! defined('EXT')) exit('Invalid file request');
  3. // define FF constants
  4. // (used by Fieldframe and Fieldframe_Main)
  5. if ( ! defined('FF_CLASS'))
  6. {
  7. define('FF_CLASS', 'Fieldframe');
  8. define('FF_NAME', 'FieldFrame');
  9. define('FF_VERSION', '1.4.5');
  10. }
  11. /**
  12. * FieldFrame Class
  13. *
  14. * This extension provides a framework for ExpressionEngine fieldtype development.
  15. *
  16. * @package FieldFrame
  17. * @author Brandon Kelly <brandon@pixelandtonic.com>
  18. * @copyright Copyright (c) 2011 Pixel & Tonic, Inc
  19. * @license http://creativecommons.org/licenses/by-sa/3.0/ Attribution-Share Alike 3.0 Unported
  20. */
  21. class Fieldframe {
  22. var $name = FF_NAME;
  23. var $version = FF_VERSION;
  24. var $description = 'Fieldtype Framework';
  25. var $settings_exist = 'y';
  26. var $docs_url = 'http://pixelandtonic.com/fieldframe/docs';
  27. var $hooks = array(
  28. 'sessions_start' => array('priority' => 1),
  29. // Edit Field Form
  30. 'publish_admin_edit_field_type_pulldown',
  31. 'publish_admin_edit_field_type_cellone',
  32. 'publish_admin_edit_field_type_celltwo',
  33. 'publish_admin_edit_field_extra_row',
  34. 'publish_admin_edit_field_format',
  35. 'publish_admin_edit_field_js',
  36. // Field Manager
  37. 'show_full_control_panel_start',
  38. 'show_full_control_panel_end',
  39. // Entry Form
  40. 'publish_form_field_unique',
  41. 'submit_new_entry_start',
  42. 'submit_new_entry_end',
  43. 'publish_form_start',
  44. // SAEF
  45. 'weblog_standalone_form_start',
  46. 'weblog_standalone_form_end',
  47. // Templates
  48. 'weblog_entries_tagdata' => array('priority' => 1),
  49. // LG Addon Updater
  50. 'lg_addon_update_register_source',
  51. 'lg_addon_update_register_addon'
  52. );
  53. /**
  54. * FieldFrame Class Constructor
  55. *
  56. * @param array $settings
  57. */
  58. function __construct($settings=array())
  59. {
  60. $this->_init_main($settings);
  61. }
  62. /**
  63. * Activate Extension
  64. */
  65. function activate_extension()
  66. {
  67. global $DB;
  68. // require PHP 5
  69. if (phpversion() < 5) return;
  70. // Get settings
  71. $query = $DB->query('SELECT settings FROM exp_extensions WHERE class = "'.FF_CLASS.'" AND settings != "" LIMIT 1');
  72. $settings = $query->num_rows ? $this->_unserialize($query->row['settings']) : array();
  73. $this->_init_main($settings, TRUE);
  74. global $FF;
  75. $FF->activate_extension($settings);
  76. }
  77. /**
  78. * Update Extension
  79. *
  80. * @param string $current Previous installed version of the extension
  81. */
  82. function update_extension($current='')
  83. {
  84. global $DB;
  85. if ( ! $current OR $current == FF_VERSION)
  86. {
  87. return FALSE;
  88. }
  89. if ($current < '1.1.3')
  90. {
  91. // no longer saving settings on a per-site basis
  92. $sql = array();
  93. // no more per-site FF settings
  94. $query = $DB->query('SELECT settings FROM exp_extensions WHERE class = "'.FF_CLASS.'" AND settings != "" LIMIT 1');
  95. if ($query->row)
  96. {
  97. $settings = array_shift(Fieldframe_Main::_unserialize($query->row['settings']));
  98. $sql[] = $DB->update_string('exp_extensions', array('settings' => Fieldframe_Main::_serialize($settings)), 'class = "'.FF_CLASS.'"');
  99. }
  100. // collect conversion info
  101. $query = $DB->query('SELECT * FROM exp_ff_fieldtypes ORDER BY enabled DESC, site_id ASC');
  102. $firsts = array();
  103. $conversions = array();
  104. foreach ($query->result as $ftype)
  105. {
  106. if ( ! isset($firsts[$ftype['class']]))
  107. {
  108. $firsts[$ftype['class']] = $ftype['fieldtype_id'];
  109. }
  110. else
  111. {
  112. $conversions[$ftype['fieldtype_id']] = $firsts[$ftype['class']];
  113. }
  114. }
  115. if ($conversions)
  116. {
  117. // remove duplicate ftype rows in exp_ff_fieldtypes
  118. $sql[] = 'DELETE FROM exp_ff_fieldtypes WHERE fieldtype_id IN ('.implode(',', array_keys($conversions)).')';
  119. // update field_type's in exp_weblog_fields
  120. foreach($conversions as $old_id => $new_id)
  121. {
  122. $sql[] = $DB->update_string('exp_weblog_fields', array('field_type' => 'ftype_id_'.$new_id), 'field_type = "ftype_id_'.$old_id.'"');
  123. }
  124. }
  125. // remove site_id column from exp_ff_fieldtypes
  126. $sql[] = 'ALTER TABLE exp_ff_fieldtypes DROP COLUMN site_id';
  127. // apply changes
  128. foreach($sql as $query)
  129. {
  130. $DB->query($query);
  131. }
  132. }
  133. if ($current < '1.1.0')
  134. {
  135. // hooks have changed, so go through
  136. // the whole activate_extension() process
  137. $this->activate_extension();
  138. }
  139. else
  140. {
  141. // just update the version #s
  142. $DB->query('UPDATE exp_extensions
  143. SET version = "'.FF_VERSION.'"
  144. WHERE class = "'.FF_CLASS.'"');
  145. }
  146. }
  147. /**
  148. * Disable Extension
  149. */
  150. function disable_extension()
  151. {
  152. global $DB;
  153. $DB->query($DB->update_string('exp_extensions', array('enabled' => 'n'), 'class = "'.FF_CLASS.'"'));
  154. }
  155. /**
  156. * Settings Form
  157. *
  158. * @param array $settings
  159. */
  160. function settings_form($settings=array())
  161. {
  162. $this->_init_main($settings);
  163. global $FF;
  164. $FF->settings_form();
  165. }
  166. function save_settings()
  167. {
  168. $this->_init_main(array(), TRUE);
  169. global $FF;
  170. $FF->save_settings();
  171. }
  172. /**
  173. * Initialize Main class
  174. *
  175. * @param array $settings
  176. * @access private
  177. */
  178. function _init_main($settings, $force=FALSE)
  179. {
  180. global $SESS, $FF;
  181. if ( ! isset($FF) OR $force)
  182. {
  183. $FF = new Fieldframe_Main($settings, $this->hooks);
  184. }
  185. }
  186. /**
  187. * __call Magic Method
  188. *
  189. * Routes calls to missing methods to $FF
  190. *
  191. * @param string $method Name of the missing method
  192. * @param array $args Arguments sent to the missing method
  193. */
  194. function __call($method, $args)
  195. {
  196. global $FF, $EXT;
  197. if (isset($FF))
  198. {
  199. if (method_exists($FF, $method))
  200. {
  201. return call_user_func_array(array(&$FF, $method), $args);
  202. }
  203. else if (substr($method, 0, 13) == 'forward_hook:')
  204. {
  205. $ext = explode(':', $method); // [ forward_hook, hook name, priority ]
  206. return call_user_func_array(array(&$FF, 'forward_hook'), array($ext[1], $ext[2], $args));
  207. }
  208. }
  209. return FALSE;
  210. }
  211. }
  212. /**
  213. * FieldFrame_Main Class
  214. *
  215. * Provides the core extension logic + hooks
  216. *
  217. * @package FieldFrame
  218. */
  219. class Fieldframe_Main {
  220. function log()
  221. {
  222. foreach(func_get_args() as $var)
  223. {
  224. if (is_string($var))
  225. {
  226. echo "<code style='display:block; margin:0; padding:5px 10px;'>{$var}</code>";
  227. }
  228. else
  229. {
  230. echo '<pre style="display:block; margin:0; padding:5px 10px; width:auto">';
  231. print_r($var);
  232. echo '</pre>';
  233. }
  234. }
  235. }
  236. var $errors = array();
  237. var $postponed_saves = array();
  238. var $snippets = array('head' => array(), 'body' => array());
  239. var $saef = FALSE;
  240. /**
  241. * FieldFrame_Main Class Initialization
  242. *
  243. * @param array $settings
  244. */
  245. function __construct($settings, $hooks)
  246. {
  247. global $SESS, $DB;
  248. $this->hooks = $hooks;
  249. // merge settings with defaults
  250. $default_settings = array(
  251. 'fieldtypes_url' => '',
  252. 'fieldtypes_path' => '',
  253. 'check_for_updates' => 'y'
  254. );
  255. $this->settings = array_merge($default_settings, $settings);
  256. // set the FT_PATH and FT_URL constants
  257. $this->_define_constants();
  258. }
  259. /**
  260. * Define Constants
  261. *
  262. * @access private
  263. */
  264. function _define_constants()
  265. {
  266. global $PREFS;
  267. if ( ! defined('FT_PATH') AND ($ft_path = isset($PREFS->core_ini['ft_path']) ? $PREFS->core_ini['ft_path'] : $this->settings['fieldtypes_path']))
  268. {
  269. define('FT_PATH', $ft_path);
  270. }
  271. if ( ! defined('FT_URL') AND ($ft_path = isset($PREFS->core_ini['ft_url']) ? $PREFS->core_ini['ft_url'] : $this->settings['fieldtypes_url']))
  272. {
  273. define('FT_URL', $ft_path);
  274. $this->snippets['body'][] = '<script type="text/javascript">;FT_URL = "'.FT_URL.'";</script>';
  275. }
  276. }
  277. /**
  278. * Array Ascii to Entities
  279. *
  280. * @access private
  281. */
  282. function _array_ascii_to_entities($vals)
  283. {
  284. if (is_array($vals))
  285. {
  286. foreach ($vals as &$val)
  287. {
  288. $val = $this->_array_ascii_to_entities($val);
  289. }
  290. }
  291. else if (! is_numeric($vals))
  292. {
  293. global $REGX;
  294. $vals = $REGX->ascii_to_entities($vals);
  295. }
  296. return $vals;
  297. }
  298. /**
  299. * Array Ascii to Entities
  300. *
  301. * @access private
  302. */
  303. function _array_entities_to_ascii($vals)
  304. {
  305. if (is_array($vals))
  306. {
  307. foreach ($vals as &$val)
  308. {
  309. $val = $this->_array_entities_to_ascii($val);
  310. }
  311. }
  312. else
  313. {
  314. global $REGX;
  315. $vals = $REGX->entities_to_ascii($vals);
  316. }
  317. return $vals;
  318. }
  319. /**
  320. * Serialize
  321. *
  322. * @access private
  323. */
  324. function _serialize($vals)
  325. {
  326. global $PREFS;
  327. if ($PREFS->ini('auto_convert_high_ascii') == 'y')
  328. {
  329. $vals = $this->_array_ascii_to_entities($vals);
  330. }
  331. return addslashes(serialize($vals));
  332. }
  333. /**
  334. * Unserialize
  335. *
  336. * @access private
  337. */
  338. function _unserialize($vals, $convert=TRUE)
  339. {
  340. global $REGX, $PREFS;
  341. if ($vals && is_string($vals) && preg_match('/^(i|s|a|o|d):(.*);/si', $vals) && ($tmp_vals = @unserialize($vals)) !== FALSE)
  342. {
  343. $vals = $REGX->array_stripslashes($tmp_vals);
  344. if ($convert AND $PREFS->ini('auto_convert_high_ascii') == 'y')
  345. {
  346. $vals = $this->_array_entities_to_ascii($vals);
  347. }
  348. }
  349. return $vals;
  350. }
  351. /**
  352. * Get Fieldtypes
  353. *
  354. * @return array All enabled FF fieldtypes, indexed by class name
  355. * @access private
  356. */
  357. function _get_ftypes()
  358. {
  359. global $DB;
  360. if ( ! isset($this->ftypes))
  361. {
  362. $this->ftypes = array();
  363. // get enabled fields from the DB
  364. $query = $DB->query('SELECT * FROM exp_ff_fieldtypes WHERE enabled = "y"');
  365. if ($query->num_rows)
  366. {
  367. foreach($query->result as $row)
  368. {
  369. if (($ftype = $this->_init_ftype($row)) !== FALSE)
  370. {
  371. $this->ftypes[] = $ftype;
  372. }
  373. }
  374. $this->_sort_ftypes($this->ftypes);
  375. }
  376. }
  377. return $this->ftypes;
  378. }
  379. function _get_ftype($class_name)
  380. {
  381. $ftypes = $this->_get_ftypes();
  382. return isset($ftypes[$class_name])
  383. ? $ftypes[$class_name]
  384. : FALSE;
  385. }
  386. /**
  387. * Get All Installed Fieldtypes
  388. *
  389. * @return array All installed FF fieldtypes, indexed by class name
  390. * @access private
  391. */
  392. function _get_all_installed_ftypes()
  393. {
  394. $ftypes = array();
  395. if (defined('FT_PATH'))
  396. {
  397. if ( $fp = @opendir(FT_PATH))
  398. {
  399. // iterate through the field folder contents
  400. while (($file = readdir($fp)) !== FALSE)
  401. {
  402. // skip hidden/navigational files
  403. if (substr($file, 0, 1) == '.') continue;
  404. // is this a directory, and does a ftype file exist inside it?
  405. if (is_dir(FT_PATH.$file) AND is_file(FT_PATH.$file.'/ft.'.$file.EXT))
  406. {
  407. if (($ftype = $this->_init_ftype($file, FALSE)) !== FALSE)
  408. {
  409. $ftypes[] = $ftype;
  410. }
  411. }
  412. }
  413. closedir($fp);
  414. $this->_sort_ftypes($ftypes);
  415. }
  416. else
  417. {
  418. $this->errors[] = 'bad_ft_path';
  419. }
  420. }
  421. return $ftypes;
  422. }
  423. /**
  424. * Sort Fieldtypes
  425. *
  426. * @param array $ftypes the array of fieldtypes
  427. * @access private
  428. */
  429. function _sort_ftypes(&$ftypes)
  430. {
  431. $ftypes_by_name = array();
  432. while($ftype = array_shift($ftypes))
  433. {
  434. $ftypes_by_name[$ftype->info['name']] = $ftype;
  435. }
  436. ksort($ftypes_by_name);
  437. foreach($ftypes_by_name as $ftype)
  438. {
  439. $ftypes[$ftype->_class_name] = $ftype;
  440. }
  441. }
  442. /**
  443. * Get Fieldtypes Indexed By Field ID
  444. *
  445. * @return array All enabled FF fieldtypes, indexed by the weblog field ID they're used in.
  446. * Strong possibility that there will be duplicate fieldtypes in here,
  447. * but it's not a big deal because they're just object references
  448. * @access private
  449. */
  450. function _get_fields()
  451. {
  452. global $DB, $REGX;
  453. if ( ! isset($this->ftypes_by_field_id))
  454. {
  455. $this->ftypes_by_field_id = array();
  456. // get the fieldtypes
  457. if ($ftypes = $this->_get_ftypes())
  458. {
  459. // index them by ID rather than class
  460. $ftypes_by_id = array();
  461. foreach($ftypes as $class_name => $ftype)
  462. {
  463. $ftypes_by_id[$ftype->_fieldtype_id] = $ftype;
  464. }
  465. // get the field info
  466. $query = $DB->query("SELECT field_id, site_id, field_name, field_type, ff_settings FROM exp_weblog_fields
  467. WHERE field_type IN ('ftype_id_".implode("', 'ftype_id_", array_keys($ftypes_by_id))."')");
  468. if ($query->num_rows)
  469. {
  470. // sort the current site's fields on top
  471. function sort_fields($a, $b)
  472. {
  473. global $PREFS;
  474. if ($a['site_id'] == $b['site_id']) return 0;
  475. if ($a['site_id'] == $PREFS->ini('site_id')) return 1;
  476. if ($b['site_id'] == $PREFS->ini('site_id')) return -1;
  477. return 0;
  478. }
  479. usort($query->result, 'sort_fields');
  480. foreach($query->result as $row)
  481. {
  482. $ftype_id = substr($row['field_type'], 9);
  483. $this->ftypes_by_field_id[$row['field_id']] = array(
  484. 'name' => $row['field_name'],
  485. 'ftype' => $ftypes_by_id[$ftype_id],
  486. 'settings' => array_merge(
  487. (isset($ftypes_by_id[$ftype_id]->default_field_settings) ? $ftypes_by_id[$ftype_id]->default_field_settings : array()),
  488. ($row['ff_settings'] ? (array)$this->_unserialize($row['ff_settings']) : array())
  489. )
  490. );
  491. }
  492. }
  493. }
  494. }
  495. return $this->ftypes_by_field_id;
  496. }
  497. /**
  498. * Initialize Fieldtype
  499. *
  500. * @param mixed $ftype fieldtype's class name or its row in exp_ff_fieldtypes
  501. * @return object Initialized fieldtype object
  502. * @access private
  503. */
  504. function _init_ftype($ftype, $req_strict=TRUE)
  505. {
  506. global $DB, $REGX, $LANG;
  507. $file = is_array($ftype) ? $ftype['class'] : $ftype;
  508. $class_name = ucfirst($file);
  509. if ( ! class_exists($class_name) && ! class_exists($class_name.'_ft'))
  510. {
  511. // import the file
  512. @include(FT_PATH.$file.'/ft.'.$file.EXT);
  513. // skip if the class doesn't exist
  514. if ( ! class_exists($class_name) && ! class_exists($class_name.'_ft'))
  515. {
  516. return FALSE;
  517. }
  518. }
  519. $_class_name = $class_name . (class_exists($class_name) ? '' : '_ft');
  520. $OBJ = new $_class_name();
  521. // is this a FieldFrame fieldtype?
  522. if ( ! isset($OBJ->_fieldframe)) return FALSE;
  523. $OBJ->_class_name = $file;
  524. $OBJ->_is_new = FALSE;
  525. $OBJ->_is_enabled = FALSE;
  526. $OBJ->_is_updated = FALSE;
  527. // settings
  528. $OBJ->site_settings = isset($OBJ->default_site_settings)
  529. ? $OBJ->default_site_settings
  530. : array();
  531. // info
  532. if ( ! isset($OBJ->info)) $OBJ->info = array();
  533. // requirements
  534. if ( ! isset($OBJ->_requires)) $OBJ->_requires = array();
  535. if (isset($OBJ->requires))
  536. {
  537. // PHP
  538. if (isset($OBJ->requires['php']) AND phpversion() < $OBJ->requires['php'])
  539. {
  540. if ($req_strict) return FALSE;
  541. else $OBJ->_requires['PHP'] = $OBJ->requires['php'];
  542. }
  543. // ExpressionEngine
  544. if (isset($OBJ->requires['ee']) AND APP_VER < $OBJ->requires['ee'])
  545. {
  546. if ($req_strict) return FALSE;
  547. else $OBJ->_requires['ExpressionEngine'] = $OBJ->requires['ee'];
  548. }
  549. // ExpressionEngine Build
  550. if (isset($OBJ->requires['ee_build']) AND APP_BUILD < $OBJ->requires['ee_build'])
  551. {
  552. if ($req_strict) return FALSE;
  553. else
  554. {
  555. $req = substr(strtolower($LANG->line('build')), 0, -1).' '.$OBJ->requires['ee_build'];
  556. if (isset($OBJ->_requires['ExpressionEngine'])) $OBJ->_requires['ExpressionEngine'] .= ' '.$req;
  557. else $OBJ->_requires['ExpressionEngine'] = $req;
  558. }
  559. }
  560. // FieldFrame
  561. if (isset($OBJ->requires['ff']) AND FF_VERSION < $OBJ->requires['ff'])
  562. {
  563. if ($req_strict) return FALSE;
  564. else $OBJ->_requires[FF_NAME] = $OBJ->requires['ff'];
  565. }
  566. // CP jQuery
  567. if (isset($OBJ->requires['cp_jquery']))
  568. {
  569. if ( ! isset($OBJ->requires['ext']))
  570. {
  571. $OBJ->requires['ext'] = array();
  572. }
  573. $OBJ->requires['ext'][] = array('class' => 'Cp_jquery', 'name' => 'CP jQuery', 'url' => 'http://www.ngenworks.com/software/ee/cp_jquery/', 'version' => $OBJ->requires['cp_jquery']);
  574. }
  575. // Extensions
  576. if (isset($OBJ->requires['ext']))
  577. {
  578. if ( ! isset($this->req_ext_versions))
  579. {
  580. $this->req_ext_versions = array();
  581. }
  582. foreach($OBJ->requires['ext'] as $ext)
  583. {
  584. if ( ! isset($this->req_ext_versions[$ext['class']]))
  585. {
  586. $ext_query = $DB->query('SELECT version FROM exp_extensions
  587. WHERE class="'.$ext['class'].'"'
  588. . (isset($ext['version']) ? ' AND version >= "'.$ext['version'].'"' : '')
  589. . ' AND enabled = "y"
  590. ORDER BY version DESC
  591. LIMIT 1');
  592. $this->req_ext_versions[$ext['class']] = $ext_query->row
  593. ? $ext_query->row['version']
  594. : '';
  595. }
  596. if ($this->req_ext_versions[$ext['class']] < $ext['version'])
  597. {
  598. if ($req_strict) return FALSE;
  599. else
  600. {
  601. $name = isset($ext['name']) ? $ext['name'] : ucfirst($ext['class']);
  602. if ($ext['url']) $name = '<a href="'.$ext['url'].'">'.$name.'</a>';
  603. $OBJ->_requires[$name] = $ext['version'];
  604. }
  605. }
  606. }
  607. }
  608. }
  609. if ( ! isset($OBJ->info['name'])) $OBJ->info['name'] = ucwords(str_replace('_', ' ', $class_name));
  610. if ( ! isset($OBJ->info['version'])) $OBJ->info['version'] = '';
  611. if ( ! isset($OBJ->info['desc'])) $OBJ->info['desc'] = '';
  612. if ( ! isset($OBJ->info['docs_url'])) $OBJ->info['docs_url'] = '';
  613. if ( ! isset($OBJ->info['author'])) $OBJ->info['author'] = '';
  614. if ( ! isset($OBJ->info['author_url'])) $OBJ->info['author_url'] = '';
  615. if ( ! isset($OBJ->info['versions_xml_url'])) $OBJ->info['versions_xml_url'] = '';
  616. if ( ! isset($OBJ->info['no_lang'])) $OBJ->info['no_lang'] = FALSE;
  617. if ( ! isset($OBJ->hooks)) $OBJ->hooks = array();
  618. if ( ! isset($OBJ->postpone_saves)) $OBJ->postpone_saves = FALSE;
  619. // do we already know about this fieldtype?
  620. if (is_string($ftype))
  621. {
  622. $query = $DB->query('SELECT * FROM exp_ff_fieldtypes WHERE class = "'.$file.'"');
  623. $ftype = $query->row;
  624. }
  625. if ($ftype)
  626. {
  627. $OBJ->_fieldtype_id = $ftype['fieldtype_id'];
  628. if ($ftype['enabled'] == 'y')
  629. {
  630. $OBJ->_is_enabled = TRUE;
  631. }
  632. if ($ftype['settings'])
  633. {
  634. if (is_array($ftype_settings = $this->_unserialize($ftype['settings'])))
  635. {
  636. $OBJ->site_settings = array_merge($OBJ->site_settings, $ftype_settings);
  637. }
  638. }
  639. // new version?
  640. if ($OBJ->info['version'] != $ftype['version'])
  641. {
  642. $OBJ->_is_updated = TRUE;
  643. // update exp_ff_fieldtypes
  644. $DB->query($DB->update_string('exp_ff_fieldtypes',
  645. array('version' => $OBJ->info['version']),
  646. 'fieldtype_id = "'.$ftype['fieldtype_id'].'"'));
  647. // update the hooks
  648. $this->_insert_ftype_hooks($OBJ);
  649. // call update()
  650. if (method_exists($OBJ, 'update'))
  651. {
  652. $OBJ->update($ftype['version']);
  653. }
  654. }
  655. }
  656. else
  657. {
  658. $OBJ->_is_new = TRUE;
  659. }
  660. return $OBJ;
  661. }
  662. /**
  663. * Insert Fieldtype Hooks
  664. *
  665. * @access private
  666. */
  667. function _insert_ftype_hooks($ftype)
  668. {
  669. global $DB, $FF;
  670. // remove any existing hooks from exp_ff_fieldtype_hooks
  671. $DB->query('DELETE FROM exp_ff_fieldtype_hooks
  672. WHERE class = "'.$ftype->_class_name.'"');
  673. // (re)insert the hooks
  674. if ($ftype->hooks)
  675. {
  676. foreach($ftype->hooks as $hook => $data)
  677. {
  678. if (is_string($data))
  679. {
  680. $hook = $data;
  681. $data = array();
  682. }
  683. // exp_ff_fieldtype_hooks
  684. $data = array_merge(array('method' => $hook, 'priority' => 10), $data, array('hook' => $hook, 'class' => $ftype->_class_name));
  685. $DB->query($DB->insert_string('exp_ff_fieldtype_hooks', $data));
  686. // exp_extensions
  687. $hooks_q = $DB->query('SELECT extension_id FROM exp_extensions WHERE class = "'.FF_CLASS.'" AND hook = "'.$hook.'" AND priority = "'.$data['priority'].'"');
  688. if ( ! $hooks_q->num_rows)
  689. {
  690. $ext_data = array('class' => FF_CLASS, 'method' => 'forward_hook:'.$hook.':'.$data['priority'], 'hook' => $hook, 'settings' => '', 'priority' => $data['priority'], 'version' => FF_VERSION, 'enabled' => 'y');
  691. $DB->query($DB->insert_string('exp_extensions', $ext_data));
  692. }
  693. }
  694. }
  695. // reset cached hooks array
  696. $this->_get_ftype_hooks(TRUE);
  697. }
  698. function _get_ftype_hooks($reset=FALSE)
  699. {
  700. global $DB;
  701. if ($reset OR ! isset($this->ftype_hooks))
  702. {
  703. $this->ftype_hooks = array();
  704. $hooks_q = $DB->query('SELECT * FROM exp_ff_fieldtype_hooks');
  705. foreach($hooks_q->result as $hook_r)
  706. {
  707. $this->ftype_hooks[$hook_r['hook']][$hook_r['priority']][$hook_r['class']] = $hook_r['method'];
  708. }
  709. }
  710. return $this->ftype_hooks;
  711. }
  712. /**
  713. * Group Inputs
  714. *
  715. * @param string $name_prefix The Fieldtype ID
  716. * @param string $settings The Fieldtype settings
  717. * @return string The modified settings
  718. * @access private
  719. */
  720. function _group_inputs($name_prefix, $settings)
  721. {
  722. return preg_replace('/(name=([\'\"]))([^\'"\[\]]+)([^\'"]*)(\2)/i', '$1'.$name_prefix.'[$3]$4$5', $settings);
  723. }
  724. /**
  725. * Group Fieldtype Inputs
  726. *
  727. * @param string $ftype_id The Fieldtype ID
  728. * @param string $settings The Fieldtype settings
  729. * @return string The modified settings
  730. * @access private
  731. */
  732. function _group_ftype_inputs($ftype_id, $settings)
  733. {
  734. return $this->_group_inputs('ftype['.$ftype_id.']', $settings);
  735. }
  736. /**
  737. * Activate Extension
  738. */
  739. function activate_extension($settings)
  740. {
  741. global $DB;
  742. // Delete old hooks
  743. $DB->query('DELETE FROM exp_extensions
  744. WHERE class = "'.FF_CLASS.'"');
  745. // Add new extensions
  746. $hook_tmpl = array(
  747. 'class' => FF_CLASS,
  748. 'settings' => $this->_serialize($settings),
  749. 'priority' => 10,
  750. 'version' => FF_VERSION,
  751. 'enabled' => 'y'
  752. );
  753. foreach($this->hooks as $hook => $data)
  754. {
  755. if (is_string($data))
  756. {
  757. $hook = $data;
  758. $data = array();
  759. }
  760. $data = array_merge($hook_tmpl, array('hook' => $hook, 'method' => $hook), $data);
  761. $DB->query($DB->insert_string('exp_extensions', $data));
  762. }
  763. // exp_ff_fieldtypes
  764. if ( ! $DB->table_exists('exp_ff_fieldtypes'))
  765. {
  766. $DB->query("CREATE TABLE exp_ff_fieldtypes (
  767. `fieldtype_id` int(10) unsigned NOT NULL auto_increment,
  768. `class` varchar(50) NOT NULL default '',
  769. `version` varchar(10) NOT NULL default '',
  770. `settings` text NOT NULL default '',
  771. `enabled` char(1) NOT NULL default 'n',
  772. PRIMARY KEY (`fieldtype_id`)
  773. )");
  774. }
  775. // exp_ff_fieldtype_hooks
  776. if ( ! $DB->table_exists('exp_ff_fieldtype_hooks'))
  777. {
  778. $DB->query("CREATE TABLE exp_ff_fieldtype_hooks (
  779. `hook_id` int(10) unsigned NOT NULL auto_increment,
  780. `class` varchar(50) NOT NULL default '',
  781. `hook` varchar(50) NOT NULL default '',
  782. `method` varchar(50) NOT NULL default '',
  783. `priority` int(2) NOT NULL DEFAULT '10',
  784. PRIMARY KEY (`hook_id`)
  785. )");
  786. }
  787. // exp_weblog_fields.ff_settings
  788. $query = $DB->query('SHOW COLUMNS FROM `'.$DB->prefix.'weblog_fields` LIKE "ff_settings"');
  789. if ( ! $query->num_rows)
  790. {
  791. $DB->query('ALTER TABLE `'.$DB->prefix.'weblog_fields` ADD COLUMN `ff_settings` text NOT NULL');
  792. }
  793. // reset all ftype hooks
  794. foreach($this->_get_ftypes() as $class_name => $ftype)
  795. {
  796. $this->_insert_ftype_hooks($ftype);
  797. }
  798. }
  799. /**
  800. * Settings Form
  801. *
  802. * @param array $current Current extension settings (not site-specific)
  803. * @see http://expressionengine.com/docs/development/extensions.html#settings
  804. */
  805. function settings_form()
  806. {
  807. global $DB, $DSP, $LANG, $IN, $SD, $PREFS;
  808. // Breadcrumbs
  809. $DSP->crumbline = TRUE;
  810. $DSP->title = $LANG->line('extension_settings');
  811. $DSP->crumb = $DSP->anchor(BASE.AMP.'C=admin'.AMP.'area=utilities', $LANG->line('utilities'))
  812. . $DSP->crumb_item($DSP->anchor(BASE.AMP.'C=admin'.AMP.'M=utilities'.AMP.'P=extensions_manager', $LANG->line('extensions_manager')))
  813. . $DSP->crumb_item(FF_NAME);
  814. $DSP->right_crumb($LANG->line('disable_extension'), BASE.AMP.'C=admin'.AMP.'M=utilities'.AMP.'P=toggle_extension_confirm'.AMP.'which=disable'.AMP.'name='.$IN->GBL('name'));
  815. // Donations button
  816. $DSP->body .= '<div class="donations">'
  817. . '<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2181794" target="_blank">'
  818. . $LANG->line('donate')
  819. . '</a>'
  820. . '</div>';
  821. // open form
  822. $DSP->body .= '<h1>'.FF_NAME.' <small>'.FF_VERSION.'</small></h1>'
  823. . $DSP->form_open(
  824. array(
  825. 'action' => 'C=admin'.AMP.'M=utilities'.AMP.'P=save_extension_settings',
  826. 'name' => 'settings_subtext',
  827. 'id' => 'ffsettings'
  828. ),
  829. array(
  830. 'name' => strtolower(FF_CLASS)
  831. ));
  832. // initialize Fieldframe_SettingsDisplay
  833. $SD = new Fieldframe_SettingsDisplay();
  834. // import lang files
  835. $LANG->fetch_language_file('publish_ad');
  836. // fieldtypes folder
  837. $DSP->body .= $SD->block('fieldtypes_folder_title')
  838. . $SD->info_row('fieldtypes_folder_info')
  839. . $SD->row(array(
  840. $SD->label('fieldtypes_path_label', 'fieldtypes_path_subtext'),
  841. $SD->text('fieldtypes_path',
  842. (isset($PREFS->core_ini['ft_path']) ? $PREFS->core_ini['ft_path'] : $this->settings['fieldtypes_path']),
  843. array('extras' => (isset($PREFS->core_ini['ft_path']) ? ' disabled="disabled" ' : '')))
  844. ))
  845. . $SD->row(array(
  846. $SD->label('fieldtypes_url_label', 'fieldtypes_url_subtext'),
  847. $SD->text('fieldtypes_url',
  848. (isset($PREFS->core_ini['ft_url']) ? $PREFS->core_ini['ft_url'] : $this->settings['fieldtypes_url']),
  849. array('extras' => (isset($PREFS->core_ini['ft_url']) ? ' disabled="disabled" ' : '')))
  850. ))
  851. . $SD->block_c();
  852. // Check for Updates
  853. $lgau_query = $DB->query("SELECT class FROM exp_extensions
  854. WHERE class = 'Lg_addon_updater_ext' AND enabled = 'y' LIMIT 1");
  855. $lgau_enabled = $lgau_query->num_rows ? TRUE : FALSE;
  856. $DSP->body .= $SD->block('check_for_updates_title')
  857. . $SD->info_row('check_for_updates_info')
  858. . $SD->row(array(
  859. $SD->label('check_for_updates_label', 'check_for_updates_subtext'),
  860. $SD->radio_group('check_for_updates', (($this->settings['check_for_updates'] != 'n') ? 'y' : 'n'), array('y'=>'yes', 'n'=>'no'))
  861. ))
  862. . $SD->block_c();
  863. // Fieldtypes Manager
  864. $this->fieldtypes_manager(FALSE, $SD);
  865. // Close form
  866. $DSP->body .= $DSP->qdiv('itemWrapperTop', $DSP->input_submit())
  867. . $DSP->form_c();
  868. ob_start();
  869. ?>
  870. <style type="text/css" charset="utf-8">
  871. .donations { float:right; }
  872. .donations a { display:block; margin:-2px 10px 0 0; padding:5px 0 5px 67px; width:193px; height:15px; font-size:12px; line-height:15px; background:url(http://brandon-kelly.com/images/shared/donations.png) no-repeat 0 0; color:#000; font-weight:bold; }
  873. h1 { padding:7px 0; }
  874. </style>
  875. <?php
  876. $this->snippets['head'][] = ob_get_contents();
  877. ob_end_clean();
  878. }
  879. /**
  880. * Add Slash to URL/Path
  881. *
  882. * @param string $path The user-submitted path
  883. * @return string $path with a slash at the end
  884. * @access private
  885. */
  886. function _add_slash($path)
  887. {
  888. if (substr($path, -1) != '/')
  889. {
  890. $path .= '/';
  891. }
  892. return $path;
  893. }
  894. /**
  895. * Save Settings
  896. */
  897. function save_settings()
  898. {
  899. global $DB;
  900. // save new FF site settings
  901. $this->settings = array(
  902. 'fieldtypes_url' => ((isset($_POST['fieldtypes_url']) AND $_POST['fieldtypes_url']) ? $this->_add_slash($_POST['fieldtypes_url']) : ''),
  903. 'fieldtypes_path' => ((isset($_POST['fieldtypes_path']) AND $_POST['fieldtypes_path']) ? $this->_add_slash($_POST['fieldtypes_path']) : ''),
  904. 'check_for_updates' => ($_POST['check_for_updates'] != 'n' ? 'y' : 'n')
  905. );
  906. $DB->query($DB->update_string('exp_extensions', array('settings' => $this->_serialize($this->settings)), 'class = "'.FF_CLASS.'"'));
  907. // set the FT_PATH and FT_URL constants
  908. $this->_define_constants();
  909. // save Fieldtypes Manager data
  910. $this->save_fieldtypes_manager();
  911. }
  912. /**
  913. * Fieldtypes Manager
  914. */
  915. function fieldtypes_manager($standalone=TRUE, $SD=NULL)
  916. {
  917. global $DB, $DSP, $LANG, $IN, $PREFS, $SD;
  918. // are they allowed to access this?
  919. if (! $DSP->allowed_group('can_admin_utilities'))
  920. {
  921. return $DSP->no_access_message();
  922. }
  923. if ( ! $SD)
  924. {
  925. // initialize Fieldframe_SettingsDisplay
  926. $SD = new Fieldframe_SettingsDisplay();
  927. }
  928. if ($standalone)
  929. {
  930. // save submitted settings
  931. if ($this->save_fieldtypes_manager())
  932. {
  933. $DSP->body .= $DSP->qdiv('box', $DSP->qdiv('success', $LANG->line('settings_update')));
  934. }
  935. // load language file
  936. $LANG->fetch_language_file('fieldframe');
  937. // Breadcrumbs
  938. $DSP->crumbline = TRUE;
  939. $DSP->title = $LANG->line('fieldtypes_manager');
  940. $DSP->crumb = $DSP->anchor(BASE.AMP.'C=admin'.AMP.'area=utilities', $LANG->line('utilities'))
  941. . $DSP->crumb_item($LANG->line('fieldtypes_manager'));
  942. // open form
  943. $DSP->body .= $DSP->form_open(
  944. array(
  945. 'action' => 'C=admin'.AMP.'M=utilities'.AMP.'P=fieldtypes_manager',
  946. 'name' => 'settings_subtext',
  947. 'id' => 'ffsettings'
  948. ),
  949. array(
  950. 'name' => strtolower(FF_CLASS)
  951. ));
  952. }
  953. // fieldtype settings
  954. $DSP->body .= $SD->block('fieldtypes_manager', 5);
  955. // initialize fieldtypes
  956. if ($ftypes = $this->_get_all_installed_ftypes())
  957. {
  958. // add the headers
  959. $DSP->body .= $SD->heading_row(array(
  960. $LANG->line('fieldtype'),
  961. $LANG->line('fieldtype_enabled'),
  962. $LANG->line('settings'),
  963. $LANG->line('documentation')
  964. ));
  965. $row_ids = array();
  966. foreach($ftypes as $class_name => $ftype)
  967. {
  968. $row_id = 'ft_'.$ftype->_class_name;
  969. $row_ids[] = '"'.$row_id.'"';
  970. if (method_exists($ftype, 'display_site_settings'))
  971. {
  972. if ( ! $ftype->info['no_lang']) $LANG->fetch_language_file($class_name);
  973. $site_settings = $ftype->display_site_settings();
  974. }
  975. else
  976. {
  977. $site_settings = FALSE;
  978. }
  979. $DSP->body .= $SD->row(array(
  980. $SD->label($ftype->info['name'].NBS.$DSP->qspan('xhtmlWrapperLight defaultSmall', $ftype->info['version']), $ftype->info['desc']),
  981. ($ftype->_requires
  982. ? '--'
  983. : $SD->radio_group('ftypes['.$class_name.'][enabled]', ($ftype->_is_enabled ? 'y' : 'n'), array('y'=>'yes', 'n'=>'no'))),
  984. ($site_settings
  985. ? '<a class="toggle show" id="'.$row_id.'_show" href="#ft_'.$class_name.'_settings"><img src="'.$PREFS->ini('theme_folder_url', 1).'cp_global_images/expand.gif" border="0">  '.$LANG->line('show').'</a>'
  986. . '<a class="toggle hide" id="'.$row_id.'_hide"><img src="'.$PREFS->ini('theme_folder_url', 1).'cp_global_images/collapse.gif" border="0">  '.$LANG->line('hide').'</a>'
  987. : '--'),
  988. ($ftype->info['docs_url'] ? '<a href="'.stripslashes($ftype->info['docs_url']).'">'.$LANG->line('documentation').'</a>' : '--')
  989. ), NULL, array('id' => $row_id));
  990. if ($ftype->_requires)
  991. {
  992. $data = '<p>'.$ftype->info['name'].' '.$LANG->line('requires').':</p>'
  993. . '<ul>';
  994. foreach($ftype->_requires as $dependency => $version)
  995. {
  996. $data .= '<li class="default">'.$dependency.' '.$version.' '.$LANG->line('or_later').'</li>';
  997. }
  998. $data .= '</ul>';
  999. $DSP->body .= $SD->row(array('', $data), $SD->row_class);
  1000. }
  1001. else if ($site_settings)
  1002. {
  1003. $data = '<div class="ftsettings">'
  1004. . $this->_group_ftype_inputs($ftype->_class_name, $site_settings)
  1005. . $DSP->div_c();
  1006. $DSP->body .= $SD->row(array($data), '', array('id' => $row_id.'_settings', 'style' => 'display:none;'));
  1007. }
  1008. }
  1009. ob_start();
  1010. ?>
  1011. <script type="text/javascript" charset="utf-8">
  1012. ;var urlParts = document.location.href.split('#'),
  1013. anchor = urlParts[1];
  1014. function ffEnable(ft) {
  1015. ft.show.className = "toggle show";
  1016. ft.show.onclick = function() {
  1017. ft.show.style.display = "none";
  1018. ft.hide.style.display = "block";
  1019. ft.settings.style.display = "table-row";
  1020. };
  1021. ft.hide.onclick = function() {
  1022. ft.show.style.display = "block";
  1023. ft.hide.style.display = "none";
  1024. ft.settings.style.display = "none";
  1025. };
  1026. }
  1027. function ffDisable(ft) {
  1028. ft.show.className = "toggle show disabled";
  1029. ft.show.onclick = null;
  1030. ft.show.style.display = "block";
  1031. ft.hide.style.display = "none";
  1032. ft.settings.style.display = "none";
  1033. }
  1034. function ffInitRow(rowId) {
  1035. var ft = {
  1036. tr: document.getElementById(rowId),
  1037. show: document.getElementById(rowId+"_show"),
  1038. hide: document.getElementById(rowId+"_hide"),
  1039. settings: document.getElementById(rowId+"_settings")
  1040. };
  1041. if (ft.settings) {
  1042. ft.toggles = ft.tr.getElementsByTagName("input");
  1043. ft.toggles[0].onchange = function() { ffEnable(ft); };
  1044. ft.toggles[1].onchange = function() { ffDisable(ft); };
  1045. if (ft.toggles[0].checked) ffEnable(ft);
  1046. else ffDisable(ft);
  1047. if (anchor == rowId+'_settings') {
  1048. ft.show.onclick();
  1049. }
  1050. }
  1051. }
  1052. var ffRowIds = [<?php echo implode(',', $row_ids) ?>];
  1053. for (var i = 0; i < ffRowIds.length; i++) {
  1054. ffInitRow(ffRowIds[i]);
  1055. }
  1056. </script>
  1057. <?php
  1058. $this->snippets['body'][] = ob_get_contents();
  1059. ob_end_clean();
  1060. }
  1061. else if ( ! defined('FT_PATH'))
  1062. {
  1063. $DSP->body .= $SD->info_row('no_fieldtypes_path');
  1064. }
  1065. else if (in_array('bad_ft_path', $this->errors))
  1066. {
  1067. $DSP->body .= $SD->info_row('bad_fieldtypes_path');
  1068. }
  1069. else
  1070. {
  1071. $DSP->body .= $SD->info_row('no_fieldtypes');
  1072. }
  1073. $DSP->body .= $SD->block_c();
  1074. if ($standalone)
  1075. {
  1076. // Close form
  1077. $DSP->body .= $DSP->qdiv('itemWrapperTop', $DSP->input_submit($LANG->line('apply')))
  1078. . $DSP->form_c();
  1079. }
  1080. ob_start();
  1081. ?>
  1082. <style type="text/css" charset="utf-8">
  1083. #ffsettings a.toggle { display:block; cursor:pointer; }
  1084. #ffsettings a.toggle.hide { display:none; }
  1085. #ffsettings a.toggle.disabled { color:#000; opacity:0.4; cursor:default; }
  1086. #ffsettings .ftsettings { margin:-3px -1px -1px; }
  1087. #ffsettings .ftsettings, #ffsettings .ftsettings .tableCellOne, #ffsettings .ftsettings .tableCellTwo, #ffsettings .ftsettings .box { background:#262e33; color:#999; }
  1088. #ffsettings .ftsettings .box { margin: 0; padding: 10px 15px; border: none; border-top: 1px solid #283036; background: -webkit-gradient(linear, 0 0, 0 100%, from(#262e33), to(#22292e)); }
  1089. #ffsettings .ftsettings table tr td .box { margin: -1px -8px; }
  1090. #ffsettings .ftsettings a, #ffsettings .ftsettings p, #ffsettings .ftsettings .subtext { color: #999; }
  1091. #ffsettings .ftsettings input.input, #ffsettings .ftsettings textarea { background:#fff; color:#333; }
  1092. #ffsettings .ftsettings table { border:none; }
  1093. #ffsettings .ftsettings table tr td { border-top:1px solid #1d2326; padding-left:8px; padding-right:8px; }
  1094. #ffsettings .ftsettings table tr td.tableHeading { color:#ddd; background:#232a2e; }
  1095. #ffsettings .ftsettings .defaultBold { color:#ccc; }
  1096. #ffsettings .ftsettings .tableCellOne, #ffsettings .ftsettings .tableCellOneBold, #ffsettings .ftsettings .tableCellTwo, #ffsettings .ftsettings .tableCellTwoBold { border-bottom:none; }
  1097. </style>
  1098. <?php
  1099. $this->snippets['head'][] = ob_get_contents();
  1100. ob_end_clean();
  1101. }
  1102. /**
  1103. * Save Fieldtypes Manager Settings
  1104. */
  1105. function save_fieldtypes_manager()
  1106. {
  1107. global $DB;
  1108. // fieldtype settings
  1109. if (isset($_POST['ftypes']))
  1110. {
  1111. foreach($_POST['ftypes'] as $file => $ftype_post)
  1112. {
  1113. // Initialize
  1114. if (($ftype = $this->_init_ftype($file)) !== FALSE)
  1115. {
  1116. $enabled = ($ftype_post['enabled'] == 'y');
  1117. // skip if new and not enabled yet
  1118. if ( ! $enabled AND $ftype->_is_new) continue;
  1119. // insert a new row if it's new
  1120. if ($enabled AND $ftype->_is_new)
  1121. {
  1122. $DB->query($DB->insert_string('exp_ff_fieldtypes', array(
  1123. 'class' => $file,
  1124. 'version' => $ftype->info['version']
  1125. )));
  1126. // get the fieldtype_id
  1127. $query = $DB->query('SELECT fieldtype_id FROM exp_ff_fieldtypes WHERE class = "'.$file.'"');
  1128. $ftype->_fieldtype_id = $query->row['fieldtype_id'];
  1129. // insert hooks
  1130. $this->_insert_ftype_hooks($ftype);
  1131. // call update()
  1132. if (method_exists($ftype, 'update'))
  1133. {
  1134. $ftype->update(FALSE);
  1135. }
  1136. }
  1137. $data = array(
  1138. 'enabled' => ($enabled ? 'y' : 'n')
  1139. );
  1140. if ($enabled)
  1141. {
  1142. $settings = (isset($_POST['ftype']) AND isset($_POST['ftype'][$ftype->_class_name]))
  1143. ? $_POST['ftype'][$ftype->_class_name]
  1144. : array();
  1145. // let the fieldtype do what it wants with them
  1146. if (method_exists($ftype, 'save_site_settings'))
  1147. {
  1148. $settings = $ftype->save_site_settings($settings);
  1149. if ( ! is_array($settings)) $settings = array();
  1150. }
  1151. $data['settings'] = $this->_serialize($settings);
  1152. }
  1153. $DB->query($DB->update_string('exp_ff_fieldtypes', $data, 'fieldtype_id = "'.$ftype->_fieldtype_id.'"'));
  1154. }
  1155. }
  1156. return TRUE;
  1157. }
  1158. return FALSE;
  1159. }
  1160. /**
  1161. * Get Last Call
  1162. *
  1163. * @param mixed $param Parameter sent by extension hook
  1164. * @return mixed Return value of last extension call if any, or $param
  1165. * @access private
  1166. */
  1167. function get_last_call($param=FALSE)
  1168. {
  1169. global $EXT;
  1170. return isset($this->_last_call)
  1171. ? $this->_last_call
  1172. : ($EXT->last_call !== FALSE ? $EXT->last_call : $param);
  1173. }
  1174. /**
  1175. * Forward hook to fieldtype
  1176. */
  1177. function forward_hook($hook, $priority, $args=array())
  1178. {
  1179. $ftype_hooks = $this->_get_ftype_hooks();
  1180. if (isset($ftype_hooks[$hook]) AND isset($ftype_hooks[$hook][$priority]))
  1181. {
  1182. $ftypes = $this->_get_ftypes();
  1183. foreach ($ftype_hooks[$hook][$priority] as $class_name => $method)
  1184. {
  1185. if (isset($ftypes[$class_name]) AND method_exists($ftypes[$class_name], $method))
  1186. {
  1187. $this->_last_call = call_user_func_array(array(&$ftypes[$class_name], $method), $args);
  1188. }
  1189. }
  1190. }
  1191. if (isset($this->_last_call))
  1192. {
  1193. $r = $this->_last_call;
  1194. unset($this->_last_call);
  1195. }
  1196. else
  1197. {
  1198. $r = $this->get_last_call();
  1199. }
  1200. return $r;
  1201. }
  1202. function forward_ff_hook($hook, $args=array(), $r=TRUE)
  1203. {
  1204. $this->_last_call = $r;
  1205. // ------------ These braces brought to you by Leevi Graham ------------
  1206. // ↓ ↓
  1207. $priority = (isset($this->hooks[$hook]) AND isset($this->hooks[$hook]['priority']))
  1208. ? $this->hooks[$hook]['priority']
  1209. : 10;
  1210. return $this->forward_hook($hook, $priority, $args);
  1211. }
  1212. /**
  1213. * Get Line
  1214. *
  1215. * @param string $line unlocalized string or the name of a $LANG line
  1216. * @return string Localized string
  1217. */
  1218. function get_line($line)
  1219. {
  1220. global $LANG;
  1221. $loc_line = $LANG->line($line);
  1222. return $loc_line ? $loc_line : $line;
  1223. }
  1224. /**
  1225. * Sessions Start
  1226. *
  1227. * - Reset any session class variable
  1228. * - Override the whole session check
  1229. * - Modify default/guest settings
  1230. *
  1231. * @param object $this The current instantiated Session class with all of its variables and functions,
  1232. * use a reference in your functions to modify.
  1233. * @see http://expressionengine.com/developers/extension_hooks/sessions_start/
  1234. */
  1235. function sessions_start($sess)
  1236. {
  1237. global $IN;
  1238. // are we saving a field?
  1239. if($IN->GBL('M', 'GET') == 'blog_admin' AND $IN->GBL('P', 'GET') == 'update_weblog_fields' AND isset($_POST['field_type']))
  1240. {
  1241. $this->publish_admin_edit_field_save();
  1242. }
  1243. $args = func_get_args();
  1244. return $this->forward_ff_hook('sessions_start', $args);
  1245. }
  1246. /**
  1247. * Publish Admin - Edit Field Form - Fieldtype Menu
  1248. *
  1249. * Allows modifying or adding onto Custom Weblog Fieldtype Pulldown
  1250. *
  1251. * @param array $data The data about this field from the database
  1252. * @param string $typemenu The contents of the type menu
  1253. * @return string The modified $typemenu
  1254. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_type_pulldown/
  1255. */
  1256. function publish_admin_edit_field_type_pulldown($data, $typemenu)
  1257. {
  1258. global $DSP;
  1259. $r = $this->get_last_call($typemenu);
  1260. foreach($this->_get_ftypes() as $class_name => $ftype)
  1261. {
  1262. // only list normal fieldtypes
  1263. if (method_exists($ftype, 'display_field'))
  1264. {
  1265. $field_type = 'ftype_id_'.$ftype->_fieldtype_id;
  1266. $r .= $DSP->input_select_option($field_type, $ftype->info['name'], ($data['field_type'] == $field_type ? 1 : 0));
  1267. }
  1268. }
  1269. $args = func_get_args();
  1270. return $this->forward_ff_hook('publish_admin_edit_field_type_pulldown', $args, $r);
  1271. }
  1272. /**
  1273. * Publish Admin - Edit Field Form - Javascript
  1274. *
  1275. * Allows modifying or adding onto Custom Weblog Field JS
  1276. *
  1277. * @param array $data The data about this field from the database
  1278. * @param string $js Currently existing javascript
  1279. * @return string The modified $js
  1280. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_js/
  1281. */
  1282. function publish_admin_edit_field_js($data, $js)
  1283. {
  1284. global $LANG, $REGX;
  1285. // Fetch the FF lang file
  1286. $LANG->fetch_language_file('fieldframe');
  1287. // Prepare fieldtypes for following Publish Admin hooks
  1288. $field_settings_tmpl = array(
  1289. 'cell1' => '',
  1290. 'cell2' => '',
  1291. 'rows' => array(),
  1292. 'formatting_available' => FALSE,
  1293. 'direction_available' => FALSE
  1294. );
  1295. $formatting_available = array();
  1296. $direction_available = array();
  1297. $prev_ftype_id = '';
  1298. $this->data = $data;
  1299. foreach($this->_get_ftypes() as $class_name => $ftype)
  1300. {
  1301. $ftype_id = 'ftype_id_'.$ftype->_fieldtype_id;
  1302. $selected = ($ftype_id == $this->data['field_type']) ? TRUE : FALSE;
  1303. if (method_exists($ftype, 'display_field_settings'))
  1304. {
  1305. // Load the language file
  1306. if ( ! $ftype->info['no_lang']) $LANG->fetch_language_file($class_name);
  1307. $field_settings = array_merge(
  1308. (isset($ftype->default_field_settings) ? $ftype->default_field_settings : array()),
  1309. ($selected ? $this->_unserialize($this->data['ff_settings']) : array())
  1310. );
  1311. $ftype->_field_settings = array_merge($field_settings_tmpl, $ftype->display_field_settings($field_settings));
  1312. }
  1313. else
  1314. {
  1315. $ftype->_field_settings = $field_settings_tmpl;
  1316. }
  1317. if ($ftype->_field_settings['formatting_available']) $formatting_available[] = $ftype->_fieldtype_id;
  1318. if ($ftype->_field_settings['direction_available']) $direction_available[] = $ftype->_fieldtype_id;
  1319. if ($selected) $prev_ftype_id = $ftype_id;
  1320. }
  1321. unset($this->data);
  1322. // Add the JS
  1323. ob_start();
  1324. ?>
  1325. var prev_ftype_id = '<?php echo $prev_ftype_id ?>';
  1326. $1
  1327. if (prev_ftype_id)
  1328. {
  1329. var c=1, r=1;
  1330. while(cell = document.getElementById(prev_ftype_id+'_cell'+c))
  1331. {
  1332. cell.style.display = 'none';
  1333. c++;
  1334. }
  1335. while(row = document.getElementById(prev_ftype_id+'_row'+r))
  1336. {
  1337. row.style.display = 'none';
  1338. r++;
  1339. }
  1340. }
  1341. if (id.match(/^ftype_id_\d+$/))
  1342. {
  1343. var c=1, r=1;
  1344. // show cells
  1345. while(cell = document.getElementById(id+'_cell'+c))
  1346. {
  1347. //var showDiv = document.getElementById(id+'_cell'+c);
  1348. var divs = cell.parentNode.childNodes;
  1349. for(var i=0; i < divs.length; i++)
  1350. {
  1351. var div = divs[i];
  1352. if ( ! (div.nodeType == 1 && div.id)) continue;
  1353. div.style.display = (div == cell) ? 'block' : 'none';
  1354. }
  1355. c++;
  1356. }
  1357. // show rows
  1358. while(row = document.getElementById(id+'_row'+r))
  1359. {
  1360. row.style.display = 'table-row';
  1361. r++;
  1362. }
  1363. // show/hide formatting
  1364. if ([<?php echo implode(',', $formatting_available) ?>].indexOf(parseInt(id.substring(9))) != -1)
  1365. {
  1366. document.getElementById('formatting_block').style.display = 'block';
  1367. document.getElementById('formatting_unavailable').style.display = 'none';
  1368. }
  1369. else
  1370. {
  1371. document.getElementById('formatting_block').style.display = 'none';
  1372. document.getElementById('formatting_unavailable').style.display = 'block';
  1373. }
  1374. // show/hide direction
  1375. if ([<?php echo implode(',', $direction_available) ?>].indexOf(parseInt(id.substring(9))) != -1)
  1376. {
  1377. document.getElementById('direction_available').style.display = 'block';
  1378. document.getElementById('direction_unavailable').style.display = 'none';
  1379. }
  1380. else
  1381. {
  1382. document.getElementById('direction_available').style.display = 'none';
  1383. document.getElementById('direction_unavailable').style.display = 'block';
  1384. }
  1385. prev_ftype_id = id;
  1386. }
  1387. <?php
  1388. $r = $this->get_last_call($js);
  1389. $r = preg_replace('/(function\s+showhide_element\(\s*id\s*\)\s*{)/is', ob_get_contents(), $r);
  1390. ob_end_clean();
  1391. $args = func_get_args();
  1392. return $this->forward_ff_hook('publish_admin_edit_field_js', $args, $r);
  1393. }
  1394. /**
  1395. * Publish Admin - Edit Field Form - Cell
  1396. *
  1397. * @param array $data The data about this field from the database
  1398. * @param string $cell The contents of the cell
  1399. * @param string $index The cell index
  1400. * @return string The modified $cell
  1401. * @access private
  1402. */
  1403. function _publish_admin_edit_field_type_cell($data, $cell, $index)
  1404. {
  1405. $r = $this->get_last_call($cell);
  1406. foreach($this->_get_ftypes() as $class_name => $ftype)
  1407. {
  1408. $ftype_id = 'ftype_id_'.$ftype->_fieldtype_id;
  1409. $selected = ($data['field_type'] == $ftype_id);
  1410. $field_settings = $this->_group_ftype_inputs($ftype_id, $ftype->_field_settings['cell'.$index]);
  1411. $r .= '<div id="'.$ftype_id.'_cell'.$index.'" style="margin-top:5px; display:'.($selected ? 'block' : 'none').';">'
  1412. . $field_settings
  1413. . '</div>';
  1414. }
  1415. return $r;
  1416. }
  1417. /**
  1418. * Publish Admin - Edit Field Form - Cell One
  1419. *
  1420. * Allows modifying or adding onto Custom Weblog Fieldtype - First Table Cell
  1421. *
  1422. * @param array $data The data about this field from the database
  1423. * @param string $cell The contents of the cell
  1424. * @return string The modified $cell
  1425. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_type_cellone/
  1426. */
  1427. function publish_admin_edit_field_type_cellone($data, $cell)
  1428. {
  1429. global $DSP;
  1430. $r = $this->_publish_admin_edit_field_type_cell($data, $cell, '1');
  1431. // formatting
  1432. foreach($this->_get_ftypes() as $class_name => $ftype)
  1433. {
  1434. $ftype_id = 'ftype_id_'.$ftype->_fieldtype_id;
  1435. $r .= $DSP->input_hidden('ftype['.$ftype_id.'][formatting_available]', ($ftype->_field_settings['formatting_available'] ? 'y' : 'n'));
  1436. }
  1437. $args = func_get_args();
  1438. return $this->forward_ff_hook('publish_admin_edit_field_type_cellone', $args, $r);
  1439. }
  1440. /**
  1441. * Publish Admin - Edit Field Form - Cell Two
  1442. *
  1443. * Allows modifying or adding onto Custom Weblog Fieldtype - Second Table Cell
  1444. *
  1445. * @param array $data The data about this field from the database
  1446. * @param string $cell The contents of the cell
  1447. * @return string The modified $cell
  1448. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_type_celltwo/
  1449. */
  1450. function publish_admin_edit_field_type_celltwo($data, $cell)
  1451. {
  1452. $r = $this->_publish_admin_edit_field_type_cell($data, $cell, '2');
  1453. $args = func_get_args();
  1454. return $this->forward_ff_hook('publish_admin_edit_field_type_celltwo', $args, $r);
  1455. }
  1456. /**
  1457. * Publish Admin - Edit Field Form - Format
  1458. *
  1459. * Allows modifying or adding onto Default Text Formatting Cell
  1460. *
  1461. * @param array $data The data about this field from the database
  1462. * @param string $y The current contents of the format cell
  1463. * @return string The modified $y
  1464. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_format/
  1465. */
  1466. function publish_admin_edit_field_format($data, $y)
  1467. {
  1468. $y = $this->get_last_call($y);
  1469. $args = func_get_args();
  1470. return $this->forward_ff_hook('publish_admin_edit_field_format', $args, $y);
  1471. }
  1472. /**
  1473. * Publish Admin - Edit Field Form - Extra Row
  1474. *
  1475. * Allows modifying or adding onto the Custom Field settings table
  1476. *
  1477. * @param array $data The data about this field from the database
  1478. * @param string $r The current contents of the page
  1479. * @return string The modified $r
  1480. * @see http://expressionengine.com/developers/extension_hooks/publish_admin_edit_field_extra_row/
  1481. */
  1482. function publish_admin_edit_field_extra_row($data, $r)
  1483. {
  1484. global $DSP, $LANG;
  1485. $r = $this->get_last_call($r);
  1486. if ($ftypes = $this->_get_ftypes())
  1487. {
  1488. $rows = '';
  1489. foreach($ftypes as $class_name => $ftype)
  1490. {
  1491. $ftype_id = 'ftype_id_'.$ftype->_fieldtype_id;
  1492. $selected = ($data['field_type'] == $ftype_id);
  1493. foreach($ftype->_field_settings['rows'] as $index => $row)
  1494. {
  1495. $class = $index % 2 ? 'tableCellOne' : 'tableCellTwo';
  1496. $rows .= '<tr id="'.$ftype_id.'_row'.($index+1).'"' . ($selected ? '' : ' style="display:none;"') . '>'
  1497. . '<td class="'.$class.'"'.(isset($row[1]) ? '' : ' colspan="2"').'>'
  1498. . $this->_group_ftype_inputs($ftype_id, $row[0])
  1499. . $DSP->td_c()
  1500. . (isset($row[1])
  1501. ? $DSP->td($class)
  1502. . $this->_group_ftype_inputs($ftype_id, $row[1])
  1503. . $DSP->td_c()
  1504. . $DSP->tr_c()
  1505. : '');
  1506. }
  1507. if ($selected)
  1508. {
  1509. // show/hide formatting
  1510. if ($ftype->_field_settings['formatting_available'])
  1511. {
  1512. $formatting_search = 'none';
  1513. $formatting_replace = 'block';
  1514. }
  1515. else
  1516. {
  1517. $formatting_search = 'block';
  1518. $formatting_replace = 'none';
  1519. }
  1520. $r = preg_replace('/(\sid\s*=\s*([\'\"])formatting_block\2.*display\s*:\s*)'

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