PageRenderTime 94ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/ied_plugin_composer/ied_plugin_composer.php

https://bitbucket.org/mrdale/txp-plugins
PHP | 3921 lines | 3026 code | 449 blank | 446 comment | 506 complexity | a0783449ec6e81388632c1026390cdfb MD5 | raw file
  1. <?php
  2. // This is a PLUGIN TEMPLATE for Textpattern CMS.
  3. // Copy this file to a new name like abc_myplugin.php. Edit the code, then
  4. // run this file at the command line to produce a plugin for distribution:
  5. // $ php abc_myplugin.php > abc_myplugin-0.1.txt
  6. // Plugin name is optional. If unset, it will be extracted from the current
  7. // file name. Plugin names should start with a three letter prefix which is
  8. // unique and reserved for each plugin author ("abc" is just an example).
  9. // Uncomment and edit this line to override:
  10. $plugin['name'] = 'ied_plugin_composer';
  11. // Allow raw HTML help, as opposed to Textile.
  12. // 0 = Plugin help is in Textile format, no raw HTML allowed (default).
  13. // 1 = Plugin help is in raw HTML. Not recommended.
  14. # $plugin['allow_html_help'] = 1;
  15. $plugin['version'] = '1.05';
  16. $plugin['author'] = 'Yura Linnyk / Stef Dawson / Steve Dickinson';
  17. $plugin['author_uri'] = 'http://stefdawson.com/';
  18. $plugin['description'] = 'Create, publish and edit plugins from within Textpattern';
  19. // Plugin load order:
  20. // The default value of 5 would fit most plugins, while for instance comment
  21. // spam evaluators or URL redirectors would probably want to run earlier
  22. // (1...4) to prepare the environment for everything else that follows.
  23. // Values 6...9 should be considered for plugins which would work late.
  24. // This order is user-overrideable.
  25. $plugin['order'] = '5';
  26. // Plugin 'type' defines where the plugin is loaded
  27. // 0 = public : only on the public side of the website (default)
  28. // 1 = public+admin : on both the public and admin side
  29. // 2 = library : only when include_plugin() or require_plugin() is called
  30. // 3 = admin : only on the admin side (no AJAX)
  31. // 4 = admin+ajax : only on the admin side (AJAX supported)
  32. // 5 = public+admin+ajax : on both the public and admin side (AJAX supported)
  33. $plugin['type'] = '5';
  34. // Plugin "flags" signal the presence of optional capabilities to the core plugin loader.
  35. // Use an appropriately OR-ed combination of these flags.
  36. // The four high-order bits 0xf000 are available for this plugin's private use
  37. if (!defined('PLUGIN_HAS_PREFS')) define('PLUGIN_HAS_PREFS', 0x0001); // This plugin wants to receive "plugin_prefs.{$plugin['name']}" events
  38. if (!defined('PLUGIN_LIFECYCLE_NOTIFY')) define('PLUGIN_LIFECYCLE_NOTIFY', 0x0002); // This plugin wants to receive "plugin_lifecycle.{$plugin['name']}" events
  39. $plugin['flags'] = '3';
  40. // Plugin 'textpack' is optional. It provides i18n strings to be used in conjunction with gTxt().
  41. // Syntax:
  42. // ## arbitrary comment
  43. // #@event
  44. // #@language ISO-LANGUAGE-CODE
  45. // abc_string_name => Localized String
  46. /** Uncomment me, if you need a textpack
  47. $plugin['textpack'] = <<<EOT
  48. #@admin
  49. #@language en-gb
  50. abc_sample_string => Sample String
  51. abc_one_more => One more
  52. #@language de-de
  53. abc_sample_string => Beispieltext
  54. abc_one_more => Noch einer
  55. EOT;
  56. **/
  57. // End of textpack
  58. if (!defined('txpinterface'))
  59. @include_once('zem_tpl.php');
  60. # --- BEGIN PLUGIN CODE ---
  61. // <?php
  62. /**
  63. * ied_plugin_composer
  64. *
  65. * A Textpattern CMS plugin for writing, editing and sharing plugins
  66. * -> Create and edit admin-side or public plugins
  67. * -> Supports plugin lifecycle events and prefs
  68. * -> Supports Textpacks
  69. * -> Optional syntax checker on save
  70. *
  71. * @author Yura Linnyk
  72. * @author Stef Dawson
  73. * @author Steve Dickinson
  74. * @link http://stefdawson.com/
  75. */
  76. // TODO:
  77. // * Use href() (from 4.6.+) for anchor creation to avoid double-encoded ampersands
  78. // * Figure out why syntax checker doesn't jump to line number sometimes (AJAX fails with error but it's not handled)
  79. // * Fix CSS/Markup so Textpack strings twisty label doesn't range right
  80. // * Show which langs have installed strings in the distribution section so the correct langs in the select list can be chosen
  81. // * Find out why uploading PHP files sometiems throws an error even though it succeeds
  82. // * jQuery on editor dropdowns in setup
  83. // * phpdoc
  84. global $ied_plugin_globals;
  85. $ied_plugin_globals = array(
  86. 'css_start' => '<!--',
  87. 'css_end' => '-->',
  88. 'dlm_start' => '#',
  89. 'dlm_end' => '',
  90. 'start' => ' --- BEGIN PLUGIN SECTION ---',
  91. 'end' => ' --- END PLUGIN SECTION ---',
  92. 'size_help' => '63535',
  93. 'size_css' => '2000',
  94. 'size_code' => '16777215',
  95. );
  96. if(@txpinterface == 'admin') {
  97. add_privs('ied_plugin_composer','1,2');
  98. add_privs('plugin_prefs.ied_plugin_composer','1,2');
  99. register_tab('extensions', 'ied_plugin_composer', gTxt('ied_plugin_lbl_composer'));
  100. register_callback('ied_plugin_composer', 'ied_plugin_composer');
  101. register_callback('ied_plugin_setup', 'plugin_prefs.ied_plugin_composer');
  102. register_callback('ied_plugin_welcome', 'plugin_lifecycle.ied_plugin_composer');
  103. register_callback('ied_plugin_inject_css', 'admin_side', 'head_end');
  104. global $ied_pc_event, $prefs;
  105. $ied_pc_event = 'ied_plugin_composer';
  106. } else {
  107. register_callback('ied_plugin_download', 'pretext');
  108. }
  109. // -------------------------------------------------------------
  110. // CSS definitions: hopefully kind to themers
  111. function ied_pc_get_style_rules() {
  112. $ied_pc_styles = array(
  113. 'ied_plugin' => '
  114. #ied_plugin64 { width:60%; }
  115. #ied_plugin_control h3 { text-align:left; }
  116. input[type="submit"] { margin:0.3em 0.7em; }
  117. .ied_label { margin:0 0.2em 0 0.6em;}
  118. .ied_plugin_setup { float:right; margin:-2em 0 0;}
  119. .ied_plugin_resizehandle { cursor:s-resize; float:left; text-align:center; font-size:1em; width:65%; padding:2px 0 6px; }
  120. .ied_plugin_info_bar { text-align:right; }
  121. #ied_plugin_jumpToLine { width:4em; margin:0 1em 0 0.4em; }
  122. .ied_editForm { width:{edwidth}; margin:0 auto; }
  123. .ied_subdue { color:gray; padding:1px 2px 2px 1px; }
  124. #ied_plugin_tp_controls input[type="text"] { width:16%; }
  125. #ied_plugin_tp_strings ul { list-style-type:none; }
  126. #ied_plugin_tp_strings ul label { margin:0 8px 0 0; }
  127. #ied_plugin_tp_strings ul input { width:450px; }
  128. .ied_plugin_edit_toolbar { text-align:right; width:95%; display:inline-block; margin:-2em 0 0 0; }
  129. #ied_plugin_msgpop { display:none; position:absolute; left:200px; max-width:500px; border:3px ridge #999; opacity:.92; filter:alpha(opacity:92); padding:15px 20px; background-color:#e2dfce; color:#80551e; }
  130. #ied_plugin_msgpop .publish { float:right; }
  131. ',
  132. );
  133. return $ied_pc_styles;
  134. }
  135. // -------------------------------------------------------------
  136. function ied_plugin_inject_css($evt, $stp) {
  137. global $ied_pc_event, $event;
  138. if ($event == $ied_pc_event) {
  139. $ied_plugin_prefs = ied_pc_get_prefs();
  140. $ied_plugin_styles = ied_pc_get_style_rules();
  141. // Possible variable replacements
  142. $edwidth = get_pref('ied_plugin_editor_width', $ied_plugin_prefs['ied_plugin_editor_width']['default']);
  143. $stylereps = array(
  144. '{edwidth}' => $edwidth,
  145. );
  146. echo '<style type="text/css">' . strtr($ied_plugin_styles['ied_plugin'], $stylereps) . '</style>';
  147. }
  148. return;
  149. }
  150. // -------------------------------------------------------------
  151. // Plugin jumpoff point
  152. function ied_plugin_composer($evt, $stp) {
  153. $available_steps = array(
  154. 'ied_plugin_code_save' => true,
  155. 'ied_plugin_create' => true,
  156. 'ied_plugin_delete' => true,
  157. 'ied_plugin_edit' => false,
  158. 'ied_plugin_generate_phpdoc' => true,
  159. 'ied_plugin_help' => true,
  160. 'ied_plugin_help_viewer' => false,
  161. 'ied_plugin_install' => true,
  162. 'ied_plugin_lang_set' => true,
  163. 'ied_plugin_table' => false,
  164. 'ied_plugin_multi_edit' => true,
  165. 'ied_plugin_prefs' => false,
  166. 'ied_plugin_restore' => true,
  167. 'ied_plugin_save' => true,
  168. 'ied_plugin_save_as_file' => true,
  169. 'ied_plugin_save_as_php_file' => true,
  170. 'ied_plugin_save_as_textpack' => true,
  171. 'ied_plugin_set_order' => true,
  172. 'ied_plugin_set_tp_prefix' => true,
  173. 'ied_plugin_switch_status' => true,
  174. 'ied_plugin_textpack_del' => true,
  175. 'ied_plugin_textpack_get' => true,
  176. 'ied_plugin_textpack_load' => true,
  177. 'ied_plugin_textpack_save' => true,
  178. 'ied_plugin_upload' => true,
  179. 'save_pane_state' => true,
  180. );
  181. if ($stp == 'save_pane_state') {
  182. $stp = 'ied_plugin_save_pane_state';
  183. } else if (!$stp or !bouncer($stp, $available_steps)) {
  184. $stp = 'ied_plugin_table';
  185. }
  186. $stp();
  187. }
  188. // -------------------------------------------------------------
  189. // Lifecycle handling, post-install / delete
  190. function ied_plugin_welcome($evt, $stp) {
  191. $msg = '';
  192. switch ($stp) {
  193. case 'installed':
  194. ied_plugin_prefs_update();
  195. $msg = 'Thanks for installing the plugin composer. Happy authoring :-)';
  196. break;
  197. case 'deleted':
  198. ied_plugin_prefs_remove(0);
  199. break;
  200. }
  201. return $msg;
  202. }
  203. // -------------------------------------------------------------
  204. // Table of plugins in both database and file system cache
  205. function ied_plugin_table($message='') {
  206. global $prefs, $ied_pc_event;
  207. pagetop(gTxt('ied_plugin_lbl_composer'),$message);
  208. require_privs('ied_plugin_composer');
  209. $ied_plugin_prefs = ied_pc_get_prefs();
  210. $lc_opts = do_list(get_pref('ied_plugin_lifecycle_options'));
  211. $auto_en = get_pref('ied_plugin_auto_enable');
  212. $cbout[] = '<p><label class="ied_label">'.gTxt('ied_plugin_run_install').'</label>';
  213. $checked = in_array('installed', $lc_opts);
  214. $cbout[] = yesnoradio('ied_plugin_installopts', $checked);
  215. $cbout[] = '<label class="ied_label">'.gTxt('ied_plugin_auto_enable').'</label>';
  216. $cbout[] = radioset($ied_plugin_prefs['ied_plugin_auto_enable']['content'], 'ied_plugin_autoenable', $auto_en).'</p>';
  217. extract(gpsa(array('sort', 'dir')));
  218. if ($sort === '') $sort = get_pref('ied_plugin_sort_column', 'name');
  219. if ($dir === '') $dir = get_pref('ied_plugin_sort_dir', 'asc');
  220. $dir = ($dir == 'desc') ? 'desc' : 'asc';
  221. if (!in_array($sort, array('name', 'status', 'author', 'version', 'load_order'))) $sort = 'name';
  222. $sort_sql = $sort.' '.$dir;
  223. set_pref('ied_plugin_sort_column', $sort, 'ied_plugin', PREF_HIDDEN, '', 0, PREF_PRIVATE);
  224. set_pref('ied_plugin_sort_dir', $dir, 'ied_plugin', PREF_HIDDEN, '', 0, PREF_PRIVATE);
  225. $switch_dir = ($dir == 'desc') ? 'asc' : 'desc';
  226. // Top control-panel part of screen
  227. echo '<h1 class="txp-heading">'.gTxt('ied_plugin_lbl_composer').sp.ied_plugin_anchor($ied_pc_event, 'ied_plugin_help_viewer', '?', array('name' => 'ied_plugin_composer'), array('class' => 'pophelp')).'</h1>'.
  228. n. '<div id="ied_plugin_control" class="txp-control-panel">'.
  229. n. sLink($ied_pc_event, 'ied_plugin_prefs', gTxt('ied_plugin_setup'), 'ied_plugin_setup').
  230. n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_cpanel_visible') ? ' expanded' : '').'"><a href="#ied_plugin_cpanel">' . gTxt('ied_plugin_cpanel_legend') . '</a></h3><div id="ied_plugin_cpanel" class="toggle" style="display:'.(get_pref('pane_ied_plugin_cpanel_visible') ? 'block' : 'none').'">'.
  231. n. '<form class="ied_plugin_form" enctype="multipart/form-data" action="index.php" method="post">'.
  232. n. '<p>'.
  233. n. '<label for="ied_plugin_newname" class="ied_label">'.gTxt('name').'</label>'.
  234. n. fInput('text', 'name', '', '', '', '', INPUT_REGULAR, '', 'ied_plugin_newname').
  235. n. fInput('submit', 'plugin_create', gTxt('ied_plugin_create_new')).
  236. n. '</p>'.
  237. n. '<p>'.
  238. n. '<label for="ied_plugin_file" class="ied_label">'.gTxt('ied_plugin_upload_php').'</label>'.
  239. n. fInput('file', 'thefile', '', '', '', '', '', '', 'ied_plugin_file').
  240. n. fInput('submit', 'plugin_upload', gTxt('upload')).
  241. n. '</p>'.
  242. n. '<p>'.
  243. n. '<label for="ied_plugin64" class="ied_label">'.gTxt('ied_plugin_install_txt').'</label>'.
  244. n. text_area('plugin64', '', '', '', 'ied_plugin64').
  245. n. fInput('submit', 'plugin_install', gTxt('install')).
  246. n. '</p>'.
  247. n. join(n, $cbout).
  248. n. eInput($ied_pc_event).
  249. n. sInput('ied_plugin_create').
  250. n. hInput('MAX_FILE_SIZE', 1000000).
  251. n. tInput().
  252. n. '</form>'.
  253. n. '</div>'.
  254. n. '</div>'.
  255. n. '</div>';
  256. // Main plugin list
  257. echo n. '<div id="ied_plugin_container" class="txp-container">';
  258. $rs = safe_rows('*', 'txp_plugin', '1=1 ORDER BY '.$sort_sql);
  259. if ($rs) {
  260. echo '<div class="summary-details">'.
  261. n. '<form action="index.php" id="ied_plugin_db_form" method="post">'.
  262. n. '<h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_dbplugs_visible') ? ' expanded' : '').'">'.
  263. n. '<a href="#ied_plugin_dbplugs">' . gTxt('ied_plugin_dbplugs_legend') . '</a>'.
  264. n. '</h3>'.
  265. n. '<div id="ied_plugin_dbplugs" class="toggle" style="display:'.(get_pref('pane_ied_plugin_dbplugs_visible') ? 'block' : 'none').'">'.
  266. n. '<div class="txp-listtables">'.
  267. n. startTable('', '', 'txp-list').
  268. n.'<thead>'.
  269. n. tr(
  270. n.hCell(fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), '', ' title="'.gTxt('toggle_all_selected').'" class="multi-edit"')
  271. .n.column_head('plugin', 'name', 'ied_plugin_composer', true, $switch_dir, '', '', (('name' == $sort) ? "$dir " : '').'name')
  272. .n.column_head('author', 'author', 'ied_plugin_composer', true, $switch_dir, '', '', (('author' == $sort) ? "$dir " : '').'author')
  273. .n.column_head(gTxt('version').' ('.gTxt('plugin_modified').')', 'version', 'ied_plugin_composer', true, $switch_dir, '', '', (('version' == $sort) ? "$dir " : '').'version')
  274. .n.hCell(gTxt('description'), '', ' class="description"')
  275. .n.hCell(gTxt('manage'), '', ' class="manage"')
  276. .n.column_head('active', 'status', 'ied_plugin_composer', true, $switch_dir, '', '', (('status' == $sort) ? "$dir " : '').'status')
  277. ).
  278. n. '</thead>'.
  279. n. '<tbody>';
  280. foreach ($rs as $row) {
  281. extract($row);
  282. $ename = ied_plugin_anchor($ied_pc_event, 'ied_plugin_edit', $name, array('name' => $name));
  283. $hlink = ($help) ? ied_plugin_anchor($ied_pc_event, 'ied_plugin_help_viewer', gTxt('ied_plugin_docs'), array('name' => $name)) : gTxt('none');
  284. $fnames = ied_plugin_get_name($name, $version);
  285. $pubtag = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('publish'), array('name' => $name), array('title' => gTxt('ied_plugin_export', array('{name}' => $fnames[0]))));
  286. $pubztag = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('ied_plugin_compress'), array('name' => $name, 'type' => 'zip'), array('title' => gTxt('ied_plugin_export', array('{name}' => $fnames[1]))));
  287. $modified = (strtolower($code) != (strtolower($code_restore)));
  288. $plugpref = ($flags & PLUGIN_HAS_PREFS) ? (sp.ied_plugin_anchor('plugin_prefs.'.urlencode($name), '', '['.gTxt('plugin_prefs').']', array('name' => $name), array('class' => 'plugin_prefs'.( ($status) ? '' : ' empty'))) ) : '';
  289. echo tr(
  290. n.td(
  291. fInput('checkbox', 'selected[]', $name)
  292. ,'', 'multi-edit')
  293. .n.td($ename.$plugpref)
  294. .n.td(( ($author_uri) ? '<a href="'.txpspecialchars($author_uri).'">'.txpspecialchars($author).'</a>' : txpspecialchars($author)))
  295. .n.td(( ($modified) ? ied_plugin_anchor($ied_pc_event, 'ied_plugin_restore', $version, array('name' => $name), array('title' => gTxt('ied_plugin_restore_help'), 'onclick' => 'return verify(\''.gTxt('ied_plugin_restore_verify', array('{name}' => $name)).'\');')) : $version) . (($modified) ? sp.'('.gTxt('yes').')' : ''))
  296. .n.td(txpspecialchars($description))
  297. .n.td($pubtag .sp. '&#124;' .sp. $pubztag .sp. '&#124;' .sp. $hlink)
  298. .n.td(ied_plugin_status_link($status,$name,yes_no($status)))
  299. );
  300. unset($name,$page);
  301. }
  302. echo n. '</tbody>'.
  303. n. endTable().
  304. n. '</div>'.
  305. n. tInput().
  306. n. '</form>'.
  307. ied_plugin_multiedit_form('db', '', $sort, $dir, '', '').
  308. n. '</div>'.
  309. n. '</div>';
  310. }
  311. if (!empty($prefs['plugin_cache_dir']) && file_exists($prefs['plugin_cache_dir'])) {
  312. $filenames = array();
  313. $directory = dir($prefs['plugin_cache_dir']);
  314. while ($file = $directory->read()) {
  315. if($file != "." && $file != "..") {
  316. $fileaddr = $prefs['plugin_cache_dir'].DS.$file;
  317. if (!is_dir($fileaddr)) {
  318. $filenames[]=$file;
  319. }
  320. }
  321. }
  322. $directory->close();
  323. ($filenames)?natcasesort($filenames):'';
  324. $out = array();
  325. foreach($filenames as $filename) {
  326. $parts = explode ('.',$filename);
  327. $fileext = array_pop($parts);
  328. if ($fileext=='php') {
  329. $basename = basename($filename);
  330. $plugin = ied_plugin_read_file($prefs['plugin_cache_dir'].DS.$filename);
  331. $hlink = ($plugin['help']) ? ied_plugin_anchor($ied_pc_event, 'ied_plugin_help_viewer', gTxt('ied_plugin_docs'), array('filename' => $filename)) : gTxt('none');
  332. $efile = ied_plugin_anchor($ied_pc_event, 'ied_plugin_edit', $plugin['name'], array('filename' => $filename));
  333. $fnames = ied_plugin_get_name($plugin['name'], $plugin['version']);
  334. $plugpref = (($plugin['flags'] & PLUGIN_HAS_PREFS)) ? ' '.ied_plugin_anchor('plugin_prefs.'.urlencode($plugin['name']), '', ' ['.gTxt('plugin_prefs').']') : '';
  335. $pubtag = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('publish'), array('filename' => $filename), array('title' => gTxt('ied_plugin_export', array('{name}' => $fnames[0]))));
  336. $pubztag = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('ied_plugin_compress'), array('filename' => $filename, 'type' => 'zip'), array('title' => gTxt('ied_plugin_export', array('{name}' => $fnames[1]))));
  337. $out[] = tr(
  338. n.td(
  339. fInput('checkbox', 'selected-cache[]', $filename)
  340. ,'', 'multi-edit')
  341. .n.td(
  342. tag($filename,'div',' class="ied_subdue"')
  343. .(isset($plugin['name']) ? $efile.$plugpref.'<br />' : '').' '
  344. )
  345. .n.td(
  346. ( isset($plugin['author_uri']) ? '<a href="'.$plugin['author_uri'].'">' : '' ) .
  347. ( isset($plugin['author']) ? $plugin['author'] : '&nbsp;' ).
  348. ( isset($plugin['author_uri']) ? '</a>' : '' )
  349. )
  350. .n.td(
  351. (isset($plugin['version']) ? $plugin['version'] : '&nbsp;')
  352. )
  353. .n.td(
  354. (isset($plugin['description']) ? $plugin['description'] : '&nbsp;')
  355. )
  356. .n.td(
  357. (isset($plugin['name']) ? $pubtag .sp. '&#124;' .sp. $pubztag
  358. : tag('&nbsp;', 'span')
  359. )
  360. .sp. '&#124;' .sp. $hlink
  361. )
  362. );
  363. }
  364. }
  365. if ($out) {
  366. echo '<div class="summary-details">'.
  367. n. '<form action="index.php" id="ied_plugin_cache_form" method="post">'.
  368. n. '<h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_cacheplugs_visible') ? ' expanded' : '').'">'.
  369. n. '<a href="#ied_plugin_cacheplugs">' . gTxt('ied_plugin_cacheplugs_legend') . '</a>'.
  370. n. '</h3>'.
  371. n. '<div id="ied_plugin_cacheplugs" class="toggle" style="display:'.(get_pref('pane_ied_plugin_cacheplugs_visible') ? 'block' : 'none').'">'.
  372. n. '<div class="txp-listtables ied_plugin_cacheplugs">'.
  373. n.startTable('', '', 'txp-list').
  374. n. '<thead>'.
  375. n. tr(
  376. n.hCell(fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), '', ' title="'.gTxt('toggle_all_selected').'" class="multi-edit"')
  377. .n.hCell(gTxt('plugin'), '', ' class="name"')
  378. .n.hCell(gTxt('author'), '', ' class="author"')
  379. .n.hCell(gTxt('version') . ' ('.gTxt('plugin_modified').')', '', ' class="version"')
  380. .n.hCell(gTxt('description'), '', ' class="description"')
  381. .n.hCell(gTxt('manage'), '', ' class="manage"')
  382. ).
  383. n. '</thead>'.
  384. n. '<tbody>'.
  385. n. join(n, $out).
  386. n. '</tbody>'.
  387. n. endTable().
  388. n. '</div>'.
  389. n. tInput().
  390. n. '</form>'.
  391. ied_plugin_multiedit_form('cache', '', $sort, $dir, '', '').
  392. n. '</div>'.
  393. n. '</div>';
  394. }
  395. }
  396. echo '</div>'.
  397. n. script_js( <<<EOS
  398. $(document).ready(function() {
  399. $('#ied_plugin_db_form').txpMultiEditForm({
  400. 'checkbox' : 'input[name="selected[]"][type=checkbox]'
  401. });
  402. $('#ied_plugin_cache_form').txpMultiEditForm({
  403. 'checkbox' : 'input[name="selected-cache[]"][type=checkbox]'
  404. });
  405. });
  406. EOS
  407. );
  408. // Show/hide "Options" link by setting the appropriate class on the plugin's TR
  409. echo script_js(<<<EOS
  410. textpattern.Relay.register('txpAsyncHref.success', function(event, data) {
  411. jQuery(data.this).closest('tr').find('a.plugin_prefs').toggleClass('empty');
  412. });
  413. EOS
  414. );
  415. }
  416. // -------------------------------------------------------------
  417. function ied_plugin_multiedit_form($flavour, $page, $sort, $dir, $crit, $search_method) {
  418. global $ied_pc_event;
  419. $orders = selectInput('order', array(1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 9=>9), 5, false);
  420. $stati = selectInput('switch_status', array('toggle' => gTxt('ied_plugin_toggle'), 'on' => gTxt('on'), 'off' => gTxt('off')), 'toggle', false);
  421. $lifecycles = selectInput('ied_lc_event', array(
  422. 'installed' => gTxt('ied_plugin_lbl_lc_install'),
  423. 'enabled' => gTxt('ied_plugin_lbl_lc_enable'),
  424. 'installed,enabled' => gTxt('ied_plugin_lbl_lc_instable'),
  425. 'disabled' => gTxt('ied_plugin_lbl_lc_disable'),
  426. 'deleted' => gTxt('ied_plugin_lbl_lc_delete'),
  427. 'disabled,deleted' => gTxt('ied_plugin_lbl_lc_disdel'),
  428. ));
  429. if ($flavour === 'db') {
  430. $methods = array(
  431. 'changestatus' => array('label' => gTxt('changestatus'), 'html' => $stati),
  432. 'changeorder' => array('label' => gTxt('changeorder'), 'html' => $orders),
  433. 'lifecycle' => array('label' => gTxt('ied_plugin_lifecycle'), 'html' => $lifecycles),
  434. 'delete' => gTxt('delete'),
  435. );
  436. } else {
  437. $methods = array(
  438. 'lifecycle' => array('label' => gTxt('ied_plugin_lifecycle'), 'html' => $lifecycles),
  439. 'textpack' => array('label' => gTxt('ied_plugin_install_textpack')),
  440. 'delete' => gTxt('delete'),
  441. );
  442. }
  443. return multi_edit($methods, $ied_pc_event, 'ied_plugin_multi_edit', $page, $sort, $dir, $crit, $search_method);
  444. }
  445. // -------------------------------------------------------------
  446. function ied_plugin_multi_edit() {
  447. global $prefs;
  448. $selected = ps('selected');
  449. $selected_cache = ps('selected-cache');
  450. $method = assert_string(ps('edit_method'));
  451. if ($selected && is_array($selected))
  452. {
  453. $where = "name IN ('".join("','", doSlash($selected))."')";
  454. $lc_opts = do_list(get_pref('ied_plugin_lifecycle_options'));
  455. switch ($method)
  456. {
  457. case 'delete':
  458. foreach ($selected as $name)
  459. {
  460. if (safe_field('flags', 'txp_plugin', "name ='".doSlash($name)."'") & PLUGIN_LIFECYCLE_NOTIFY)
  461. {
  462. load_plugin($name, true);
  463. if (in_array('disabled', $lc_opts)) {
  464. callback_event("plugin_lifecycle.$name", 'disabled');
  465. }
  466. if (in_array('deleted', $lc_opts)) {
  467. callback_event("plugin_lifecycle.$name", 'deleted');
  468. }
  469. }
  470. }
  471. safe_delete('txp_plugin', $where);
  472. break;
  473. case 'changestatus':
  474. switch (ps('switch_status')) {
  475. case 'on':
  476. $newstat = '1';
  477. break;
  478. case 'off':
  479. $newstat = '0';
  480. break;
  481. case 'toggle':
  482. default:
  483. $newstat = '(1-status)';
  484. break;
  485. }
  486. foreach ($selected as $name) {
  487. if (safe_field('flags', 'txp_plugin', "name ='".doSlash($name)."'") & PLUGIN_LIFECYCLE_NOTIFY) {
  488. $status = safe_field('status', 'txp_plugin', "name ='".doSlash($name)."'");
  489. $status = $status ? 'disabled' : 'enabled';
  490. load_plugin($name, true);
  491. if (in_array($status, $lc_opts)) {
  492. callback_event("plugin_lifecycle.$name", $status);
  493. }
  494. }
  495. }
  496. safe_update('txp_plugin', 'status = '.$newstat, $where);
  497. break;
  498. case 'changeorder':
  499. $order = min(max(intval(ps('order')), 1), 9);
  500. safe_update('txp_plugin', 'load_order = '.$order, $where);
  501. break;
  502. case 'lifecycle':
  503. $lc_evs = do_list(ps('ied_lc_event'));
  504. foreach ($selected as $name) {
  505. foreach ($lc_evs as $lc_ev) {
  506. callback_event("plugin_lifecycle.$name", $lc_ev);
  507. }
  508. }
  509. break;
  510. }
  511. } else {
  512. $selected = array();
  513. }
  514. if ($selected_cache && is_array($selected_cache))
  515. {
  516. switch ($method)
  517. {
  518. case 'delete':
  519. foreach ($selected_cache as $name) {
  520. $filenames = array();
  521. $dir = dir($prefs['plugin_cache_dir']);
  522. while ($file = $dir->read()) {
  523. if($file != "." && $file != ".." && in_array($file, $selected_cache)) {
  524. $fileaddr = $prefs['plugin_cache_dir'].DS.$file;
  525. if (!is_dir($fileaddr)) {
  526. unlink($fileaddr);
  527. }
  528. }
  529. }
  530. $dir->close();
  531. }
  532. break;
  533. case 'lifecycle':
  534. $lc_evs = do_list(ps('ied_lc_event'));
  535. foreach ($selected_cache as $name) {
  536. $name = str_replace('.php', '', $name);
  537. foreach ($lc_evs as $lc_ev) {
  538. callback_event("plugin_lifecycle.$name", $lc_ev);
  539. }
  540. }
  541. break;
  542. case 'textpack':
  543. // Read the textpack from the .php file and call install_textpack()
  544. $textpack = array();
  545. foreach ($selected_cache as $name) {
  546. $fileaddr = $prefs['plugin_cache_dir'].DS.$name;
  547. $contents = file($fileaddr);
  548. $in_tp = $in_comment = false;
  549. foreach ($contents as $row) {
  550. if (strpos($row, '/**') === 0) {
  551. $in_comment = true;
  552. }
  553. if (strpos($row, '**/') === 0) {
  554. $in_comment = false;
  555. }
  556. if (strpos($row, 'EOT;') !== false) {
  557. break;
  558. }
  559. if ($in_tp === true) {
  560. $textpack[] = trim($row);
  561. }
  562. if (!$in_comment && strpos($row, '$plugin[\'textpack\']') !== false) {
  563. $in_tp = true;
  564. }
  565. }
  566. }
  567. $done = install_textpack(join(n, $textpack));
  568. break;
  569. }
  570. } else {
  571. $selected_cache = array();
  572. }
  573. $message = '';
  574. if ($selected || $selected_cache) {
  575. if ($method === 'delete') {
  576. $message = gTxt('plugin_deleted', array('{name}' => join(', ', array_merge($selected, $selected_cache))));
  577. } else if ($method === 'lifecycle') {
  578. $message = gTxt('ied_plugin_lc_fired', array('{name}' => join(', ', array_merge($selected, $selected_cache)), '{event}' => join(',', $lc_evs)));
  579. } else if ($method === 'textpack') {
  580. $message = gTxt('textpack_strings_installed', array('{count}' => $done));
  581. } else {
  582. $message = gTxt('plugin_updated', array('{name}' => join(', ', $selected)));
  583. }
  584. }
  585. ied_plugin_table($message);
  586. }
  587. // -------------------------------------------------------------
  588. function ied_plugin_anchor($evt, $stp, $linktext, $nv = array(), $atts = array()) {
  589. $nv['_txp_token'] = form_token();
  590. $atts['href'] = '?event='.$evt.($stp ? '&step='.$stp : '').'&'.http_build_query($nv);
  591. $attribs = '';
  592. foreach ($atts as $n => $v) {
  593. $attribs .= ' '.txpspecialchars($n).'="'.txpspecialchars($v).'"';
  594. }
  595. return tag($linktext, 'a', $attribs);
  596. }
  597. // -------------------------------------------------------------
  598. function ied_plugin_status_link($status, $name, $linktext) {
  599. return asyncHref($linktext, array('step' => 'ied_plugin_switch_status', 'thing' => $name),' title="'.($status==1 ? gTxt('disable') : gTxt('enable')).'"' );
  600. }
  601. // -------------------------------------------------------------
  602. function ied_plugin_switch_status() {
  603. extract(array_map('assert_string', gpsa(array('thing', 'value'))));
  604. $change = ($value == gTxt('yes')) ? 0 : 1;
  605. safe_update('txp_plugin', "status = $change", "name = '".doSlash($thing)."'");
  606. $lc_opts = do_list(get_pref('ied_plugin_lifecycle_options'));
  607. $stp = $change ? 'enabled' : 'disabled';
  608. if (in_array($stp, $lc_opts) && (safe_field('flags', 'txp_plugin', "name='".doSlash($thing)."'") & PLUGIN_LIFECYCLE_NOTIFY) ) {
  609. load_plugin($thing, true);
  610. $message = callback_event("plugin_lifecycle.$thing", $stp);
  611. }
  612. echo gTxt($change ? 'yes' : 'no');
  613. }
  614. // -------------------------------------------------------------
  615. function ied_plugin_set_order() {
  616. extract(doSlash(gpsa(array('name', 'load_order'))));
  617. $order = min(max( intval($load_order), 1), 9);
  618. safe_update('txp_plugin', "load_order = $load_order", "name = '$name'");
  619. ied_plugin_table(gTxt('plugin_saved', array('{name}' => $name)));
  620. }
  621. // -------------------------------------------------------------
  622. function ied_plugin_delete() {
  623. $name = doSlash(ps('name'));
  624. $lc_opts = do_list(get_pref('ied_plugin_lifecycle_options'));
  625. $lc_dis = in_array('disabled', $lc_opts);
  626. $lc_del = in_array('deleted', $lc_opts);
  627. if ( ($lc_del || $lc_dis) && (safe_field('flags', 'txp_plugin', "name='".$name."'") & PLUGIN_LIFECYCLE_NOTIFY) ) {
  628. load_plugin($name, true);
  629. if ($lc_dis) {
  630. callback_event("plugin_lifecycle.$name", 'disabled');
  631. }
  632. if ($lc_del) {
  633. callback_event("plugin_lifecycle.$name", 'deleted');
  634. }
  635. }
  636. safe_delete('txp_plugin', "name='$name'");
  637. ied_plugin_table(gTxt('plugin_deleted', array('{name}' => $name)));
  638. }
  639. // -------------------------------------------------------------
  640. function ied_plugin_restore() {
  641. $name = doSlash(gps('name'));
  642. safe_update("txp_plugin","code = code_restore","name='$name'");
  643. ied_plugin_table(gTxt('ied_plugin_restored', array('{name}' => $name)));
  644. }
  645. // -------------------------------------------------------------
  646. function ied_plugin_edit($message='', $newfile='') {
  647. global $prefs, $ied_plugin_globals, $ied_pc_event;
  648. $newname = trim(gps('newname'));
  649. $filename = gps('filename');
  650. $editfile = $filename ? 1 : 0;
  651. $name = empty($newname) ? gps('name') : $newname;
  652. $name = ($newfile) ? $newfile : (($filename) ? $filename : $name);
  653. pagetop(gTxt('ied_plugin_editing', array('{name}' => txpspecialchars($name))), $message);
  654. require_privs('ied_plugin_composer');
  655. echo ied_insert_editors();
  656. if (!$editfile) {
  657. $rs = safe_row("author, author_uri, version, description, code, help, status, type, load_order, flags", "txp_plugin", "name='".doSlash($name)."'");
  658. extract($rs);
  659. list($css,$help) = ($help) ? ied_plugin_extract_hunk($help, "CSS", "<!--|-->", true) : array('',$help);
  660. } else {
  661. $plugin = ied_plugin_read_file($prefs['plugin_cache_dir'].DS.$name);
  662. $filename = $name;
  663. $name = explode ('.', $name);
  664. $fileext = array_pop($name);
  665. $name = implode($name);
  666. extract($plugin);
  667. $status = ($fileext=='php')? 1: 0;
  668. }
  669. $ifel = get_pref('ied_plugin_interface_elems');
  670. $distblock = (strpos($ifel, 'distribution') !== false);
  671. $styleblock = (strpos($ifel, 'style') !== false);
  672. $distribution = '';
  673. list ($start_css, $end_css) = ied_plugin_make_markers("CSS", $ied_plugin_globals['css_start'], $ied_plugin_globals['css_end']);
  674. if ($distblock) {
  675. $plugin['name'] = $name;
  676. $plugin['author'] = $author;
  677. $plugin['author_uri'] = $author_uri;
  678. $plugin['version'] = $version;
  679. $plugin['description'] = $description;
  680. $plugin['help'] = ied_plugin_textile($name, $help, $css, $start_css, $end_css);
  681. $plugin['code'] = $code;
  682. $plugin['type'] = $type;
  683. $plugin['order'] = $load_order;
  684. $plugin['flags'] = $flags;
  685. $plugin['md5'] = md5( $plugin['code'] );
  686. $distribution = '<textarea name="distribution" rows="1" onclick="this.select()">'.base64_encode(serialize($plugin)).'</textarea>';
  687. }
  688. for ($i = 1; $i <= 9; $i++) $orders[$i] = $i;
  689. $tp_pfx = unserialize(get_pref('ied_plugin_tp_prefix', '', 1));
  690. $tp_pfx = isset($tp_pfx[$name]) ? $tp_pfx[$name] : '';
  691. $fnames = ied_plugin_get_name($name, $version);
  692. $namedLink = ($filename) ? array('filename' => $filename) : array('name' => $name);
  693. $zippedLink = array_merge($namedLink, array('type' => 'zip'));
  694. $slink = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('ied_plugin_export', array('{name}' => $fnames[0])), $namedLink);
  695. $sziplink = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_file', gTxt('ied_plugin_export_zip', array('{name}' => $fnames[1])), $zippedLink);
  696. $sphplink = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_php_file', gTxt('ied_plugin_save_as', array('{name}' => $fnames[2])), $namedLink);
  697. $stxtlink = ied_plugin_anchor($ied_pc_event, 'ied_plugin_save_as_textpack', gTxt('ied_plugin_export_textpack'), $namedLink);
  698. $vhelplinkfull = ($help) ? '[ ' .ied_plugin_anchor($ied_pc_event, 'ied_plugin_help_viewer', gTxt('ied_plugin_docs'), $namedLink) . ' ]' : '';
  699. $msgpop = '<div id="ied_plugin_msgpop"><input type="button" class="publish" value="'.gTxt('ok').'" onclick="ied_plugin_toggle_msgpop(\'0\');" /><h2>'.gTxt('ied_plugin_msgpop_lbl').'</h2><span class="ied_plugin_msgpop_content"></span></div>';
  700. $newname = fInput('text', 'newname', $name, '', '', '', INPUT_REGULAR);
  701. $author_widget = fInput('text', 'author', $author, '', '', '', INPUT_REGULAR);
  702. $author_uri_widget = fInput('text', 'author_uri', $author_uri, '', '', '', INPUT_REGULAR);
  703. $version_widget = fInput('text', 'version', $version, 'input-small', '', '',INPUT_SMALL) .sp. (($editfile) ? checkbox('rename_file', '1', 0, '','rename_file') . ' <label for="rename_file">'.gTxt('ied_plugin_rename_file').'</label>' : checkbox('restore_point', '1', 0, '','restore_point') . ' <label for="restore_point">'.gTxt('ied_plugin_restore_point').'</label>');
  704. $description_widget = fInput('text', 'description', $description, 'input-xlarge', '', '', INPUT_LARGE);
  705. $codeblock = '<textarea name="code" id="plugin_editor" rows="'.INPUT_REGULAR.'" class="code codepress php" maxlength="'.$ied_plugin_globals['size_code'].'">'.txpspecialchars($code).'</textarea><div class="ied_plugin_info_bar"><span>'.gTxt('ied_plugin_jump_to_line').'</span><input type="text" id="ied_plugin_jumpToLine" size="5" maxlength="6" /><span class="ied_plugin_charsRemain"></span></div>';
  706. $help_widget = '<textarea name="help" rows="'.INPUT_REGULAR.'" class="mceEditor" maxlength="'.$ied_plugin_globals['size_help'].'">'.txpspecialchars($help).'</textarea><div class="ied_plugin_info_bar"><span class="ied_plugin_charsRemain"></span></div>';
  707. $css_widget = ($styleblock) ? '<textarea name="css" rows="'.INPUT_MEDIUM.'" class="code" maxlength="'.$ied_plugin_globals['size_css'].'">'.txpspecialchars($css).'</textarea><div class="ied_plugin_info_bar"><span class="ied_plugin_charsRemain"></span></div>' : '';
  708. $plugstatus = (!$editfile) ? sp.sp.checkbox('status',1,$status, '','status'). ' <label for="status">'.gTxt('ied_plugin_enable').'</label>' : '';
  709. $plugtype = radio('type',0,(($type==0)?1:0)).gTxt('ied_plugin_type_0')." "
  710. . radio('type',1,(($type==1)?1:0)).gTxt('ied_plugin_type_1')." "
  711. . radio('type',2,(($type==2)?1:0)).gTxt('ied_plugin_type_2')." "
  712. . radio('type',3,(($type==3)?1:0)).gTxt('ied_plugin_type_3')." "
  713. . radio('type',4,(($type==4)?1:0)).gTxt('ied_plugin_type_4')." "
  714. . radio('type',5,(($type==5)?1:0)).gTxt('ied_plugin_type_5');
  715. $plugorder = selectInput('load_order', $orders, $load_order, 0, 0);
  716. $flaglist = checkbox('flags[]',PLUGIN_HAS_PREFS,(($flags & PLUGIN_HAS_PREFS)?1:0)) . '<label>'.gTxt('ied_plugin_flag_has_prefs').'</label>&nbsp;&nbsp;'
  717. .checkbox('flags[]',PLUGIN_LIFECYCLE_NOTIFY,(($flags & PLUGIN_LIFECYCLE_NOTIFY)?1:0)) . '<label>'.gTxt('ied_plugin_flag_lifecycle_notify').'</label>&nbsp;&nbsp;';
  718. // .checkbox('flags[]',0x0004,(($flags & 0x0004)?1:0)) . '<label>Summat else</label>&nbsp;&nbsp;';
  719. $sub = fInput('submit', '', gTxt('save'), 'publish', '', '', '', '', 'ied_editSave');
  720. $codesub = (!$editfile) ? '<a class="navlink" name="ied_plugin_code_save" id="ied_plugin_code_save">' . gTxt('ied_plugin_code_save') . '</a>' : '';
  721. // Language info. ied_visible_langs is the user's choice of which ones they want to see available.
  722. // ied_available_langs is the list of actual, currently-installed langs
  723. $ied_listlangs = get_pref('ied_plugin_lang_choose', 'installed');
  724. $ied_visible_langs = ied_plugin_lang_list($ied_listlangs);
  725. $ied_available_langs = ($ied_listlangs == 'installed') ? $ied_visible_langs : ied_plugin_lang_list('installed');
  726. $dflt_lang = get_pref('ied_plugin_lang_default', $prefs['language']);
  727. $dflt_lang = array_key_exists($dflt_lang, $ied_visible_langs) ? $dflt_lang : $prefs['language'];
  728. $langsel = selectInput('ied_plugin_tp_lang', $ied_visible_langs, $dflt_lang, '', '', 'ied_plugin_tp_lang')
  729. .fInput('button', 'ied_plugin_tp_refresh', gTxt('ied_plugin_load'), '', '', '', '', '', 'ied_plugin_tp_refresh');
  730. $preselected = do_list(get_pref('ied_plugin_lang_selected', ''));
  731. $op_langs[] = '<select name="ied_plugin_tp_oplangs" id="ied_plugin_tp_oplangs" multiple="multiple"><option value=""></option>';
  732. foreach ($ied_available_langs as $langcode => $alang) {
  733. $sel = in_array($langcode, $preselected) ? ' selected="selected"' : '';
  734. $op_langs[] = '<option value="'.$langcode.'"'.$sel.'>'.$alang.'</option>';
  735. }
  736. $op_langs[] = '</select>';
  737. $tp_strings = array();
  738. $tp_rows = ied_plugin_textpack_grab($dflt_lang, $tp_pfx);
  739. foreach ($tp_rows as $tp_string) {
  740. $apsel = selectInput('ied_plugin_tp_event', array('admin' => gTxt('admin'), 'public' => gTxt('public'), 'common' => gTxt('both')), ($tp_string['event'] == 'public' ? 'public' : ($tp_string['event'] == 'common' ? 'common' : 'admin')) );
  741. $tp_strings[] = '<li>'.fInput('text', 'textpack_'.$tp_string['name'], $tp_string['data']).' '.$apsel.' <label>'.$tp_string['name'].'</label>'.'</li>';
  742. }
  743. $err_prefix = gTxt('ied_plugin_syntax_err');
  744. $codesave_ok = gTxt('ied_plugin_code_saved');
  745. $phpdoc = '';
  746. // TODO
  747. $classFinder = '/class[\s\n]+(\w+)[\s\n]*\{?(function[\s\n]+(\w+)[\s\n]*\(.*\)[\s\n]*\{?)*/';
  748. $functionFinder = '/function[\s\n]+(\w+)[\s\n]*\(.*\)[\s\n]*\{?/';
  749. preg_match_all($functionFinder, $code , $functionArray);
  750. // selectInput requires key and value to be the same
  751. //dmp($functionArray);
  752. $fnArray = array();
  753. /*
  754. foreach($functionArray[1] as $key => $val) {
  755. $fnArray[$val] = $functionArray[1][$key];
  756. }
  757. $phpdoc = selectInput('ied_plugin_to_phpdoc', $fnArray, '', false, '', 'ied_plugin_to_phpdoc')
  758. .'<a class="navlink" name="ied_plugin_btn_phpdoc" id="ied_plugin_btn_phpdoc">' . gTxt('ied_plugin_php_doc') . '</a>';
  759. */
  760. $phpdoc = '';
  761. echo
  762. hed(gTxt('ied_plugin_edit', array('{name}' => $name, '{version}' => $version)).n.$vhelplinkfull, 2).
  763. n. form(
  764. '<div id="ied_plugin_sub">'. ($sub).'</div>'
  765. .n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_meta_visible') ? ' expanded' : '').'"><a href="#ied_plugin_meta">' . gTxt('ied_plugin_meta_legend') . '</a></h3><div id="ied_plugin_meta" class="toggle" style="display:'.(get_pref('pane_ied_plugin_meta_visible') ? 'block' : 'none').'">'
  766. .n. '<p><label>' . gTxt('name') . '</label>' . sp . $newname . sp. '<label>' . gTxt('version') . '</label>' . sp . $version_widget . $plugstatus . ( ($filename) ? tag(sp.sp.'('.$filename.')','span',' style="color:gray;"').hInput('filename',$filename) : '' ) . '</p>'
  767. .n. '<p><label>' . gTxt('description') . '</label>' . sp . $description_widget . '</p>'
  768. .n. '<p><label>' . gTxt('author') . '</label>' . sp . $author_widget . sp. '<label>' . gTxt('website') . '</label>' .sp. $author_uri_widget. '</p>'
  769. .n. '<p><label>' . gTxt('ied_plugin_type') . '</label>' . sp . $plugtype . '</p>'
  770. .n. '<p><label>' . gTxt('ied_plugin_flags') . '</label>' . sp . $flaglist .sp. '<label>' . gTxt('ied_plugin_load_order') . '</label>' . sp . $plugorder . sp.sp . gTxt('ied_plugin_load_order_help') . '</p>'
  771. .n. '</div></div>'
  772. .n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_code_visible') ? ' expanded' : '').'"><a href="#ied_plugin_code">'.gTxt('ied_plugin_code_legend').'</a></h3><div id="ied_plugin_code" class="toggle" style="display:'.(get_pref('pane_ied_plugin_code_visible') ? 'block' : 'none').'">'
  773. .n. '<span class="ied_plugin_edit_toolbar">' . $msgpop . $phpdoc . sp . $codesub . '</span>'
  774. .n. '<div>' . $codeblock . '</div>'
  775. .n. '</div></div>'
  776. .n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_tp_strings_visible') ? ' expanded' : '').'"><a href="#ied_plugin_tp_strings">' . gTxt('ied_plugin_tp_legend') . ' <span id="ied_plugin_tp_count"></span></a></h3><div id="ied_plugin_tp_strings" class="toggle" style="display:'.(get_pref('pane_ied_plugin_tp_strings_visible') ? 'block' : 'none').'">'
  777. .n. '<div id="ied_plugin_tp_controls">'
  778. .n. '<label>'
  779. .n. gTxt('ied_plugin_tp_prefix')
  780. .n. '</label>'
  781. .n. fInput('text', 'ied_plugin_tp_prefix', $tp_pfx, '', '', '', '', '', 'ied_plugin_tp_prefix')
  782. .n. sp
  783. .n. $langsel
  784. .n. sp
  785. .n. fInput('hidden', 'ied_plugin_tp_lang_dflt', $dflt_lang, '', '', '', '', '', 'ied_plugin_tp_lang_dflt')
  786. .n. gTxt('ied_plugin_tp_populate').n.'</label>'.fInput('text', 'ied_plugin_tp_populate', '', '', '', '', '', '', 'ied_plugin_tp_populate') . sp . '<button id="ied_plugin_tp_load">'.gTxt('go').'</button>'
  787. .n. '<span id="ied_plugin_tp_load_count"></span>'
  788. .n. '</div>'
  789. .n. '<a href="#" id="ied_plugin_add_string">+</a>'
  790. .n. '<ul>'
  791. .n. join(n, $tp_strings)
  792. .n. '</ul></div></div>'
  793. .n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_docs_visible') ? ' expanded' : '').'"><a href="#ied_plugin_docs">'.gTxt('ied_plugin_docs_legend').'</a></h3><div id="ied_plugin_docs" class="toggle" style="display:'.(get_pref('pane_ied_plugin_docs_visible') ? 'block' : 'none').'">'
  794. .n. '<div>' . $help_widget . '</div>'
  795. .n. (($styleblock) ? '<div>' . gTxt( 'css' ) . $css_widget . '</div>' : '')
  796. .n. '</div></div>'
  797. .n. '<div>' . $sub . '</div>'
  798. .n. '<div class="summary-details"><h3 class="lever txp-summary'.(get_pref('pane_ied_plugin_utils_visible') ? ' expanded' : '').'"><a href="#ied_plugin_utils">'.gTxt('ied_plugin_utils_legend').'</a></h3><div id="ied_plugin_utils" class="toggle" style="display:'.(get_pref('pane_ied_plugin_utils_visible') ? 'block' : 'none').'">'
  799. .n. (($distblock) ? '<div>' . gTxt('ied_plugin_code_dist') . $distribution . '</div>' : '')
  800. .n. '<div>' . join(n, $op_langs) . '</div>'
  801. .n. '<div>' . $slink . '</div>'
  802. .n. '<div>' . $sziplink . '</div>'
  803. .n. '<div>' . $stxtlink . '</div>'
  804. .n. '<div>' . $sphplink . '</div>'
  805. .n. '</div></div>'
  806. .n. sInput('ied_plugin_save')
  807. .n. eInput($ied_pc_event)
  808. .n. hInput('name',$name)
  809. , '', '', 'post', 'ied_editForm').
  810. script_js(<<<EOF
  811. var ied_plugin_tp_total = 0;
  812. jQuery.fn.ied_plugin_resizehandle = function(curh) {
  813. return this.each(function() {
  814. var me = jQuery(this);
  815. me.animate({height: curh});
  816. me.after(
  817. jQuery('<div class="ied_plugin_resizehandle">--- + ---</div>').bind("mousedown", function(e) {
  818. var h = me.height();
  819. var y = e.clientY;
  820. var moveHandler = function(e) {
  821. me.height(Math.max(20, e.clientY + h - y));
  822. };
  823. var upHandler = function(e) {
  824. jQuery("html").unbind("mousemove",moveHandler).unbind("mouseup",upHandler);
  825. newh = me.height();
  826. setCookie('ied_plugin_edheight', newh, 365);
  827. };
  828. jQuery("html").bind("mousemove", moveHandler).bind("mouseup", upHandler);
  829. })
  830. );
  831. });
  832. }
  833. jQuery.fn.selectRange = function(start, end) {
  834. return this.each(function() {
  835. if(this.setSelectionRange) {
  836. this.focus();
  837. this.setSelectionRange(start, end);
  838. } else if(this.createTextRange) {
  839. var range = this.createTextRange();
  840. range.collapse(true);
  841. range.moveEnd('character', end);
  842. range.moveStart('character', start);
  843. range.select();
  844. }
  845. });
  846. };
  847. Array.prototype.unique = function () {
  848. var r = new Array();
  849. o:for(var i = 0, n = this.length; i < n; i++) {
  850. for(var x = 0, y = r.length; x < y; x++) {
  851. if(r[x]==this[i]) {
  852. continue o;
  853. }
  854. }
  855. r[r.length] = this[i];
  856. }
  857. return r;
  858. }
  859. function ied_goToLine() {
  860. var line = parseInt(jQuery('#ied_plugin_jumpToLine').val());
  861. var ied_ed = jQuery('#plugin_editor');
  862. var ied_edd = document.getElementById('plugin_editor'); // Dunno how to convert a jQuery obj back to DOM
  863. var lines = ied_ed.val().split('\\n');
  864. var numchars = 0;
  865. var count = 0;
  866. var findstr = '';
  867. jQuery.each(lines, function() {
  868. count++;
  869. if (count >= line) {
  870. findstr = this;
  871. return false;
  872. }
  873. numchars += (this.length)+2; // Don't ask. +2 is something to do with line endings I think
  874. });
  875. // Find the line containing the string we found. Start counting from the line before.
  876. // Those pesky line endings come into play again so we need to subtract the number
  877. // of lines found from the start character position *shrug*
  878. start = ied_ed.val().indexOf(findstr, numchars-count);
  879. start = (findstr == '') ? start+1 : start;
  880. end = start+findstr.length;
  881. ied_ed.selectRange(end-1, end);
  882. if(document.createEvent) {
  883. var ied_theCode = ied_ed.val().charCodeAt(end-1);
  884. if( window.KeyEvent ) {
  885. var ev = document.createEvent('KeyEvents');
  886. ev.initKeyEvent('keypress', false, true, window, false, false, false, false, 0, ied_theCode);
  887. } else {
  888. var ev = document.createEvent('UIEvents');
  889. ev.initUIEvent('keypress', false, true, window, 0);
  890. ev.keyCode = ied_theCode;
  891. }
  892. ied_edd.dispatchEvent(ev); // cause scroll to cursor by replacing last char with itself
  893. }
  894. ied_ed.selectRange(start, end);
  895. return false;
  896. }
  897. function ied_plugin_toggle_msgpop(state) {
  898. var obj = jQuery("#ied_plugin_msgpop");
  899. if (state != undefined) {
  900. if (state == 1) {
  901. obj.show('normal');
  902. } else {
  903. obj.hide('normal');
  904. }
  905. } else {
  906. obj.toggle('normal');
  907. }
  908. }
  909. function ied_plugin_rtrim(str, chars) {
  910. chars = chars || "\s";
  911. return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
  912. }
  913. function ied_plugin_update_tp_count() {
  914. var tp_count = tp_warns = 0;
  915. jQuery('#ied_plugin_tp_strings ul label').each(function() {
  916. tp_count++;
  917. if (jQuery(this).hasClass('warning')) {
  918. tp_warns++;
  919. }
  920. });
  921. jQuery('#ied_plugin_tp_count').empty().append('('+tp_count+ ' | '+tp_warns+ ' warnings)');
  922. // Update the global var for use when loading strings
  923. ied_plugin_tp_total = tp_count;
  924. }
  925. jQuery(function() {
  926. curh = getCookie('ied_plugin_edheight');
  927. curh = (curh == null) ? '480' : curh;
  928. jQuery("#plugin_editor").ied_plugin_resizehandle(parseInt(curh));
  929. jQuery('textarea[maxlength]').keyup(function(){
  930. var max = parseInt(jQuery(this).attr('maxlength'));
  931. if(jQuery(this).val().length > max){
  932. jQuery(this).val(jQuery(this).val().substr(0, jQuery(this).attr('maxlength')));
  933. }
  934. jQuery(this).parent().find('.ied_plugin_charsRemain').html('Chars remaining: '+ (max - jQuery(this).val().length));
  935. });
  936. jQuery('textarea[maxlength]').keyup();
  937. jQuery('#ied_plugin_jumpToLine').keydown(function(e) {
  938. var code = (e.keyCode ? e.keyCode : e.which);
  939. if(code == 13) {
  940. e.preventDefault();
  941. e.stopPropagation();
  942. ied_goToLine();
  943. return false;
  944. }
  945. });
  946. // Store the prefix
  947. jQuery('#ied_plugin_tp_prefix').blur(function() {
  948. var pfx = jQuery(this).val();
  949. sendAsyncEvent(
  950. {
  951. event: textpattern.event,
  952. step: 'ied_plugin_set_tp_prefix',
  953. plugin: '{$name}',
  954. prefix: pfx
  955. });
  956. });
  957. // Find all occurrences of gTxt('something')
  958. jQuery('#plugin_editor, #ied_plugin_tp_prefix').blur(function() {
  959. var ied_tp_pfx = jQuery('#ied_plugin_tp_prefix').val();
  960. if (ied_tp_pfx != '') {
  961. var ied_gtxt_re = /gTxt\([\'\"]([a-zA-Z0-9_]*?)[\"\'][,\)]/gi;
  962. var ied_tp_ta = jQuery('#plugin_editor').val().replace(/\s*/g,''); // Strip spaces to make lookups easier
  963. var ied_tp_items = ied_tp_ta.match(ied_gtxt_re);
  964. // if JS RegExp captured parenthical expressions in global searches or it was easy to inject variables
  965. // into new RegExp() calls, this loop could be avoided
  966. var ied_tp_used = [];
  967. for (var idx = 0; idx < ied_tp_items.length; idx++) {
  968. var pos = ied_tp_items[idx].lastIndexOf("'");
  969. pos = (pos == -1) ? ied_tp_items[idx].lastIndexOf('"') : pos;
  970. tpstr = ied_tp_items[idx].substr(6,pos-6);
  971. if (tpstr.indexOf(ied_tp_pfx) == 0) {
  972. ied_tp_used[ied_tp_used.length] = tpstr;
  973. }
  974. }
  975. ied_tp_used = ied_tp_used.unique();
  976. // List of all current textpack strings in use (as of last Save operation)
  977. var ied_tp_curr = [];
  978. jQuery('#ied_plugin_tp_strings label').each(function() {
  979. ied_tp_curr[ied_tp_curr.length] = jQuery(this).text();
  980. });
  981. // Iterate over current array and check if each name is in the used textpack item list.
  982. // If it is, remove it from the final list.
  983. for (var idx = 0; idx < ied_tp_curr.length; idx++) {
  984. if ((pos = jQuery.inArray(ied_tp_curr[idx], ied_tp_used)) > -1) {
  985. ied_tp_used.splice(pos, 1);
  986. jQuery('#ied_plugin_tp_strings ul label:contains('+ied_tp_curr[idx]+')').toggleClass('warning', false).next(".ied_plugin_xbtn").remove();
  987. } else {
  988. setclass = 1;
  989. jQuery('#ied_plugin_tp_strings ul label:contains('+ied_tp_curr[idx]+')').toggleClass('warning', true).next(".ied_plugin_xbtn").remove().end().after('<a href="#" class="ied_plugin_xbtn">[x]</a>');
  990. }
  991. }
  992. // For each remaining item that has been used, add an input box
  993. // TODO: i18n the select options
  994. for (var idx = 0; idx < ied_tp_used.length; idx++) {
  995. jQuery('#ied_plugin_tp_strings ul').prepend('<li><input type="text" name="textpack_'+ied_tp_used[idx]+'" value="" /> <select name="ied_plugin_tp_event"><option value="admin">Admin</option><option value="public">Public</option><option value="common">Both</option></select> <label>'+ied_tp_used[idx]+'</label></li>');
  996. }
  997. ied_plugin_update_tp_count();
  998. }
  999. }).blur();
  1000. // Handle adding new strings manually
  1001. jQuery('#ied_plugin_add_string').click(function(ev) {
  1002. jQuery('#ied_plugin_tp_strings ul').before('<div id="ied_plugin_new_container"><label>'+jQuery('#ied_plugin_tp_prefix').val()+'_<input type="text" name="ied_plugin_tp_newname" id="ied_plugin_tp_newname" value="" /></label></div>');
  1003. jQuery('#ied_plugin_tp_newname').focus();
  1004. ev.preventDefault();
  1005. });
  1006. jQuery(document).on('blur', '#ied_plugin_tp_newname', function() {
  1007. var newname = ied_plugin_rtrim(jQuery('#ied_plugin_tp_prefix').val()+'_'+jQuery('#ied_plugin_tp_newname').val(), '_');
  1008. var newok = true;
  1009. jQuery('#ied_plugin_tp_strings ul li label').each(function() {
  1010. if (jQuery(this).text() == newname) {
  1011. jQuery('#ied_plugin_tp_newname').css('color', '#E00');
  1012. newok = false;
  1013. }
  1014. });
  1015. // TODO: i18n select option text
  1016. if (newok) {
  1017. jQuery('#ied_plugin_tp_strings ul').prepend('<li><input type="text" name="textpack_'+newname+'" value="" /> <select name="ied_plugin_tp_event"><option value="admin">Admin</option><option value="public">Public</option><option value="common">Both</option></select> <label>'+newname+'</label></li>');
  1018. jQuery('#ied_plugin_new_container').remove();
  1019. jQuery('input[name="textpack_'+newname+'"]').focus();
  1020. }
  1021. ied_plugin_update_tp_count();
  1022. });
  1023. // Initialise the generic AJAX error handler
  1024. jQuery('.ied_editForm').ajaxError(function(event, request, settings) {
  1025. var xhr = jQuery(request.responseText);
  1026. // phpdoc generation barfed
  1027. if (settings.data.indexOf('step=ied_plugin_generate_phpdoc') > -1) {
  1028. var msgContent = jQuery("#ied_plugin_msgpop .ied_plugin_msgpop_content");
  1029. status = xhr.find('http-status').attr('value')
  1030. if (status == '200 OK') {
  1031. msgContent.append(xhr.find('ied_plugin_phpdoc').attr('value'));
  1032. } else if (status == '501 Not Implemented') {
  1033. msgContent.append(xhr.find('error_msg').attr('value'));
  1034. }
  1035. ied_plugin_toggle_msgpop('1');
  1036. }
  1037. // code save barfed
  1038. if (settings.data.indexOf('step=ied_plugin_code_save') > -1) {
  1039. var msg = xhr.find('ied_plugin_msg').attr('value');
  1040. var line = xhr.find('ied_plugin_err_line').attr('value');
  1041. jQuery('#ied_plugin_jumpToLine').val(line);
  1042. ied_goToLine();
  1043. var codeobj = jQuery("#plugin_editor");
  1044. codeobj.css({'opacity': '.75'}); // Reduced opacity as a visual cue that something's wrong
  1045. eval(msg); // bleurgh!
  1046. }
  1047. });
  1048. // Handle 'x' button
  1049. jQuery(document).on('click', '.ied_plugin_xbtn', function(event) {
  1050. var elem = jQuery(this).prev('label');
  1051. var tp_lbl = elem.text();
  1052. sendAsyncEvent(
  1053. {
  1054. event: textpattern.event,
  1055. step: 'ied_plugin_textpack_del',
  1056. ied_tp_lbl: tp_lbl
  1057. });
  1058. elem.parent().remove();
  1059. event.preventDefault();
  1060. ied_plugin_update_tp_count();
  1061. });
  1062. // Save textpack string to database
  1063. function ied_plugin_tp_save(event) {
  1064. var elem = jQuery(this);
  1065. var isSel = elem.is('select');
  1066. var tp_lbl = elem.nextAll('label').text();
  1067. var tp_str = (isSel) ? elem.prevAll('input').val() : elem.val();
  1068. var tp_ev = (isSel) ? elem.val() : elem.nextAll('select').val();
  1069. var tp_evt = (tp_ev=='public' || tp_ev=='common') ? tp_ev : jQuery('#ied_plugin_tp_prefix').val();
  1070. var tp_lng = jQuery('#ied_plugin_tp_lang').val();
  1071. sendAsyncEvent(
  1072. {
  1073. event: textpattern.event,
  1074. step: 'ied_plugin_textpack_save',
  1075. ied_tp_evt: tp_evt,
  1076. ied_tp_lbl: tp_lbl,
  1077. ied_tp_lng: tp_lng,
  1078. ied_tp_str: tp_str
  1079. });
  1080. }
  1081. // Handle saving textpack string
  1082. jQuery(document).on('blur', '#ied_plugin_tp_strings ul li input', ied_plugin_tp_save);
  1083. jQuery(document).on('change', '#ied_plugin_tp_strings ul li select', ied_plugin_tp_save);
  1084. // Handle language change
  1085. jQuery("#ied_plugin_tp_lang").change(function(event) {
  1086. jQuery('#ied_plugin_tp_load_count').empty().show();
  1087. var tp_lng = jQuery(this).val();
  1088. var tp_dflt = jQuery('#ied_plugin_tp_lang_dflt').val();
  1089. var sel = '#ied_plugin_tp_strings ul li';
  1090. var numStrings = sel.length;
  1091. var numFetched = 0;
  1092. jQuery(sel).each(function() {
  1093. var obj = jQuery(this);
  1094. var tp_lbl = obj.find('label').text();
  1095. var tp_dest = obj.find('input');
  1096. sendAsyncEvent(
  1097. {
  1098. event: textpattern.event,
  1099. step: 'ied_plugin_textpack_get',
  1100. ied_tp_lbl: tp_lbl,
  1101. ied_tp_lng: tp_lng,
  1102. ied_tp_dflt: tp_dflt
  1103. }, function(data) {
  1104. numFetched++;
  1105. var theVal = data.ied_plugin_tp_string;
  1106. var xl8str = data.ied_plugin_tp_dflt;
  1107. tp_dest.val(theVal);
  1108. if (xl8str == undefined || xl8str == '') {
  1109. obj.removeAttr('title');
  1110. } else {
  1111. obj.attr('title', xl8str);
  1112. }
  1113. if (numFetched < ied_plugin_tp_total) {
  1114. jQuery('#ied_plugin_tp_load_count').text(numFetched + '/' + ied_plugin_tp_total);
  1115. } else {
  1116. jQuery('#ied_plugin_tp_load_count').text('OK').hide('slow');
  1117. }
  1118. },
  1119. 'json');
  1120. });
  1121. });
  1122. // Current language refresh
  1123. jQuery('#ied_plugin_tp_refresh').click(function() {
  1124. // Trigger the change event
  1125. jQuery("#ied_plugin_tp_lang").change();
  1126. });
  1127. // Load textpack strings from plugin's custom gTxt()
  1128. jQuery("#ied_plugin_tp_load").click(function(event) {
  1129. var ied_fn = jQuery("#ied_plugin_tp_populate").val();
  1130. jQuery('#ied_plugin_tp_strings ul li').each(function() {
  1131. var obj = jQuery(this);
  1132. var tp_lbl = obj.find('label').text();
  1133. var tp_dest = obj.find('input');
  1134. sendAsyncEvent(
  1135. {
  1136. event: textpattern.event,
  1137. step: 'ied_plugin_textpack_load',
  1138. ied_tp_fn: ied_fn,
  1139. ied_tp_lbl: tp_lbl
  1140. }, function(data) {
  1141. // Paste the returned string into the input box and save it by invoking blur()
  1142. tp_dest.val(data.ied_plugin_tp_string).blur();
  1143. },
  1144. 'json');
  1145. });
  1146. event.preventDefault();
  1147. });
  1148. // Handle saving code
  1149. jQuery("#ied_plugin_code_save").click(function(event) {
  1150. var msgarea = jQuery("#ied_plugin_messages");
  1151. msgarea.empty();
  1152. var codeobj = jQuery("#plugin_editor");
  1153. var codeblock = codeobj.val();
  1154. var plugin = '{$name}';
  1155. codeobj.css('opacity', '0.4');
  1156. sendAsyncEvent(
  1157. {
  1158. event: textpattern.event,
  1159. step: 'ied_plugin_code_save',
  1160. plugin: plugin,
  1161. codeblock: codeblock
  1162. }, function(data) {
  1163. codeobj.css({'opacity': '1'});
  1164. var msg = jQuery(data).find('ied_plugin_msg').attr('value');
  1165. eval(msg); // yuk!
  1166. });
  1167. event.preventDefault();
  1168. });
  1169. // Handle generating phpdoc
  1170. jQuery("#ied_plugin_btn_phpdoc").click(function(event) {
  1171. var msgarea = jQuery("#ied_plugin_msgpop");
  1172. var msgContent = jQuery("#ied_plugin_msgpop .ied_plugin_msgpop_content");
  1173. msgContent.empty();
  1174. var fnobj = jQuery("#ied_plugin_to_phpdoc");
  1175. var fn = fnobj.val();
  1176. var plugin = '{$name}';
  1177. sendAsyncEvent(
  1178. {
  1179. event: textpattern.event,
  1180. step: 'ied_plugin_generate_phpdoc',
  1181. plugin: plugin,
  1182. fn: fn
  1183. }, function(data) {
  1184. msgContent.append(jQuery(data).find('ied_plugin_phpdoc').attr('value'));
  1185. ied_plugin_toggle_msgpop('1');
  1186. });
  1187. event.preventDefault();
  1188. });
  1189. jQuery('#ied_plugin_tp_oplangs').change(function() {
  1190. sel = jQuery('#ied_plugin_tp_oplangs option:selected').map(function(){ return this.value }).get().join(', ');
  1191. sendAsyncEvent(
  1192. {
  1193. event: textpattern.event,
  1194. step: 'ied_plugin_lang_set',
  1195. ied_tp_langsel: sel
  1196. });
  1197. });
  1198. });
  1199. EOF
  1200. );
  1201. }
  1202. // -------------------------------------------------------------
  1203. function ied_plugin_save() {
  1204. global $ied_plugin_globals, $prefs;
  1205. extract(doSlash(gpsa(array('name','newname','filename','code','author','author_uri','version','description','help','css','status','type','load_order','rename_file','restore_point','ied_plugin_tp_prefix'))));
  1206. $flags = gps('flags');
  1207. list ($start_css, $end_css) = ied_plugin_make_markers("CSS", $ied_plugin_globals['css_start'], $ied_plugin_globals['css_end']);
  1208. $extraMsg = $newfilename = $msg1 = $msg2 = '';
  1209. $newname = trim($newname);
  1210. if ($flags) {
  1211. $flagout = 0;
  1212. foreach ($flags as $flag) {
  1213. $flagout |= $flag;
  1214. }
  1215. $flags = $flagout;
  1216. }
  1217. if (empty($newname)) {
  1218. $msg1 = gTxt('ied_plugin_name_first');
  1219. $msgType = E_ERROR;
  1220. } else {
  1221. if (empty($filename)) {
  1222. $hout = array();
  1223. $hout[0] = $help;
  1224. if ($css) { $hout[1] = n.$start_css.n.$css.n.$end_css; }
  1225. safe_update(
  1226. 'txp_plugin',
  1227. "name='".$newname."',
  1228. status = ".intval($status).",
  1229. type = ".intval($type).",
  1230. author = '".$author."',
  1231. author_uri = '".$author_uri."',
  1232. version = '".$version."',
  1233. description = '".$description."',
  1234. help = '".join('',$hout)."',
  1235. code = '".$code."',
  1236. flags = ".intval($flags).",
  1237. ".(($restore_point == 1)? "code_restore = '".$code."'," : '')."
  1238. load_order = ".$load_order,
  1239. "name = '".$name."'");
  1240. $msg1 = gTxt('plugin_saved', array('{name}' => $newname));
  1241. $msgType = '';
  1242. } else {
  1243. $dir = $prefs['plugin_cache_dir'].DS;
  1244. if (file_exists($dir.$filename)) {
  1245. $filecontent = file($dir.$filename);
  1246. } else {
  1247. if (empty($prefs['plugin_cache_dir'])) {
  1248. $filecontent = '';
  1249. $msg2 = gTxt('ied_plugin_cache_not_set');
  1250. } else {
  1251. $oporder = (isset($prefs['ied_plugin_output_order']) && is_numeric($prefs['ied_plugin_output_order'])) ? $prefs['ied_plugin_output_order'] : 0;
  1252. $helpchunk = ied_plugin_build_template('help', array($help, ' ')); // Use a space to force a CSS hunk in the file
  1253. $codechunk = ied_plugin_build_template('code', $code);
  1254. $filecontent = ied_plugin_build_template("preamble")
  1255. .ied_plugin_build_template("name", $newname)
  1256. .ied_plugin_build_template("html_help")
  1257. .ied_plugin_build_template("version", $version)
  1258. .ied_plugin_build_template("author", $author)
  1259. .ied_plugin_build_template("author_uri", $author_uri)
  1260. .ied_plugin_build_template("description", $description)
  1261. .ied_plugin_build_template("load_order", $load_order)
  1262. .ied_plugin_build_template("type", $type)
  1263. .ied_plugin_build_template("flags", $flags)
  1264. .ied_plugin_build_template("include")
  1265. .(($oporder == 0) ? $codechunk : $helpchunk)
  1266. .(($oporder == 1) ? $codechunk : $helpchunk)
  1267. .ied_plugin_build_template("postamble");
  1268. $msg1 = gTxt('ied_plugin_edit_new');
  1269. $msgType = '';
  1270. }
  1271. }
  1272. $filecontent = ied_plugin_make_array($filecontent);
  1273. $metavars = array("name" => "$newname",
  1274. "version" => "$version",
  1275. "author" => "$author",
  1276. "author_uri" => "$author_uri",
  1277. "description" => "$description",
  1278. "type" => "$type",
  1279. "flags" => "$flags",
  1280. "order" => "$load_order",
  1281. );
  1282. $hunkvars = array("CODE" => doStrip(str_replace('\r\n','
  1283. ',$code)), // newline workaround
  1284. "HELP" => doStrip(str_replace('\r\n','
  1285. ',$help)),
  1286. "CSS" => doStrip(str_replace('\r\n','
  1287. ',$css)),
  1288. );
  1289. foreach ($metavars as $varname => $value) {
  1290. for($idx = 0; $idx < count($filecontent); $idx++) {
  1291. if (strpos($filecontent[$idx], '$plugin[\''.$varname.'\']') === 0) {
  1292. $filecontent[$idx] = '$plugin[\''.$varname.'\'] = '.doQuote($value).';';
  1293. break;
  1294. }
  1295. }
  1296. }
  1297. foreach ($hunkvars as $varname => $hunk) {
  1298. list ($start_delim, $end_delim) = ied_plugin_make_markers($varname, $ied_plugin_globals['dlm_start'], $ied_plugin_globals['dlm_end']);
  1299. $start = array_search($start_delim, $filecontent) + 1;
  1300. $end = array_search($end_delim, $filecontent);
  1301. if (is_numeric($start) && is_numeric($end) && $end >= $start) {
  1302. array_splice($filecontent, $start, $end-$start, $hunk);
  1303. }
  1304. }
  1305. $filecontent = implode("\n", $filecontent);
  1306. if ($filecontent) {
  1307. $fh = fopen($dir.$filename, 'w+');
  1308. fwrite($fh, $filecontent);
  1309. fclose($fh);
  1310. }
  1311. $msg1 = (empty($msg1)) ? gTxt('plugin_saved', array('{name}' => $newname)) : $msg1;
  1312. $msgType = '';
  1313. // Make new file if required
  1314. if ($rename_file == 1) {
  1315. $fnames = ied_plugin_get_name($newname, $version);
  1316. $newfilename = $fnames[2];
  1317. $res = rename($dir.$filename, $dir.$newfilename);
  1318. $extraMsg = ($res) ? gTxt('ied_plugin_renamed') : gTxt('ied_plugin_rename_failed');
  1319. }
  1320. }
  1321. // Store the plugin textpack prefix
  1322. ied_plugin_set_tp_prefix($newname, $ied_plugin_tp_prefix);
  1323. }
  1324. if ($msg2) {
  1325. ied_plugin_table($msg2);
  1326. } else {
  1327. // Check the plugin type matches the code used
  1328. $extraMsg .= (ied_plugin_admin_check($code, $type)) ? '' : strong(gTxt('ied_plugin_check_type'));
  1329. $message = $msg1.$extraMsg;
  1330. ied_plugin_edit(array($message, $msgType), $newfilename);
  1331. }
  1332. }
  1333. // -------------------------------------------------------------
  1334. function ied_plugin_save_as_file() {
  1335. global $prefs, $ied_plugin_globals;
  1336. if (gps('name')) {
  1337. $name = gps('name');
  1338. $rs = safe_row('description, author, author_uri, version, code, help, type, load_order, flags', 'txp_plugin', "name='".doSlash($name)."'");
  1339. extract($rs);
  1340. list($css,$help) = ($help) ? ied_plugin_extract_hunk($help, "CSS", "<!--|-->", true) : array('',$help);
  1341. } elseif (gps('filename')) {
  1342. $plugin=ied_plugin_read_file($prefs['plugin_cache_dir'].DS.gps('filename'));
  1343. extract($plugin);
  1344. }
  1345. $zip = gps('type');
  1346. if (gps('trim')==1) {
  1347. $code=explode("\r\n",$code);
  1348. $code=array_map('trim',$code);
  1349. $code=implode("\r\n",$code);
  1350. }
  1351. // Get any textpack strings
  1352. $textpack = ied_plugin_textpack_build($name);
  1353. list ($start_css, $end_css) = ied_plugin_make_markers("CSS", $ied_plugin_globals['css_start'], $ied_plugin_globals['css_end']);
  1354. $fnames = ied_plugin_get_name($name, $version);
  1355. header('Content-type: text/plain');
  1356. header('Content-Disposition: attachment; filename=' . (($zip === 'zip') ? $fnames[1] : $fnames[0]));
  1357. $types = array('Public' , 'Admin/Public' , 'Library' , 'Admin', 'Admin', 'Admin/Public'); // No gTxt() because the template is English
  1358. $plugin['name'] = $name;
  1359. $plugin['author'] = $author;
  1360. $plugin['author_uri'] = $author_uri;
  1361. $plugin['version'] = $version;
  1362. $plugin['description'] = $description;
  1363. $plugin['code'] = $code;
  1364. $plugin['type'] = $type;
  1365. $plugin['order'] = $load_order;
  1366. $plugin['flags'] = $flags;
  1367. // $plugin['allow_html_help'] = true;
  1368. // $plugin['help_raw'] = $help.$start_css.$css.$end_css;
  1369. $plugin['help'] = ied_plugin_textile($name, $help, $css, $start_css, $end_css);
  1370. $plugin['md5'] = md5( $plugin['code'] );
  1371. if ($textpack) {
  1372. $plugin['textpack'] = $textpack;
  1373. }
  1374. echo '# Name: '.$name.' v'.$version.' '.(($zip === 'zip') ? "(compressed)" : "").'
  1375. # Type: '.$types[$type].' plugin
  1376. # '.$description.'
  1377. # Author: '.$author.'
  1378. # URL: '.$author_uri.'
  1379. # Recommended load order: '.$load_order.'
  1380. # .....................................................................
  1381. # This is a plugin for Textpattern CMS - http://textpattern.com/
  1382. # To install: textpattern > admin > plugins
  1383. # Paste the following text into the \'Install plugin\' box:
  1384. # .....................................................................
  1385. '.(($zip === 'zip') ? chunk_split(base64_encode(gzencode(serialize($plugin))), 72) : chunk_split(base64_encode(serialize($plugin)), 72));
  1386. die();
  1387. }
  1388. // -------------------------------------------------------------
  1389. function ied_plugin_save_as_php_file() {
  1390. global $prefs;
  1391. if (gps('name')) {
  1392. $name = gps('name');
  1393. $rs = safe_row("description, author, author_uri, version, code, help, type, load_order, flags", "txp_plugin", "name='".doSlash($name)."'");
  1394. extract($rs);
  1395. list($css,$help) = ($help) ? ied_plugin_extract_hunk($help, "CSS", "<!--|-->", true) : array('',$help);
  1396. } elseif (gps('filename')) {
  1397. $plugin=ied_plugin_read_file($prefs['plugin_cache_dir'].DS.gps('filename'));
  1398. extract($plugin);
  1399. }
  1400. $oporder = (isset($prefs['ied_plugin_output_order']) && is_numeric($prefs['ied_plugin_output_order'])) ? $prefs['ied_plugin_output_order'] : 0;
  1401. $fnames = ied_plugin_get_name($name, $version);
  1402. header('Content-type: text/php');
  1403. header('Content-Disposition: attachment; filename=' .$fnames[2]);
  1404. $textpack = ied_plugin_textpack_build($name);
  1405. $helpchunk = ied_plugin_build_template('help', array($help, $css));
  1406. $codechunk = ied_plugin_build_template('code', str_replace("\r\n","\n",$code));
  1407. echo ied_plugin_build_template('preamble').
  1408. ied_plugin_build_template('name', $name).
  1409. ied_plugin_build_template('html_help').
  1410. ied_plugin_build_template('version', $version).
  1411. ied_plugin_build_template('author', $author).
  1412. ied_plugin_build_template('author_uri', $author_uri).
  1413. ied_plugin_build_template('description', $description).
  1414. ied_plugin_build_template('load_order', $load_order).
  1415. ied_plugin_build_template('type', $type).
  1416. ied_plugin_build_template('flags', $flags).
  1417. ied_plugin_build_template('textpack', $textpack).
  1418. ied_plugin_build_template('include').
  1419. (($oporder == 0) ? $codechunk : $helpchunk).
  1420. (($oporder == 1) ? $codechunk : $helpchunk).
  1421. ied_plugin_build_template('postamble');
  1422. die();
  1423. }
  1424. // -------------------------------------------------------------
  1425. function ied_plugin_save_as_textpack() {
  1426. global $prefs;
  1427. if (gps('name')) {
  1428. $name = gps('name');
  1429. $version = safe_field('version', "txp_plugin", "name='".doSlash($name)."'");
  1430. } elseif (gps('filename')) {
  1431. $name = gps('filename');
  1432. $plugin=ied_plugin_read_file($prefs['plugin_cache_dir'].DS.gps('filename'));
  1433. $version = $plugin['version'];
  1434. }
  1435. $langs = gps('lang');
  1436. if (!$langs) {
  1437. $langs = get_pref('ied_plugin_lang_selected', '');
  1438. }
  1439. $force = 0;
  1440. if ($langs == '') {
  1441. $langstr = 'all';
  1442. $force = 1;
  1443. } else {
  1444. $langlist = do_list($langs);
  1445. if (count($langlist) == 1) {
  1446. $langstr = $langlist[0];
  1447. } else {
  1448. $country_codes = array();
  1449. foreach($langlist as $ln) {
  1450. $lparts = do_list($ln, '-');
  1451. $country_codes[] = $lparts[0];
  1452. }
  1453. $langstr = join('+', array_unique($country_codes));
  1454. }
  1455. }
  1456. $textpack = ied_plugin_textpack_build($name, $force);
  1457. $fnames = ied_plugin_get_name($name, $version, $langstr);
  1458. header('Content-type: text/html; charset=UTF-8');
  1459. header('Content-Disposition: attachment; filename=' . $fnames[3]);
  1460. echo $textpack;
  1461. die();
  1462. }
  1463. // -------------------------------------------------------------
  1464. function ied_plugin_build_template($prt, $val='') {
  1465. $css = $help = '';
  1466. if (is_array($val)) {
  1467. $help = $val[0];
  1468. $css = isset($val[1]) ? $val[1] : '';
  1469. $val = '';
  1470. }
  1471. $template = array(
  1472. "preamble" => '<?php'.n.n
  1473. .'// This is a PLUGIN TEMPLATE for Textpattern CMS.'.n.n
  1474. .'// Copy this file to a new name like abc_myplugin.php. Edit the code, then'.n
  1475. .'// run this file at the command line to produce a plugin for distribution:'.n
  1476. .'// $ php abc_myplugin.php > abc_myplugin-0.1.txt'.n.n,
  1477. "name" => '// Plugin name is optional. If unset, it will be extracted from the current'.n
  1478. .'// file name. Plugin names should start with a three letter prefix which is'.n
  1479. .'// unique and reserved for each plugin author ("abc" is just an example).'.n
  1480. .'// Uncomment and edit this line to override:'.n
  1481. .'$plugin[\'name\'] = '.doQuote(doSlash($val)).';'.n.n,
  1482. "html_help" => '// Allow raw HTML help, as opposed to Textile.'.n
  1483. .'// 0 = Plugin help is in Textile format, no raw HTML allowed (default).'.n
  1484. .'// 1 = Plugin help is in raw HTML. Not recommended.'.n
  1485. .'# $plugin[\'allow_html_help\'] = 1;'.n.n,
  1486. "version" => '$plugin[\'version\'] = '.doQuote($val).';'.n,
  1487. "flags" => '// Plugin "flags" signal the presence of optional capabilities to the core plugin loader.'.n
  1488. .'// Use an appropriately OR-ed combination of these flags.'.n
  1489. .'// The four high-order bits 0xf000 are available for this plugin\'s private use'.n
  1490. .'if (!defined(\'PLUGIN_HAS_PREFS\')) define(\'PLUGIN_HAS_PREFS\', 0x0001); // This plugin wants to receive "plugin_prefs.{$plugin[\'name\']}" events'.n
  1491. .'if (!defined(\'PLUGIN_LIFECYCLE_NOTIFY\')) define(\'PLUGIN_LIFECYCLE_NOTIFY\', 0x0002); // This plugin wants to receive "plugin_lifecycle.{$plugin[\'name\']}" events'.n.n
  1492. .'$plugin[\'flags\'] = '.doQuote($val).';'.n.n,
  1493. "textpack" => '// Plugin \'textpack\' is optional. It provides i18n strings to be used in conjunction with gTxt().'.n
  1494. .'// Syntax:'.n
  1495. .'// ## arbitrary comment'.n
  1496. .'// #@event'.n
  1497. .'// #@language ISO-LANGUAGE-CODE'.n
  1498. .'// abc_string_name => Localized String'.n.n
  1499. .(($val)
  1500. ? '$plugin[\'textpack\'] = <<<EOT'.n
  1501. .$val.n
  1502. .'EOT;'.n.n
  1503. : '/** Uncomment me, if you need a textpack'.n
  1504. .'$plugin[\'textpack\'] = <<<EOT'.n
  1505. .'#@admin'.n
  1506. .'#@language en-gb'.n
  1507. .'abc_sample_string => Sample String'.n
  1508. .'abc_one_more => One more'.n
  1509. .'#@language de-de'.n
  1510. .'abc_sample_string => Beispieltext'.n
  1511. .'abc_one_more => Noch einer'.n
  1512. .'EOT;'.n
  1513. .'**/'.n
  1514. .'// End of textpack'.n.n),
  1515. "author" => '$plugin[\'author\'] = '.doQuote(doSlash($val)).';'.n,
  1516. "author_uri" => '$plugin[\'author_uri\'] = '.doQuote(doSlash($val)).';'.n,
  1517. "description" => '$plugin[\'description\'] = '.doQuote(doSlash($val)).';'.n.n,
  1518. "load_order" => '// Plugin load order:'.n
  1519. .'// The default value of 5 would fit most plugins, while for instance comment'.n
  1520. .'// spam evaluators or URL redirectors would probably want to run earlier'.n
  1521. .'// (1...4) to prepare the environment for everything else that follows.'.n
  1522. .'// Values 6...9 should be considered for plugins which would work late.'.n
  1523. .'// This order is user-overrideable.'.n
  1524. .'$plugin[\'order\'] = '.doQuote($val).';'.n.n,
  1525. "type" => '// Plugin \'type\' defines where the plugin is loaded'.n
  1526. .'// 0 = public : only on the public side of the website (default)'.n
  1527. .'// 1 = public+admin : on both the public and admin side'.n
  1528. .'// 2 = library : only when include_plugin() or require_plugin() is called'.n
  1529. .'// 3 = admin : only on the admin side (no AJAX)'.n
  1530. .'// 4 = admin+ajax : only on the admin side (AJAX supported)'.n
  1531. .'// 5 = public+admin+ajax : on both the public and admin side (AJAX supported)'.n
  1532. .'$plugin[\'type\'] = '.doQuote($val).';'.n.n,
  1533. "include" => 'if (!defined(\'txpinterface\'))'.n
  1534. .' @include_once(\'zem_tpl.php\');'.n.n,
  1535. "help" => 'if (0) {'.n
  1536. .'?>'.n
  1537. . (($css) ? '<!--'.n
  1538. .'# --- BEGIN PLUGIN CSS ---'.n
  1539. .$css.n
  1540. .'# --- END PLUGIN CSS ---'.n
  1541. .'-->'.n : '')
  1542. .'<!--'.n
  1543. .'# --- BEGIN PLUGIN HELP ---'.n
  1544. .$help.n
  1545. .'# --- END PLUGIN HELP ---'.n
  1546. .'-->'.n
  1547. .'<?php'.n
  1548. .'}'.n,
  1549. "code" => '# --- BEGIN PLUGIN CODE ---'.n
  1550. .$val.n
  1551. .'# --- END PLUGIN CODE ---'.n,
  1552. "postamble" => '?>',
  1553. );
  1554. return (array_key_exists($prt, $template) ? $template[$prt] : '');
  1555. }
  1556. // -------------------------------------------------------------
  1557. // Create / upload / install a new plugin
  1558. function ied_plugin_create() {
  1559. global $ied_plugin_globals;
  1560. extract(doSlash(gpsa(array('name', 'plugin_create', 'plugin_upload', 'plugin_install', 'ied_plugin_autoenable'))));
  1561. if ($plugin_create) {
  1562. $fname = '';
  1563. if ($name == '') {
  1564. ied_plugin_table(array(gTxt('ied_plugin_name_first'), E_ERROR));
  1565. return;
  1566. }
  1567. if (strpos($name, ".php") !== false) {
  1568. $fname = $name;
  1569. $name = basename($name, ".php");
  1570. }
  1571. $exists = fetch('name', 'txp_plugin', 'name', $name);
  1572. // MySQL defaults to set status=1 so we need to explicitly override this if this
  1573. // is a fresh installation
  1574. $state = ($ied_plugin_autoenable==1) ? 1 : 0;
  1575. if (!$exists) {
  1576. if ($fname) {
  1577. // Put plugin in cache dir by faking a POST submission and saving
  1578. $_POST['filename'] = $fname;
  1579. $_POST['newname'] = $name;
  1580. $_POST['status'] = 1; // Cache_dir plugins are always on so this setting is ignored
  1581. $_POST['type'] = 0;
  1582. $_POST['version'] = '0.1';
  1583. $_POST['load_order'] = 5;
  1584. $_POST['flags'] = 0;
  1585. ied_plugin_save();
  1586. } else {
  1587. safe_insert("txp_plugin", "
  1588. name='$name',
  1589. status=$state,
  1590. type=0,
  1591. version='0.1',
  1592. load_order=5,
  1593. flags=0,
  1594. description='',
  1595. help='',
  1596. code='',
  1597. code_restore=''
  1598. ");
  1599. ied_plugin_edit(gTxt('ied_plugin_edit_new'), $name);
  1600. }
  1601. } else {
  1602. ied_plugin_table(array(gTxt('plugin').' <strong>'.$name.'</strong> '.gTxt('already_exists'), E_ERROR));
  1603. }
  1604. } else if ($plugin_upload) {
  1605. list ($start_css, $end_css) = ied_plugin_make_markers("CSS", $ied_plugin_globals['css_start'], $ied_plugin_globals['css_end']);
  1606. if (!$_FILES['thefile']['tmp_name']) {
  1607. ied_plugin_table(array(gTxt('ied_plugin_choose_file'), E_ERROR));
  1608. return;
  1609. }
  1610. $info = explode ('.',$_FILES['thefile']['name']);
  1611. $lastpart = count($info)-1;
  1612. $ext = $info[$lastpart];
  1613. if ($ext == 'php') {
  1614. $plugin = ied_plugin_read_file($_FILES['thefile']['tmp_name']);
  1615. // $newname = (empty($plugin['name'])) ? basename($_FILES['thefile']['name'], '.php') : $plugin['name'];
  1616. $newname = ($name) ? $name : doSlash($plugin['name']);
  1617. if (empty($plugin['code'])) {
  1618. $plugin['code'] = file_get_contents($_FILES['thefile']['tmp_name']);
  1619. }
  1620. extract(doSlash($plugin));
  1621. $md5 = md5($code);
  1622. $version = ($version) ? $version : "0.1";
  1623. $help = $help.(($css) ? n.$start_css.n.$css.n.$end_css : '');
  1624. $exists = fetch('name', 'txp_plugin', 'name', $newname);
  1625. // MySQL defaults to set status=1 so we need to explicitly override this if this
  1626. // is a fresh installation
  1627. $state = ($ied_plugin_autoenable==1) ? 'status=1,' : ($exists && ($ied_plugin_autoenable==2) ? '' : 'status=0,');
  1628. if ($exists) {
  1629. // Note: status omitted so it retains its value
  1630. $rs = safe_update(
  1631. "txp_plugin",
  1632. "$state
  1633. type = ".intval($type).",
  1634. author = '$author',
  1635. author_uri = '$author_uri',
  1636. version = '$version',
  1637. description = '$description',
  1638. help = '$help',
  1639. code = '$code',
  1640. code_restore = '$code',
  1641. code_md5 = '$md5',
  1642. flags = ".intval($flags).",
  1643. load_order = ".intval($load_order),
  1644. "name = '$newname'"
  1645. );
  1646. } else {
  1647. $rs = safe_insert(
  1648. "txp_plugin",
  1649. "name = '$newname',
  1650. $state
  1651. type = ".intval($type).",
  1652. author = '$author',
  1653. author_uri = '$author_uri',
  1654. version = '$version',
  1655. description = '$description',
  1656. help = '$help',
  1657. code = '$code',
  1658. code_restore = '$code',
  1659. code_md5 = '$md5',
  1660. flags = ".intval($flags).",
  1661. load_order = ".intval($load_order)
  1662. );
  1663. }
  1664. $msg = ($exists) ? gTxt('ied_plugin_updated', array('{name}' => $newname)) : gTxt('ied_plugin_uploaded', array('{name}' => $newname));
  1665. ied_plugin_table($msg, $newname);
  1666. } else if ($ext = 'txt') {
  1667. $plugin64 = file_get_contents($_FILES['thefile']['tmp_name']);
  1668. $ret = ied_plugin_install($plugin64);
  1669. if ($ret['err'] == '') {
  1670. ied_plugin_table($ret['msg'], $ret['nam']);
  1671. } else {
  1672. ied_plugin_table(array($ret['msg'], $ret['err']));
  1673. }
  1674. }
  1675. } else if ($plugin_install) {
  1676. $ret = ied_plugin_install(ps('plugin64'));
  1677. if ($ret['err'] == '') {
  1678. ied_plugin_table($ret['msg'], $ret['nam']);
  1679. } else {
  1680. ied_plugin_table(array($ret['msg'], $ret['err']));
  1681. }
  1682. }
  1683. }
  1684. // -------------------------------------------------------------
  1685. // Frankensteined shamelessly from txp_plugin.php
  1686. function ied_plugin_install($plugin='') {
  1687. if (strpos($plugin, '$plugin=\'') !== false) {
  1688. @ini_set('pcre.backtrack_limit', '1000000');
  1689. $plugin = preg_replace('@.*\$plugin=\'([\w=+/]+)\'.*@s', '$1', $plugin);
  1690. }
  1691. $plugin = preg_replace('/^#.*$/m', '', $plugin);
  1692. if(trim($plugin)) {
  1693. $plugin = base64_decode($plugin);
  1694. if (strncmp($plugin,"\x1F\x8B",2)===0)
  1695. $plugin = gzinflate(substr($plugin, 10));
  1696. if ($plugin = @unserialize($plugin)) {
  1697. if(is_array($plugin)){
  1698. extract($plugin);
  1699. $type = empty($type) ? 0 : min(max(intval($type), 0), 5);
  1700. $order = empty($order) ? 5 : min(max(intval($order), 1), 9);
  1701. $flags = empty($flags) ? 0 : intval($flags);
  1702. $exists = fetch('name', 'txp_plugin', 'name', $name);
  1703. if (isset($help_raw) && empty($plugin['allow_html_help'])) {
  1704. // default: help is in Textile format
  1705. include_once txpath.'/lib/classTextile.php';
  1706. $textile = new Textile();
  1707. $help = $textile->TextileRestricted($help_raw, 0, 0);
  1708. }
  1709. // MySQL defaults to set status=1 so we need to explicitly override this if this
  1710. // is a fresh installation
  1711. $ied_plugin_autoenable = ps('ied_plugin_autoenable');
  1712. $state = ($ied_plugin_autoenable==1) ? 'status=1,' : ($exists && ($ied_plugin_autoenable==2) ? '' : 'status=0,');
  1713. if ($exists) {
  1714. $rs = safe_update(
  1715. "txp_plugin",
  1716. "$state
  1717. type = $type,
  1718. author = '".doSlash($author)."',
  1719. author_uri = '".doSlash($author_uri)."',
  1720. version = '".doSlash($version)."',
  1721. description = '".doSlash($description)."',
  1722. help = '".doSlash($help)."',
  1723. code = '".doSlash($code)."',
  1724. code_restore = '".doSlash($code)."',
  1725. code_md5 = '".doSlash($md5)."',
  1726. flags = $flags",
  1727. "name = '".doSlash($name)."'"
  1728. );
  1729. } else {
  1730. $rs = safe_insert(
  1731. "txp_plugin",
  1732. "name = '".doSlash($name)."',
  1733. $state
  1734. type = $type,
  1735. author = '".doSlash($author)."',
  1736. author_uri = '".doSlash($author_uri)."',
  1737. version = '".doSlash($version)."',
  1738. description = '".doSlash($description)."',
  1739. help = '".doSlash($help)."',
  1740. code = '".doSlash($code)."',
  1741. code_restore = '".doSlash($code)."',
  1742. code_md5 = '".doSlash($md5)."',
  1743. load_order = '".$order."',
  1744. flags = $flags"
  1745. );
  1746. }
  1747. if ($rs and $code)
  1748. {
  1749. if (!empty($textpack))
  1750. {
  1751. install_textpack($textpack, false);
  1752. }
  1753. if (ps('ied_plugin_installopts') && ($flags & PLUGIN_LIFECYCLE_NOTIFY) )
  1754. {
  1755. load_plugin($name, true);
  1756. $message = callback_event("plugin_lifecycle.$name", 'installed');
  1757. }
  1758. if (empty($message)) $message = gTxt('plugin_installed', array('{name}' => $name));
  1759. return array('msg' => $message, 'err' => '', 'nam' => $name);
  1760. }
  1761. else
  1762. {
  1763. return array('msg' => gTxt('plugin_install_failed', array('{name}' => $name)), 'err' => E_ERROR);
  1764. }
  1765. }
  1766. }
  1767. }
  1768. return array('msg' => gTxt('bad_plugin_code'), 'err' => E_ERROR);
  1769. }
  1770. // -------------------------------------------------------------
  1771. function ied_plugin_save_pane_state() {
  1772. $panes = array('ied_plugin_tp_strings', 'ied_plugin_code', 'ied_plugin_docs', 'ied_plugin_meta', 'ied_plugin_utils', 'ied_plugin_cpanel', 'ied_plugin_dbplugs', 'ied_plugin_cacheplugs');
  1773. $pane = gps('pane');
  1774. if (in_array($pane, $panes))
  1775. {
  1776. set_pref("pane_{$pane}_visible", (gps('visible') == 'true' ? '1' : '0'), 'ied_plugin', PREF_HIDDEN, 'yesnoradio', 0, PREF_PRIVATE);
  1777. send_xml_response();
  1778. } else {
  1779. send_xml_response(array('http-status' => '400 Bad Request'));
  1780. }
  1781. }
  1782. // -------------------------------------------------------------
  1783. // Reurns an array of filenames;
  1784. // 1) the standard plugin
  1785. // 2) the compressed plugin
  1786. // 3) the PHP template
  1787. // 4) the textpack
  1788. function ied_plugin_get_name($name, $version, $lang='') {
  1789. $ied_plugin_prefs = ied_pc_get_prefs();
  1790. $op = get_pref('ied_plugin_output_sfile');
  1791. $opc = get_pref('ied_plugin_output_sfilec');
  1792. $opp = get_pref('ied_plugin_output_sfilep');
  1793. $opt = get_pref('ied_plugin_output_sfilet');
  1794. $out = array(
  1795. ( (empty($op)) ? $ied_plugin_prefs['ied_plugin_output_sfile']['default'] : $op ),
  1796. ( (empty($opc)) ? $ied_plugin_prefs['ied_plugin_output_sfilec']['default'] : $opc ),
  1797. ( (empty($opp)) ? $ied_plugin_prefs['ied_plugin_output_sfilep']['default'] : $opp ),
  1798. ( (empty($opt)) ? $ied_plugin_prefs['ied_plugin_output_sfilet']['default'] : $opt ),
  1799. );
  1800. $from = array('{name}', '{version}', '{lang}');
  1801. $to = array($name, $version, $lang);
  1802. foreach ($out as $fidx => $fname) {
  1803. $fname = str_replace($from, $to, $fname);
  1804. $out[$fidx] = sanitizeForFile($fname);
  1805. }
  1806. return $out;
  1807. }
  1808. // -------------------------------------------------------------
  1809. // Parse a plugin in Standard Textpattern Template format
  1810. function ied_plugin_read_file($filepath) {
  1811. $content = file($filepath);
  1812. $justfile = basename($filepath);
  1813. $parts = explode ('.',$justfile);
  1814. $ext = array_pop($parts);
  1815. $ext = ($ext==$justfile) ? '' : '.'.$ext; // Only assign an extension if one exists
  1816. $source_lines = count($content);
  1817. $commentblock = false;
  1818. $in_textpack = false;
  1819. $metadata = array(
  1820. 'name' => '',
  1821. 'version' => '',
  1822. 'revision' => '',
  1823. 'author' => '',
  1824. 'author_uri' => '',
  1825. 'description' => '',
  1826. 'order' => '5',
  1827. 'type' => '',
  1828. 'flags' => '',
  1829. 'textpack' => '',
  1830. );
  1831. for ($idx=0; $idx < $source_lines; $idx++) {
  1832. $content[$idx] = rtrim($content[$idx]);
  1833. // Bomb out if we reach the end of the definition area
  1834. if (strpos( $content[$idx], 'if (!defined(\'txpinterface\')' ) === 0) {
  1835. break;
  1836. }
  1837. if (strpos($content[$idx], '/*') === 0) {
  1838. $commentblock = true;
  1839. continue;
  1840. }
  1841. if ( (strpos($content[$idx], '*/') === 0) || (strpos($content[$idx], '**/') === 0) ) {
  1842. $commentblock = false;
  1843. continue;
  1844. }
  1845. if (strpos($content[$idx], 'EOT;') === 0) {
  1846. $in_textpack = false;
  1847. continue;
  1848. }
  1849. if ($in_textpack) {
  1850. $metadata['textpack'] .= $content[$idx].n;
  1851. }
  1852. if (!$commentblock && strpos($content[$idx], '$plugin[') === 0) {
  1853. // Found a plugin variable so extract it
  1854. $parts = explode(" = ", $content[$idx]);
  1855. $parts[0] = str_replace("'", "", $parts[0]); // Make the match easier!
  1856. $parts[1] = str_replace(";", "", $parts[1]); // Ditto
  1857. preg_match("/plugin\[(.*)\]/", $parts[0], $var); // Extract just the variable name
  1858. if (is_numeric($parts[1])) {
  1859. $parts[1] = "'".$parts[1]."'";
  1860. }
  1861. preg_match("/.*'(.*)'.*/", $parts[1], $val); // Remove anything outside the quotes (e.g. $revision)
  1862. if ($var[1] == 'revision' && isset($val[1]) && !empty($val[1])) {
  1863. $revparts = explode(' ', trim($val[1], '$ '));
  1864. $val[1] = $revparts[count($revparts)-1];
  1865. $val[1] = (empty($val[1])) ? '' : '.' .$val[1];
  1866. }
  1867. if ($var[1] == 'flags' && !isset($val[1])) {
  1868. // Unquoted value; possibly constants
  1869. $val[1] = 0;
  1870. $constants = do_list($parts[1], '|');
  1871. foreach ($constants as $constant) {
  1872. $val[1] |= (defined($constant)) ? constant($constant) : 0;
  1873. }
  1874. }
  1875. if ($var[1] == 'textpack') {
  1876. $in_textpack = true;
  1877. continue;
  1878. }
  1879. $metadata[$var[1]] = $val[1];
  1880. }
  1881. }
  1882. $metadata['name'] = (empty($metadata['name'])) ? basename($justfile, $ext) : $metadata['name'];
  1883. $metadata['load_order'] = $metadata['order'];
  1884. $metadata['version'] .= $metadata['revision'];
  1885. $metadata['help'] = ied_plugin_extract_hunk($content, 'HELP');
  1886. $metadata['css'] = ied_plugin_extract_hunk($content, 'CSS' );
  1887. $metadata['code'] = ied_plugin_extract_hunk($content, 'CODE');
  1888. return $metadata;
  1889. }
  1890. // -------------------------------------------------------------
  1891. // ripped and modded from net-carver's zem_tpl template
  1892. function ied_plugin_extract_hunk($content, $hunk, $cmnt="#", $delete=false) {
  1893. $dlm = explode("|", $cmnt);
  1894. $dlmStart = $dlm[0];
  1895. $dlmEnd = (count($dlm) > 1) ? $dlm[1] : '';
  1896. $lines = ied_plugin_make_array($content);
  1897. list ($start_delim, $end_delim) = ied_plugin_make_markers($hunk, $dlmStart, $dlmEnd);
  1898. $start = array_search($start_delim, $lines) + 1;
  1899. $end = array_search($end_delim, $lines);
  1900. // Kludge to get round the delimiter change in v0.83
  1901. if ($hunk == 'CSS' && $end === false) {
  1902. $start_delim = str_replace('---', '***', $start_delim);
  1903. $end_delim = str_replace('---', '***', $end_delim);
  1904. $start = array_search($start_delim, $lines) + 1;
  1905. $end = array_search($end_delim, $lines);
  1906. }
  1907. $extracted = array();
  1908. if (count($lines) > 0 && is_numeric($start) && is_numeric($end) && $end > $start) {
  1909. $extracted = array_slice($lines, $start, $end-$start);
  1910. $lineNum = count($extracted)-1;
  1911. while ($lineNum > 0 && trim($extracted[$lineNum--]) == "") {
  1912. array_pop($extracted);
  1913. }
  1914. while (count($extracted) > 0 && trim($extracted[0]) == "") {
  1915. array_shift($extracted);
  1916. }
  1917. if ($delete) {
  1918. $chopped = array_splice($lines, $start-1, $end-$start+2, "");
  1919. }
  1920. }
  1921. $remains = $lines;
  1922. // Check if the CSS section has <style> tags around it; add them if not
  1923. if ($hunk == "CSS") {
  1924. $numrows = count($extracted);
  1925. if ($numrows > 1) {
  1926. if (strpos($extracted[$numrows-1], '</style>') === false) {
  1927. $extracted[] = '</style>';
  1928. }
  1929. if (strpos($extracted[0], '<style ') === false) {
  1930. array_unshift($extracted, '<style type="text/css">');
  1931. }
  1932. }
  1933. }
  1934. if ($delete) {
  1935. return array(trim(join("\n", $extracted)), trim(join("\n", $remains)));
  1936. } else {
  1937. return trim(join("\n", $extracted));
  1938. }
  1939. }
  1940. // -------------------------------------------------------------
  1941. function ied_plugin_make_array($arr) {
  1942. if (!is_array($arr)) {
  1943. $arr = explode("\n", $arr);
  1944. }
  1945. $source_lines = count($arr);
  1946. for ($idx=0; $idx < $source_lines; $idx++) {
  1947. $arr[$idx] = rtrim($arr[$idx]);
  1948. }
  1949. return $arr;
  1950. }
  1951. // -------------------------------------------------------------
  1952. function ied_plugin_make_markers($hunk, $start, $end) {
  1953. global $ied_plugin_globals;
  1954. $smarker = $start . str_replace("SECTION", $hunk, $ied_plugin_globals['start']) . (($end)?' '.$end:'');
  1955. $emarker = $start . str_replace("SECTION", $hunk, $ied_plugin_globals['end']) . (($end)?' '.$end:'');
  1956. return (array($smarker, $emarker));
  1957. }
  1958. // -------------------------------------------------------------
  1959. // ripped and modded from net-carver's zem_tpl template
  1960. function ied_plugin_admin_check($codeblock, $type) {
  1961. // Short circuit since we're only interested in client plugins
  1962. if ($type != '0' || strpos($codeblock, 'txpinterface')===false) {
  1963. return true;
  1964. }
  1965. // Believe it or not this is several orders of magnitude quicker than a single preg_match
  1966. $cb = str_replace(array('\t', ' '), '', $codeblock);
  1967. $cb = str_replace('===', '==', $cb);
  1968. if (strpos($cb, 'txpinterface==\"admin')) return false;
  1969. if (strpos($cb, "txpinterface==\'admin")) return false;
  1970. if (strpos($cb, 'admin\"==txpinterface')) return false;
  1971. if (strpos($cb, "admin\'==txpinterface")) return false;
  1972. if (strpos($cb, 'admin\"==@txpinterface')) return false;
  1973. if (strpos($cb, "admin\'==@txpinterface")) return false;
  1974. return true;
  1975. }
  1976. // -------------------------------------------------------------
  1977. function ied_plugin_textile($name, $help, $css, $start_dlm, $end_dlm) {
  1978. global $prefs, $ied_plugin_globals;
  1979. $tmpdir = $hlpfile = '';
  1980. $changed = true;
  1981. $out = array();
  1982. if (isset($prefs['ied_plugin_output_tmpcache']) && !empty($prefs['ied_plugin_output_tmpcache']) && is_writable($prefs['ied_plugin_output_tmpcache'])) {
  1983. $tmpdir = $prefs['ied_plugin_output_tmpcache'];
  1984. $cache = build_file_path($tmpdir, 'ied_plugin_composer.cache');
  1985. if (!file_exists($cache)) {
  1986. touch($cache); // Create file if it doesn't exist
  1987. }
  1988. $plugs = parse_ini_file($cache);
  1989. $old_md5 = (is_array($plugs) && array_key_exists($name, $plugs)) ? $plugs[$name] : '';
  1990. $curr_md5 = md5($help);
  1991. $hlpfile = build_file_path($tmpdir, $name.'_help.txtl');
  1992. if ($old_md5 == $curr_md5) {
  1993. $out[0] = file_get_contents($hlpfile);
  1994. $changed = false;
  1995. } else {
  1996. $plugs[$name] = $curr_md5;
  1997. $fd = fopen($cache, "w");
  1998. foreach ($plugs as $idx => $val) {
  1999. fwrite($fd, $idx.' = '.$val.n);
  2000. }
  2001. fclose($fd);
  2002. }
  2003. }
  2004. if (!$out) {
  2005. @include_once txpath.'/lib/classTextile.php';
  2006. $out[0] = $help;
  2007. if (class_exists('Textile')) {
  2008. // Try and be a little more intelligent about applying textile or not
  2009. $re = '/h[0-6](\(.*\))?\./';
  2010. if (preg_match($re, $help)) {
  2011. $textile = new Textile();
  2012. $out[0] = $textile->TextileThis($help);
  2013. }
  2014. }
  2015. }
  2016. if ($tmpdir && $changed) {
  2017. if (!file_exists($hlpfile)) {
  2018. touch($hlpfile);
  2019. }
  2020. $fd = fopen($hlpfile, "w");
  2021. fwrite($fd, $out[0]);
  2022. fclose($fd);
  2023. }
  2024. // Replace the triple '---' in the delimiters so the help file can validate
  2025. $start_dlm = str_replace('---', '***', $start_dlm);
  2026. $end_dlm = str_replace('---', '***', $end_dlm);
  2027. $out[1] = ($css) ? "\n$start_dlm\n$css\n$end_dlm\n" : '';
  2028. return substr(join('', $out), 0, $ied_plugin_globals['size_help']+$ied_plugin_globals['size_css']);
  2029. }
  2030. // -------------------------------------------------------------
  2031. // Put the necessary javascript tags and stuff on the page
  2032. function ied_insert_editors() {
  2033. global $prefs;
  2034. $out = array();
  2035. $ed = array();
  2036. $ced = get_pref('ied_plugin_editor');
  2037. $hed = get_pref('ied_plugin_help_editor');
  2038. $cop = get_pref('ied_plugin_editor_options');
  2039. $hop = get_pref('ied_plugin_help_editor_options');
  2040. $ed[$ced] = get_pref('ied_plugin_editor_path');
  2041. $ed[$hed] = get_pref('ied_plugin_help_editor_path');
  2042. $cop = $cop ? ','.$cop : '';
  2043. $hop = $hop ? ','.$hop : '';
  2044. foreach ($ed as $editor => $editor_locs) {
  2045. $jsop = array();
  2046. $locs = do_list($editor_locs);
  2047. foreach($locs as $loc) {
  2048. if (strpos($loc, 'css:') !== false) {
  2049. $jsop[] = '<link rel="stylesheet" href="'.substr($loc, 4).'">';
  2050. } else {
  2051. $jsop[] = '<script type="javascript" src="'.$loc.'"></script>';
  2052. }
  2053. }
  2054. $jsop = join(n, $jsop);
  2055. switch ($editor) {
  2056. case "tiny_mce":
  2057. $out[] = <<<EOJS
  2058. {$jsop}
  2059. <script type="text/javascript">
  2060. tinyMCE.init({
  2061. mode : "specific_textareas",
  2062. editor_selector : "mceEditor"
  2063. {$hop}
  2064. });
  2065. </script>;
  2066. EOJS;
  2067. break;
  2068. case "edit_area":
  2069. $out[] = <<<EOJS
  2070. {$jsop}
  2071. <script type="text/javascript">
  2072. // initialisation
  2073. editAreaLoader.init({
  2074. id: "plugin_editor",
  2075. syntax: "php"
  2076. {$cop}
  2077. });
  2078. </script>
  2079. EOJS;
  2080. break;
  2081. case "codemirror":
  2082. $out[] = <<<EOJS
  2083. {$jsop}
  2084. var ied_pc_editor = CodeMirror.fromTextArea(document.getElementById("plugin_editor"), {
  2085. {$cop}
  2086. });
  2087. EOJS;
  2088. break;
  2089. case "codepress":
  2090. $out[] = $jsop;
  2091. break;
  2092. }
  2093. }
  2094. return implode("\n", $out);
  2095. }
  2096. // -------------------------------------------------------------
  2097. function ied_plugin_help_viewer($message='') {
  2098. global $prefs, $ied_plugin_globals;
  2099. if (gps('name')) {
  2100. $name = gps('name');
  2101. $help = ($name) ? fetch('help','txp_plugin','name',$name) : '';
  2102. list($css,$help) = ($help) ? ied_plugin_extract_hunk($help, "CSS", "<!--|-->", true) : array('',$help);
  2103. } else if (gps('filename')) {
  2104. $plugin = ied_plugin_read_file($prefs['plugin_cache_dir'].DS.gps('filename'));
  2105. extract($plugin);
  2106. }
  2107. $out = '';
  2108. if (empty($help)) {
  2109. $out = gTxt('ied_plugin_help_not_available');
  2110. } else {
  2111. list ($start_css, $end_css) = ied_plugin_make_markers("CSS", $ied_plugin_globals['css_start'], $ied_plugin_globals['css_end']);
  2112. $out = ied_plugin_textile($name, $help, $css, $start_css, $end_css);
  2113. }
  2114. pagetop(gTxt('ied_plugin_view_help', array('{name}' => $name)), $message);
  2115. echo n. '<div id="plugin_container" class="txp-container txp-view">'.
  2116. n. '<div class="text-column">' . $out . '</div>'.
  2117. n. '</div>';
  2118. }
  2119. // -------------------------------------------------------------
  2120. function ied_plugin_wrap_widget($widget) {
  2121. return '<span class="edit-value">'.$widget.'</span>';
  2122. }
  2123. // -------------------------------------------------------------
  2124. // Stub with correct signature for lifecycle callback
  2125. function ied_plugin_setup($evt='', $stp='') {
  2126. ied_plugin_prefs();
  2127. }
  2128. // -------------------------------------------------------------
  2129. // Display the composer's setup / prefs panel
  2130. function ied_plugin_prefs($message='') {
  2131. global $ied_pc_event;
  2132. require_privs('plugin_prefs.'.$ied_pc_event);
  2133. if (ps('ied_plugin_pref_save')) {
  2134. ied_plugin_prefs_update();
  2135. $message = gTxt('preferences_saved');
  2136. }
  2137. $ied_plugin_prefs = ied_pc_get_prefs();
  2138. pagetop(gTxt('ied_plugin_lbl_setup'), $message);
  2139. $btnSave = fInput('submit', 'submit', gTxt('save'), 'publish');
  2140. echo '<h1 class="txp-heading">' . gTxt('ied_plugin_lbl_setup') . '</h1>'.
  2141. script_js(<<<EOJS
  2142. var ied_plugin_path_re = new RegExp("^.*[/\\]", "g")
  2143. function ied_plugin_prefswap(selID, selValue) {
  2144. var id = selID+'_path';
  2145. var nuval = ((basename($("#"+id).val()) == selValue) ? $("#"+id).val() : dirname($("#"+id).val())+selValue);
  2146. if ($("#"+selID)[0].selectedIndex == 0) {
  2147. $("#"+id).attr("disabled", true); ;
  2148. } else {
  2149. $("#"+id).attr("disabled", false); ;
  2150. $("#"+id).val(nuval);
  2151. }
  2152. }
  2153. function basename(path, suffix) {
  2154. return path.replace(ied_plugin_path_re, '');
  2155. }
  2156. function dirname(path) {
  2157. return path.match(ied_plugin_path_re);
  2158. }
  2159. jQuery(function() {
  2160. jQuery(".ied_plugin_setup select option:selected").each(function(obj) {
  2161. var item = jQuery(this);
  2162. ied_plugin_prefswap(item.parent().attr('id'), item.val());
  2163. });
  2164. });
  2165. EOJS
  2166. );
  2167. $out = array();
  2168. $out[] = n.'<div class="plugin-column">';
  2169. $out[] = '<form name="ied_pc_prefs" id="ied_pc_prefs" action="index.php" method="post">';
  2170. $out[] = eInput($ied_pc_event).sInput('ied_plugin_prefs');
  2171. $last_grp = '';
  2172. foreach ($ied_plugin_prefs as $idx => $prefobj) {
  2173. if ($last_grp != $prefobj['group']) {
  2174. $out[] = hed(gTxt($prefobj['group']), 2);
  2175. }
  2176. $last_grp = $prefobj['group'];
  2177. $subout = array();
  2178. $label = '<span class="edit-label">'
  2179. .'<label>'.gTxt($idx).'</label>'
  2180. .'</span>';
  2181. $val = get_pref($idx, $prefobj['default'], 1);
  2182. $vis = (isset($prefobj['visible']) && !$prefobj['visible']) ? 'empty' : '';
  2183. switch ($prefobj['html']) {
  2184. case 'text_input':
  2185. $subout[] = ied_plugin_wrap_widget(fInput('text', $idx, $val, '', '', '', INPUT_REGULAR, '', $idx));
  2186. break;
  2187. case 'textarea':
  2188. $subout[] = text_area($idx, '', '', $val, $idx);
  2189. break;
  2190. case 'yesnoradio':
  2191. $subout[] = ied_plugin_wrap_widget(yesnoRadio($idx, $val));
  2192. break;
  2193. case 'radioset':
  2194. $subout[] = ied_plugin_wrap_widget(radioSet($prefobj['content'], $idx, $val));
  2195. break;
  2196. case 'checkboxset':
  2197. $vals = do_list($val);
  2198. $lclout = array();
  2199. foreach ($prefobj['content'] as $cb => $val) {
  2200. $checked = in_array($cb, $vals);
  2201. $lclout[] = checkbox($idx.'[]', $cb, $checked). '<label>' . gTxt($val) . '</label>';
  2202. }
  2203. $subout[] = ied_plugin_wrap_widget(join(n, $lclout));
  2204. break;
  2205. case 'selectlist':
  2206. $subout[] = ied_plugin_wrap_widget(selectInput($idx, $prefobj['content'][0], $val, $prefobj['content'][1]));
  2207. break;
  2208. default:
  2209. if ( strpos($prefobj['html'], 'ied_plugin_') !== false && is_callable($prefobj['html']) ) {
  2210. $subout[] = ied_plugin_wrap_widget($prefobj['html']($idx, $val));
  2211. }
  2212. break;
  2213. }
  2214. $out[] = graf($label . n.join(n ,$subout), ($vis ? ' class="'.$vis.'"' : ''));
  2215. }
  2216. $out[] = graf(fInput('submit', 'ied_plugin_pref_save', gTxt('save'), 'publish'));
  2217. $out[] = tInput();
  2218. $out[] = '</form></div>';
  2219. echo join(n, $out);
  2220. /*
  2221. echo tr(tda(strong(gTxt('prefs_title')), ' colspan="2"') . tda($btnRemove, $btnStyle) );
  2222. echo '<form method="post" action="?event=ied_plugin_composer&#38;step=ied_plugin_prefs_update">';
  2223. for ($idx = 0; $idx < $numRows; $idx++) {
  2224. $a = $rs[$idx];
  2225. $label = '<label for="'.$a['name'].'">'.gTxt($a['name']).':</label>';
  2226. $out = tda($label, ' style="text-align: right; vertical-align: middle;"');
  2227. switch($a['name']) {
  2228. case "ied_plugin_editor_path":
  2229. case "ied_plugin_help_editor_path":
  2230. case "ied_plugin_output_sfile":
  2231. case "ied_plugin_output_sfilec":
  2232. case "ied_plugin_output_sfilep":
  2233. case "ied_plugin_output_tmpcache":
  2234. $out .= td(fInput('text', $a['name'], $a['val'], 'edit', '', '', 50, '', $a['name']));
  2235. break;
  2236. case "ied_plugin_editor":
  2237. // $out .= td(selectInput($a['name'], array('none' => 'None', 'edit_area' => 'Edit Area', 'codepress' => 'CodePress'), $a['val'], '', ' onchange="ied_plugin_prefswap(this.id, this.value);"', $a['name']));
  2238. $out .= td(selectInput($a['name'], array('none' => 'None', 'edit_area' => 'Edit Area'), $a['val'], '', ' onchange="ied_plugin_prefswap(this.id, this.value);"', $a['name']));
  2239. break;
  2240. case "ied_plugin_help_editor":
  2241. $out .= td(selectInput($a['name'], array('textile' => 'None (Textile)', 'tiny_mce' => 'Tiny MCE'), $a['val'], '', ' onchange="ied_plugin_prefswap(this.id, this.value);"', $a['name']));
  2242. break;
  2243. case "ied_plugin_interface_elems":
  2244. $out .= td(
  2245. checkbox('ied_plugin_interface_elems[]', 'distribution', strpos($a['val'], 'distribution') !== false)
  2246. . '<label>'.gTxt('ied_plugin_if_el_dist').'</label>'
  2247. . checkbox('ied_plugin_interface_elems[]', 'style', strpos($a['val'], 'style') !== false)
  2248. . '<label>'.gTxt('ied_plugin_if_el_style').'</label>'
  2249. );
  2250. break;
  2251. case "ied_plugin_lang_default":
  2252. $out .= td(selectInput($a['name'], ied_plugin_lang_list('all'), $a['val'], '', '', $a['name']));
  2253. break;
  2254. case "ied_plugin_lang_choose":
  2255. $out .= td(selectInput($a['name'], array('installed' => gTxt('ied_plugin_langs_installed'), 'all' => gTxt('ied_plugin_langs_all')), $a['val'], '', '', $a['name']));
  2256. break;
  2257. case "ied_plugin_output_order":
  2258. $out .= td(radio($a['name'],0,(($a['val']==0)?1:0)).gTxt('output_code_first')." " . radio($a['name'],1,(($a['val']==1)?1:0)).gTxt('output_help_first')." ");
  2259. break;
  2260. case "ied_plugin_editor_width":
  2261. $out .= td(fInput('text', $a['name'], $a['val'], 'edit', '', '', 5, '', $a['name']));
  2262. break;
  2263. }
  2264. echo tr($out);
  2265. }
  2266. echo tr(tda($btnSave, $btnStyle));
  2267. echo '</form>';
  2268. } else if ($numRows > 0 && $numRows < $numReqPrefs) {
  2269. echo tr(tda(strong(gTxt('prefs_title')), ' colspan="2"'));
  2270. echo tr(tda(strong(gTxt('prefs_some')).br.br
  2271. .gTxt('prefs_some_explain').br.br
  2272. .gTxt('prefs_some_options'), ' colspan="2"'));
  2273. echo tr(tda($btnRemove,$btnStyle) . tda($btnInstall, $btnStyle));
  2274. } else {
  2275. echo tr(tda(strong(gTxt('prefs_title')), ' colspan="2"'));
  2276. echo tr(tda(gTxt('prefs_not_installed'), ' colspan="2"'));
  2277. echo tr(tda($btnInstall, $btnStyle));
  2278. }
  2279. echo endTable();
  2280. */
  2281. }
  2282. // -------------------------------------------------------------
  2283. // Save plugin prefs from setup panel
  2284. function ied_plugin_prefs_update() {
  2285. global $prefs;
  2286. $ied_plugin_prefs = ied_pc_get_prefs();
  2287. $saved = ps('ied_plugin_pref_save');
  2288. // Loop through each plugin setting and make sure it's in the prefs table, as follows:
  2289. // 1) if the value has been POSTed from the prefs panel, set the passed value
  2290. // 2) if the value exists in the $prefs (i.e. run on upgrade), set the existing value
  2291. // 3) if neither exist, set the plugin default value
  2292. foreach ($ied_plugin_prefs as $key => $prefobj) {
  2293. $val = ($saved || isset($_POST[$key])) ? ps($key) : ((isset($prefs[$key])) ? $prefs[$key] : $prefobj['default']);
  2294. $val = (is_array($val)) ? join(', ', $val) : $val;
  2295. set_pref($key, doSlash($val), 'ied_plugin', $prefobj['type'], $prefobj['html'], $prefobj['position']);
  2296. }
  2297. }
  2298. // -------------------------------------------------------------
  2299. // Delete plugin prefs
  2300. function ied_plugin_prefs_remove($showpane='1') {
  2301. safe_delete('txp_prefs', "name like 'ied_plugin_%'");
  2302. if ($showpane) {
  2303. $message = gTxt('ied_plugin_prefs_deleted');
  2304. ied_plugin_prefs($message);
  2305. }
  2306. }
  2307. // -------------------------------------------------------------
  2308. function ied_plugin_lang_list($flavour='installed') {
  2309. global $prefs;
  2310. $ied_langs = array();
  2311. if ($flavour == 'installed') {
  2312. // Self-join to get all the installed langs and language strings in one step
  2313. // $installed_langs = safe_query('select t1.lang, t2.data from '.PFX.'txp_lang as t1, '.PFX.'txp_lang as t2 WHERE t1.lang = t2.name GROUP BY lang');
  2314. $ied_langs = safe_column('lang', 'txp_lang', '1=1 GROUP BY lang');
  2315. } else {
  2316. // Grab all available langs from the RPC server
  2317. require_once txpath.'/lib/IXRClass.php';
  2318. $client = new IXR_Client(RPC_SERVER);
  2319. // Get items from RPC
  2320. @set_time_limit(5);
  2321. if ($client->query('tups.listLanguages',$prefs['blog_uid'])) {
  2322. $response = $client->getResponse();
  2323. foreach ($response as $language) {
  2324. $ied_langs[] = $language['language'];
  2325. }
  2326. }
  2327. }
  2328. // Build the select list array
  2329. $langlist = array();
  2330. foreach ($ied_langs as $ied_lang) {
  2331. $langlist[$ied_lang] = gTxt($ied_lang);
  2332. }
  2333. return $langlist;
  2334. }
  2335. // -------------------------------------------------------------
  2336. function ied_plugin_textpack_build($name, $force_all = 0) {
  2337. global $prefs;
  2338. $fetch_lang = null;
  2339. if ($force_all === 0) {
  2340. $fetch_lang = gps('lang');
  2341. }
  2342. if (!$fetch_lang) {
  2343. $fetch_lang = ($force_all === 1) ? join(',', array_keys(ied_plugin_lang_list('installed'))) : $prefs['ied_plugin_lang_selected'];
  2344. }
  2345. $tpout = array();
  2346. if ($fetch_lang) {
  2347. $chosen_lang = get_pref('ied_plugin_lang_default', '');
  2348. $dflt_lang = ($chosen_lang === '') ? $prefs['language'] : $chosen_lang; // Guard against situations when the chosen default lang is 'any'
  2349. $tp_pfx = unserialize(get_pref('ied_plugin_tp_prefix', '', 1));
  2350. $tp_pfx = isset($tp_pfx[$name]) ? $tp_pfx[$name] : '';
  2351. $tp_rows = ied_plugin_textpack_grab($fetch_lang, $tp_pfx);
  2352. if ($tp_rows) {
  2353. $ctr = 0;
  2354. $prevlang = '';
  2355. // Go through all the languages and put the default language at the start of the array
  2356. foreach ($tp_rows as $row) {
  2357. // Add the event marker
  2358. $theEvent = in_array($row['event'], array('public', 'common')) ? $row['event'] : $tp_pfx;
  2359. if ($prevlang != $row['lang']) {
  2360. $ctr++;
  2361. }
  2362. $idx = ($row['lang'] == $dflt_lang) ? 0 : $ctr;
  2363. $tplang[$idx][$theEvent][$row['lang']][$row['name']] = $row['data'];
  2364. $prevlang = $row['lang'];
  2365. $prevevent = $row['event'];
  2366. }
  2367. ksort($tplang); // Make sure default language is actually first
  2368. // Build the final textpack array with language markers.
  2369. // Note the marker for the default language may (should!) be omitted if the author wants
  2370. // the strings to be installed regardless of language on destination server.
  2371. // If a specific language is set and the user does not have that language
  2372. // installed, the strings would not be inserted
  2373. $prevevent = '';
  2374. foreach ($tplang as $idx => $langblock) {
  2375. foreach ($langblock as $ev => $codeblock) {
  2376. $tpheader = array();
  2377. $tpstrings = array();
  2378. if ($prevevent != $ev) {
  2379. $tpheader[] = '#@'.$ev;
  2380. }
  2381. foreach ($codeblock as $code => $data) {
  2382. if ( ($idx == 0 && $chosen_lang) || ($idx > 0) ) {
  2383. $tpheader[] = '#@language '.$code;
  2384. }
  2385. foreach ($data as $key => $val) {
  2386. // Don't output empty strings
  2387. if ($val) {
  2388. $tpstrings[] = $key . ' => ' . $val;
  2389. }
  2390. }
  2391. if ($tpstrings) {
  2392. $tpout = array_merge($tpout, $tpheader, $tpstrings);
  2393. }
  2394. }
  2395. }
  2396. }
  2397. }
  2398. }
  2399. return join(n, $tpout);
  2400. }
  2401. // -------------------------------------------------------------
  2402. function ied_plugin_textpack_grab($lang, $prefix) {
  2403. if ($lang === 'IED_ALL') {
  2404. $lang_query = '';
  2405. } else {
  2406. $lang = (empty($lang)) ? get_pref('language', 'en-gb') : $lang;
  2407. $langs = quote_list(do_list($lang));
  2408. $lang_query = "lang IN (".join(', ', $langs).") AND ";
  2409. }
  2410. return ($prefix) ? safe_rows('name, data, lang, event', 'txp_lang', $lang_query."name LIKE '".doSlash($prefix)."%' ORDER BY event,lang,name") : array();
  2411. }
  2412. // -------------------------------------------------------------
  2413. // *** AJAX calls
  2414. // -------------------------------------------------------------
  2415. function ied_plugin_lang_set() {
  2416. $sel = doSlash(gps('ied_tp_langsel'));
  2417. set_pref('ied_plugin_lang_selected', $sel, 'ied_plugin', PREF_HIDDEN, 'text_input', 0, PREF_PRIVATE);
  2418. }
  2419. // -------------------------------------------------------------
  2420. // Store the plugin textpack prefix
  2421. function ied_plugin_set_tp_prefix($plugname='', $pfx='') {
  2422. global $app_mode;
  2423. $plugname = ($plugname) ? $plugname : gps('plugin');
  2424. $pfx = ($pfx) ? $pfx : gps('prefix');
  2425. if ($pfx) {
  2426. $curr_pfx = unserialize(get_pref('ied_plugin_tp_prefix'));
  2427. $curr_pfx[$plugname] = $pfx;
  2428. set_pref('ied_plugin_tp_prefix', serialize($curr_pfx), 'ied_plugin', PREF_HIDDEN, 'text_input');
  2429. }
  2430. }
  2431. // -------------------------------------------------------------
  2432. // TODO: sanitize $fn
  2433. // Return a string from a (type 4 or 5) plugin gTxt() function/method.
  2434. function ied_plugin_textpack_load() {
  2435. $fn = doSlash(gps('ied_tp_fn'));
  2436. $lbl = doSlash(gps('ied_tp_lbl'));
  2437. $ret = '';
  2438. $fnparts = do_list($fn, '::');
  2439. if (count($fnparts) == 2) {
  2440. // Callable class -> method
  2441. $fobj = array($fnparts[0], $fnparts[1]);
  2442. } else {
  2443. $fobj = $fnparts[0];
  2444. }
  2445. if (is_callable($fobj)) {
  2446. $ret = call_user_func_array($fobj, array($lbl));
  2447. }
  2448. if ($ret) {
  2449. echo json_encode(array('ied_plugin_tp_string' => $ret));
  2450. }
  2451. }
  2452. // -------------------------------------------------------------
  2453. function ied_plugin_textpack_del() {
  2454. $lbl = doSlash(gps('ied_tp_lbl'));
  2455. $ret = safe_delete('txp_lang', "name='$lbl'");
  2456. }
  2457. // -------------------------------------------------------------
  2458. function ied_plugin_textpack_save() {
  2459. $lbl = doSlash(gps('ied_tp_lbl'));
  2460. $str = doSlash(gps('ied_tp_str'));
  2461. $lng = doSlash(gps('ied_tp_lng'));
  2462. $evt = doSlash(gps('ied_tp_evt'));
  2463. $where = "name='$lbl' AND lang='$lng'";
  2464. $ret = safe_update('txp_lang', "data='$str', event='$evt'", $where);
  2465. if ($ret && (mysql_affected_rows() or safe_count('txp_lang', $where))) {
  2466. // Update OK: do nothing else
  2467. } else {
  2468. $ret = safe_insert('txp_lang', "name='$lbl', lang='$lng', event='$evt', data='$str'");
  2469. }
  2470. }
  2471. // -------------------------------------------------------------
  2472. function ied_plugin_textpack_get() {
  2473. $lbl = doSlash(gps('ied_tp_lbl'));
  2474. $lng = doSlash(gps('ied_tp_lng'));
  2475. $dflt = doSlash(gps('ied_tp_dflt'));
  2476. $rs = safe_rows('lang, data', 'txp_lang', "name='$lbl' AND (lang='$lng' OR lang='$dflt')");
  2477. $out = array();
  2478. foreach ($rs as $row) {
  2479. if (($row['lang'] == $dflt) && ($lng != $dflt)) {
  2480. $out['ied_plugin_tp_dflt'] = $row['data'];
  2481. } else {
  2482. $out['ied_plugin_tp_string'] = $row['data'];
  2483. }
  2484. }
  2485. echo json_encode($out);
  2486. }
  2487. // -------------------------------------------------------------
  2488. function ied_plugin_code_save() {
  2489. global $theme;
  2490. $syntax_check = get_pref('ied_plugin_syntax_check');
  2491. $plug = doSlash(ps('plugin'));
  2492. $code = ps('codeblock');
  2493. $ret = ($syntax_check) ? ied_plugin_check_syntax_err($code) : false;
  2494. $msg = '';
  2495. if ($ret === false) {
  2496. $ret = @safe_update('txp_plugin', "code='".doSlash($code)."'", "name='$plug'");
  2497. if ($ret) {
  2498. $msg = $theme->announce_async(gTxt('ied_plugin_code_saved'));
  2499. } else {
  2500. $msg = $theme->announce_async(array(gTxt('ied_plugin_code_saved_fail'), E_ERROR));
  2501. }
  2502. send_xml_response(array('ied_plugin_msg' => $msg));
  2503. } else {
  2504. $msg = $theme->announce_async(array(htmlentities($ret[0], ENT_QUOTES), E_ERROR));
  2505. send_xml_response(array('http-status' => '412 Precondition Failed', 'ied_plugin_msg' => $msg, 'ied_plugin_err_line' => $ret[1]));
  2506. }
  2507. }
  2508. /**
  2509. * Check the syntax of some PHP code.
  2510. * Mostly from a comment in http://php.net/manual/en/function.php-check-syntax.php
  2511. * @param string $code PHP code to check.
  2512. * @return boolean|array If false, then check was successful, otherwise an array(message,line) of errors is returned.
  2513. */
  2514. function ied_plugin_check_syntax_err($code) {
  2515. if (!defined('CR')) define('CR',chr(13));
  2516. if (!defined('LF')) define('LF',chr(10));
  2517. $braces=0;
  2518. $inString=0;
  2519. foreach (token_get_all('<?php ' . $code) as $token) {
  2520. if (is_array($token)) {
  2521. switch ($token[0]) {
  2522. case T_CURLY_OPEN:
  2523. case T_DOLLAR_OPEN_CURLY_BRACES:
  2524. case T_START_HEREDOC: ++$inString; break;
  2525. case T_END_HEREDOC: --$inString; break;
  2526. }
  2527. } else if ($inString & 1) {
  2528. switch ($token) {
  2529. case '`': case '\'':
  2530. case '"': --$inString; break;
  2531. }
  2532. } else {
  2533. switch ($token) {
  2534. case '`': case '\'':
  2535. case '"': ++$inString; break;
  2536. case '{': ++$braces; break;
  2537. case '}':
  2538. if ($inString) {
  2539. --$inString;
  2540. } else {
  2541. --$braces;
  2542. if ($braces < 0) break 2;
  2543. }
  2544. break;
  2545. }
  2546. }
  2547. }
  2548. $inString = @ini_set('log_errors', false);
  2549. $token = @ini_set('display_errors', true);
  2550. ob_start();
  2551. $braces || $code = "if(0){{$code}\n}";
  2552. if (eval($code) === false) {
  2553. if ($braces) {
  2554. $braces = PHP_INT_MAX;
  2555. } else {
  2556. false !== strpos($code,CR) && $code = str_replace(CR,LF,str_replace(CRLF,LF,$code));
  2557. $braces = substr_count($code,LF);
  2558. }
  2559. $code = ob_get_clean();
  2560. $code = strip_tags($code);
  2561. if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
  2562. $code[2] = (int) $code[2];
  2563. $code = $code[2] <= $braces
  2564. ? array($code[1], $code[2])
  2565. : array('unexpected $end' . substr($code[1], 14), $braces);
  2566. } else $code = array('syntax error', 0);
  2567. } else {
  2568. ob_end_clean();
  2569. $code = false;
  2570. }
  2571. @ini_set('display_errors', $token);
  2572. @ini_set('log_errors', $inString);
  2573. return $code;
  2574. }
  2575. // Reflection utility for phpdoc generation
  2576. function ied_plugin_reflunction_factory($callback) {
  2577. if (is_array($callback)) {
  2578. // must be a class method
  2579. list($class, $method) = $callback;
  2580. return new ReflectionMethod($class, $method);
  2581. }
  2582. // class::method syntax
  2583. if (is_string($callback) && strpos($callback, "::") !== false) {
  2584. list($class, $method) = explode("::", $callback);
  2585. return new ReflectionMethod($class, $method);
  2586. }
  2587. // objects as functions (PHP 5.3+)
  2588. if (version_compare(PHP_VERSION, "5.3.0", ">=") && method_exists($callback, "__invoke")) {
  2589. return new ReflectionMethod($callback, "__invoke");
  2590. }
  2591. // assume it's a function
  2592. return new ReflectionFunction($callback);
  2593. }
  2594. function ied_plugin_generate_phpdoc() {
  2595. $plug = gps('plugin');
  2596. $obj = gps('fn');
  2597. $ret = load_plugin($plug);
  2598. if ($ret) {
  2599. try {
  2600. $obj = ied_plugin_reflunction_factory($obj);
  2601. $name = $obj->getName();
  2602. $doc = $obj->getDocComment();
  2603. if ($doc) {
  2604. //TODO: make list of allowed docblock tags a pref
  2605. //TODO: parse existing doc block and append any new vars/params
  2606. preg_match_all('/\s*\*\s*\@(abstract|access|author|category|copyright|deprecated|example|final|filesource|global|ignore|internal|license|link|method|name|package|param|property|return|see|since|static|staticvar|subpackage|todo|tutorial|uses|var|version)\s+(\w+)\s+(\$\w+(?::\w+|->\w+)*)\s+(\[(\&|\+|abstract|final|private|protected|public|static)\])*\s+(.*)/', $doc, $matches, PREG_SET_ORDER);
  2607. $tags = array();
  2608. // Reshuffle the matches to index on [tag][var]
  2609. foreach ($matches as $idx => $data) {
  2610. $ful = isset($data[0]) ? $data[0] : '';
  2611. $tag = isset($data[1]) ? $data[1] : '';
  2612. $typ = isset($data[2]) ? $data[2] : '';
  2613. $var = isset($data[3]) ? $data[3] : '';
  2614. $mod = isset($data[4]) ? $data[4] : '';
  2615. $tags[$tag][$var] = array(
  2616. 'type' => $typ,
  2617. 'full' => $ful,
  2618. 'mod' => $mod,
  2619. );
  2620. }
  2621. }
  2622. $fntype = array('meta' => array(), 'type' => array());
  2623. if (method_exists($obj, 'isConstructor')) {
  2624. // Dealing with a class method
  2625. if ($obj->isConstructor()) {
  2626. $fntype['meta'][] = ' [c]';
  2627. }
  2628. if ($obj->isPublic()) {
  2629. $fntype['mod'][] = '[public]';
  2630. }
  2631. if ($obj->isPrivate()) {
  2632. $fntype['mod'][] = '[private]';
  2633. }
  2634. if ($obj->isProtected()) {
  2635. $fntype['mod'][] = '[protected]';
  2636. }
  2637. if ($obj->isAbstract()) {
  2638. $fntype['mod'][] = '[abstract]';
  2639. }
  2640. if ($obj->isFinal()) {
  2641. $fntype['mod'][] = '[final]';
  2642. }
  2643. if ($obj->isStatic()) {
  2644. $fntype['mod'][] = '[static]';
  2645. }
  2646. }
  2647. $params = $obj->getParameters();
  2648. $param_list = array();
  2649. if ($params != null) {
  2650. foreach($params as $param) {
  2651. $has_dflt = false;
  2652. $type = 'string';
  2653. // Crude type checker
  2654. if ($param->isDefaultValueAvailable()) {
  2655. $has_dflt = true;
  2656. $dflt = $param->getDefaultValue();
  2657. if (is_bool($dflt)) {
  2658. $type = 'boolean';
  2659. $dflt = $dflt ? 'true' : 'false';
  2660. } else if (is_null($dflt)) {
  2661. $dflt = 'NULL';
  2662. } else if ($dflt == (string)(int)$dflt) {
  2663. $type = 'integer';
  2664. } else if ($dflt == (string)(float)$dflt) {
  2665. $type = 'float';
  2666. } else if (is_numeric($dflt)) {
  2667. $type = 'number';
  2668. } else if (is_array($dflt)) {
  2669. $type = 'array';
  2670. }
  2671. if ( ($type == 'string' || $type == 'array') && empty($dflt) ) {
  2672. $dflt = 'empty';
  2673. } else if ($type == 'string' && $dflt != 'NULL') {
  2674. $dflt = '"'.$dflt.'"';
  2675. }
  2676. }
  2677. $item = '@param'.t.$type.t.'$'.$param->getName();
  2678. if($param->isPassedByReference()) {
  2679. $item .= ' [&]';
  2680. }
  2681. if($param->allowsNull()) {
  2682. $item .= ' [+]';
  2683. }
  2684. $item .= ($has_dflt) ? t.'(Default: ' .$dflt. ')' : t;
  2685. $item .= t.'Param description';
  2686. $param_list[] = $item;
  2687. }
  2688. }
  2689. $final = '<pre>';
  2690. $final .= "/**".br;
  2691. $final .= " * $name" . ((!empty($fntype['meta'])) ? $fntype['meta'] : '') .t. (($fnType['mod']) ? join(' ', $fnType['mod']).' ' : '') . "Description".br;
  2692. $final .= " *".br;
  2693. $final .= " * Summary goes here".br;
  2694. $final .= " *".br;
  2695. foreach ($param_list as $item) {
  2696. $final .= " * ". $item.br;
  2697. }
  2698. $final .= " */</pre>".br;
  2699. send_xml_response(array('ied_plugin_phpdoc' => $final));
  2700. } catch (Exception $ex) {
  2701. send_xml_response( array( 'http-status' => '501 Not Implemented', 'error_msg' => $ex->getMessage() . gTxt('ied_plugin_fn_not_exist') ) );
  2702. }
  2703. } else {
  2704. send_xml_response(array('http-status' => '400 Bad Request'));
  2705. }
  2706. /* $classInterfaces=$reflection->getInterfaces();
  2707. //get information about the interfaces
  2708. if($classInterfaces != null)
  2709. {
  2710. fwrite($hf,"\n</i>\t</td></tr>\n\t<tr>".
  2711. "<td align=\"center\" colspan=\"0\">".
  2712. "<font face=\"arial\" size=\"2\"".
  2713. " color=\"purple\">Implemented".
  2714. " interfaces:</td><td align=\"center\"".
  2715. " colspan=\"0\"><font face=\"arial\"".
  2716. " size=\"2\" color=\"black\"><b>Name</b>".
  2717. "</font></td><td align=\"center\"".
  2718. " colspan=\"3\"><font face=\"arial\"".
  2719. " size=\"2\" color=\"black\"><b>".
  2720. "Description</b></font></td></tr>\n");
  2721. foreach($classInterfaces as $in)
  2722. {
  2723. fwrite($hf,"\t<tr><td></td><td align=".
  2724. "\"center\">");
  2725. fwrite($hf,$in->getName());
  2726. fwrite($hf,"</td><td align=\"center\"".
  2727. " colspan=\"3\"><i>\n");
  2728. fwrite($hf,$in->getDocComment());
  2729. fwrite($hf,"</i></td></tr>\n");
  2730. }
  2731. }
  2732. //get the superclass information
  2733. $superclass=$reflection->getParentClass();
  2734. if ($superclass != null){
  2735. fwrite($hf,"\t<tr><td align=\"center\"".
  2736. "colspan=\"0\"><font face=\"arial\"".
  2737. " size=\"2\" color=\"purple\">".
  2738. "The superclass is:</td><td".
  2739. " align=\"center\" colspan=\"4\">".
  2740. "<font face=\"arial\" size=\"2\"".
  2741. " color=\"black\"><b>\n");
  2742. fwrite($hf,$superclass->getName());
  2743. fwrite($hf,"</font></td></tr>\n");
  2744. }
  2745. //get information about the constants
  2746. $constants=$reflection->getConstants();
  2747. if($constants != null)
  2748. {
  2749. $constantsNumber=count($constants);
  2750. fwrite($hf,"\t<tr><td align=\"center\"".
  2751. " colspan=\"0\"><font face=\"arial\"".
  2752. " size=\"2\" color=\"purple\">".
  2753. "Constants:</td><td align=\"center\"".
  2754. " colspan=\"0\"><font face=\"arial\"".
  2755. " size=\"2\" color=\"black\"><b>Name</b>".
  2756. "</font></td><td align=\"center\"".
  2757. " colspan=\"3\"><font face=\"arial\"".
  2758. " size=\"2\" color=\"black\"><b>Value</b>".
  2759. "</font></td></tr>\n");
  2760. foreach($constants as $keys=>$value)
  2761. {
  2762. fwrite($hf,"\t<tr><td></td><td".
  2763. " align=\"center\">");
  2764. fwrite($hf,$keys);
  2765. fwrite($hf,"</td><td align=\"center\"".
  2766. " colspan=\"3\">");
  2767. fwrite($hf,$value);
  2768. fwrite($hf,"</td></tr>\n");
  2769. }
  2770. }
  2771. //get information about properties
  2772. $properties=$reflection->getProperties();
  2773. if($properties != null)
  2774. {
  2775. fwrite($hf,"\t<tr><td align=\"center\"".
  2776. " colspan=\"0\"><font face=\"arial\"".
  2777. " size=\"2\" color=\"purple\">".
  2778. "Properties:</td><td align=\"center\"".
  2779. " colspan=\"0\"><font face=\"arial\"".
  2780. " size=\"2\" color=\"black\"><b>Name</b>".
  2781. "</font></td><td align=\"center\"".
  2782. " colspan=\"3\"><font face=\"arial\"".
  2783. " size=\"2\" color=\"black\"><b>".
  2784. "Modifiers</b></font></td></tr>\n");
  2785. foreach($properties as $in)
  2786. {
  2787. fwrite($hf,"\t<tr><td></td><td".
  2788. " align=\"center\" colspan=\"0\">");
  2789. fwrite($hf,$in->getName());
  2790. fwrite($hf,"</td><td align=\"center\"".
  2791. " colspan=\"3\">");
  2792. if($in->isPublic())
  2793. { fwrite($hf,"[public]"); }
  2794. if($in->isPrivate())
  2795. { fwrite($hf,"[private]"); }
  2796. if($in->isProtected())
  2797. { fwrite($hf,"[protected]"); }
  2798. if($in->isStatic())
  2799. { fwrite($hf,"[static]"); }
  2800. fwrite($hf,"</td></tr>\n");
  2801. }
  2802. }
  2803. */
  2804. }
  2805. // ------------------------
  2806. // List of supported javascript syntax highlighter / code editors
  2807. // NB: no i18n since these are the names of the projects
  2808. function ied_plugin_code_editors($name, $val='') {
  2809. $eds['none'] = gTxt('none');
  2810. $eds['edit_area'] = 'EditArea';
  2811. $eds['codemirror'] = 'CodeMirror';
  2812. $eds['codepress'] = 'CodePress';
  2813. return selectInput($name, $eds, $val, false);
  2814. }
  2815. // ------------------------
  2816. // List of supported javascript help editors
  2817. // NB: no i18n since these are the names of the projects
  2818. function ied_plugin_help_editors($name, $val='') {
  2819. $eds['textilee'] = 'Textile';
  2820. $eds['tiny_mce'] = 'TinyMCE';
  2821. return selectInput($name, $eds, $val, false);
  2822. }
  2823. // ------------------------
  2824. // List of language options
  2825. function ied_plugin_lang_options($name, $val='') {
  2826. $lngs['installed'] = gTxt('ied_plugin_langs_installed');
  2827. $lngs['all'] = gTxt('ied_plugin_langs_all');
  2828. return selectInput($name, $lngs, $val, false);
  2829. }
  2830. // ------------------------
  2831. // List of language options
  2832. function ied_plugin_lang_default($name, $val='') {
  2833. $langs = array_merge(array('' => gTxt('ied_plugin_any')), ied_plugin_lang_list('all'));
  2834. return selectInput($name, $langs, $val, false);
  2835. }
  2836. // ------------------------
  2837. // Settings for the plugin
  2838. function ied_pc_get_prefs() {
  2839. global $prefs;
  2840. $ied_pc_prefs = array(
  2841. 'ied_plugin_editor' => array(
  2842. 'html' => 'ied_plugin_code_editors',
  2843. 'type' => PREF_HIDDEN,
  2844. 'position' => 10,
  2845. 'default' => 'none',
  2846. 'group' => 'ied_plugin_if_settings',
  2847. ),
  2848. 'ied_plugin_editor_path' => array(
  2849. 'html' => 'text_input',
  2850. 'type' => PREF_HIDDEN,
  2851. 'position' => 20,
  2852. 'default' => hu.'js/',
  2853. 'group' => 'ied_plugin_if_settings',
  2854. ),
  2855. 'ied_plugin_editor_options' => array(
  2856. 'html' => 'textarea',
  2857. 'type' => PREF_HIDDEN,
  2858. 'position' => 30,
  2859. 'default' => '',
  2860. 'group' => 'ied_plugin_if_settings',
  2861. ),
  2862. 'ied_plugin_editor_width' => array(
  2863. 'html' => 'text_input',
  2864. 'type' => PREF_HIDDEN,
  2865. 'position' => 40,
  2866. 'default' => '90%',
  2867. 'group' => 'ied_plugin_if_settings',
  2868. ),
  2869. 'ied_plugin_help_editor' => array(
  2870. 'html' => 'ied_plugin_help_editors',
  2871. 'type' => PREF_HIDDEN,
  2872. 'position' => 50,
  2873. 'default' => 'textile',
  2874. 'group' => 'ied_plugin_if_settings',
  2875. ),
  2876. 'ied_plugin_help_editor_path' => array(
  2877. 'html' => 'text_input',
  2878. 'type' => PREF_HIDDEN,
  2879. 'position' => 60,
  2880. 'default' => hu.'js/',
  2881. 'group' => 'ied_plugin_if_settings',
  2882. ),
  2883. 'ied_plugin_help_editor_options' => array(
  2884. 'html' => 'textarea',
  2885. 'type' => PREF_HIDDEN,
  2886. 'position' => 70,
  2887. 'default' => 'theme_advanced_toolbar_location : "top",
  2888. theme_advanced_buttons1 : "bold,italic,underline,strikethrough,forecolor,backcolor,removeformat,numlist,bullist,outdent,indent,justifyleft,justifycenter,justifyright,justifyfull",
  2889. theme_advanced_buttons2 : "link,unlink,separator,ibrowser,separator,search,replace,separator,cut,copy,paste,separator,code,separator,formatselect",
  2890. theme_advanced_buttons3 : ""',
  2891. 'group' => 'ied_plugin_if_settings',
  2892. ),
  2893. 'ied_plugin_interface_elems' => array(
  2894. 'html' => 'checkboxset',
  2895. 'type' => PREF_HIDDEN,
  2896. 'position' => 80,
  2897. 'content' => array('distribution' => 'ied_plugin_if_el_dist', 'style' => 'ied_plugin_if_el_style'),
  2898. 'default' => 'style',
  2899. 'group' => 'ied_plugin_if_settings',
  2900. ),
  2901. 'ied_plugin_lifecycle_options' => array(
  2902. 'html' => 'checkboxset',
  2903. 'type' => PREF_HIDDEN,
  2904. 'position' => 90,
  2905. 'content' => array('installed' => 'ied_plugin_lbl_lc_install', 'enabled' => 'ied_plugin_lbl_lc_enable', 'disabled' => 'ied_plugin_lbl_lc_disable', 'deleted' => 'ied_plugin_lbl_lc_delete'),
  2906. 'default' => '',
  2907. 'group' => 'ied_plugin_prefs',
  2908. ),
  2909. 'ied_plugin_auto_enable' => array(
  2910. 'html' => 'radioset',
  2911. 'type' => PREF_HIDDEN,
  2912. 'position' => 100,
  2913. 'content' => array('0' => gTxt('no'), '1' => gTxt('yes'), '2' => gTxt('ied_plugin_same')),
  2914. 'default' => '2',
  2915. 'group' => 'ied_plugin_prefs',
  2916. ),
  2917. 'ied_plugin_syntax_check' => array(
  2918. 'html' => 'yesnoradio',
  2919. 'type' => PREF_HIDDEN,
  2920. 'position' => 110,
  2921. 'default' => '1',
  2922. 'group' => 'ied_plugin_prefs',
  2923. ),
  2924. 'ied_plugin_lang_choose' => array(
  2925. 'html' => 'ied_plugin_lang_options',
  2926. 'type' => PREF_HIDDEN,
  2927. 'position' => 120,
  2928. 'default' => 'installed',
  2929. 'group' => 'ied_plugin_prefs',
  2930. ),
  2931. 'ied_plugin_lang_default' => array(
  2932. 'html' => 'ied_plugin_lang_default',
  2933. 'type' => PREF_HIDDEN,
  2934. 'position' => 130,
  2935. 'default' => '',
  2936. 'group' => 'ied_plugin_prefs',
  2937. ),
  2938. 'ied_plugin_output_order' => array(
  2939. 'html' => 'radioset',
  2940. 'type' => PREF_HIDDEN,
  2941. 'position' => 140,
  2942. 'content' => array('0' => gTxt('ied_plugin_lbl_op_code_first'), '1' => gTxt('ied_plugin_lbl_op_help_first')),
  2943. 'default' => '0',
  2944. 'group' => 'ied_plugin_prefs',
  2945. ),
  2946. 'ied_plugin_output_sfile' => array(
  2947. 'html' => 'text_input',
  2948. 'type' => PREF_HIDDEN,
  2949. 'position' => 150,
  2950. 'default' => '{name}_v{version}.txt',
  2951. 'group' => 'ied_plugin_prefs',
  2952. ),
  2953. 'ied_plugin_output_sfilec' => array(
  2954. 'html' => 'text_input',
  2955. 'type' => PREF_HIDDEN,
  2956. 'position' => 160,
  2957. 'default' => '{name}_v{version}_zip.txt',
  2958. 'group' => 'ied_plugin_prefs',
  2959. ),
  2960. 'ied_plugin_output_sfilep' => array(
  2961. 'html' => 'text_input',
  2962. 'type' => PREF_HIDDEN,
  2963. 'position' => 170,
  2964. 'default' => '{name}_v{version}.php',
  2965. 'group' => 'ied_plugin_prefs',
  2966. ),
  2967. 'ied_plugin_output_sfilet' => array(
  2968. 'html' => 'text_input',
  2969. 'type' => PREF_HIDDEN,
  2970. 'position' => 180,
  2971. 'default' => '{name}_v{version}_{lang}_textpack.txt',
  2972. 'group' => 'ied_plugin_prefs',
  2973. ),
  2974. 'ied_plugin_output_tmpcache' => array(
  2975. 'html' => 'text_input',
  2976. 'type' => PREF_HIDDEN,
  2977. 'position' => 190,
  2978. 'default' => $prefs['tempdir'],
  2979. 'group' => 'ied_plugin_prefs',
  2980. ),
  2981. );
  2982. return $ied_pc_prefs;
  2983. }
  2984. /*
  2985. * Public tag: List plugins, filtered by name or prefix
  2986. */
  2987. function ied_plugin_list($atts = array(), $thing = null) {
  2988. global $ied_plugin_data;
  2989. extract(lAtts(array(
  2990. 'from' => 'database', // database, cache (or both)
  2991. 'name' => '', // List of plugin names to return
  2992. 'prefix' => '', // Plugin prefixes
  2993. 'exclude' => '', // names to exclude from the list
  2994. 'type' => '', // 0-5 or comma-separated combos thereof
  2995. 'wraptag' => '',
  2996. 'class' => '',
  2997. 'break' => '',
  2998. 'breakclass' => '',
  2999. 'html_id' => '',
  3000. 'form' => '',
  3001. ),$atts));
  3002. $thing = (empty($form)) ? ((empty($thing)) ? '<txp:ied_plugin_info item="name" />' : $thing) : fetch_form($form);
  3003. $location = do_list($from);
  3004. $names = do_list($name);
  3005. $prefixes = do_list($prefix);
  3006. $excludes = do_list($exclude);
  3007. if (in_array('database', $location)) {
  3008. $sql = array();
  3009. $sql[] = '1';
  3010. if ($name) {
  3011. $sql[] = "name IN ('".join("','", doSlash($names))."')";
  3012. }
  3013. if ($prefix) {
  3014. $sqlor = array();
  3015. foreach ($prefixes as $pfx) {
  3016. $sqlor[] = "name LIKE '".doSlash($pfx)."%'";
  3017. }
  3018. $sql[] = '(' . join(' OR ', $sqlor) . ')';
  3019. }
  3020. if ($exclude) {
  3021. $sql[] = "name NOT IN ('".join("','", doSlash($excludes))."')";
  3022. }
  3023. $rs = safe_rows('*', 'txp_plugin', join(' AND ', $sql) . ' ORDER BY name');
  3024. }
  3025. // TODO: Add the meta data from matching plugins in the cache folder
  3026. if (in_array('cache', $location)) {
  3027. }
  3028. $out = array();
  3029. $ied_pd_saved = $ied_plugin_data;
  3030. foreach ($rs as $row) {
  3031. $ied_plugin_data = $row;
  3032. $out[] = parse($thing);
  3033. $ied_plugin_data = array();
  3034. }
  3035. $ied_plugin_data = $ied_pd_saved;
  3036. return ($wraptag) ? doWrap($out, $wraptag, $break, $class, $breakclass, '', '', $html_id) : join($break, $out);
  3037. }
  3038. /**
  3039. * Public tag: Display plugin data for form/container usage
  3040. */
  3041. function ied_plugin_info($atts, $thing = null) {
  3042. global $ied_plugin_data;
  3043. extract(lAtts(array(
  3044. 'item' => '',
  3045. 'wraptag' => '',
  3046. 'break' => '',
  3047. 'class' => '',
  3048. 'debug' => 0,
  3049. ), $atts));
  3050. $pdata = is_array($ied_plugin_data) ? $ied_plugin_data : array();
  3051. if ($debug) {
  3052. echo '++ AVAILABLE INFO ++';
  3053. dmp($pdata);
  3054. }
  3055. $items = do_list($item);
  3056. $out = array();
  3057. foreach ($items as $it) {
  3058. if (isset($pdata[$it])) {
  3059. $out[] = $pdata[$it];
  3060. }
  3061. }
  3062. return doWrap($out, $wraptag, $break, $class);
  3063. }
  3064. /**
  3065. * Public tag: List of available textpack information.
  3066. */
  3067. function ied_plugin_textpacks($atts, $thing = null)
  3068. {
  3069. global $ied_plugin_data;
  3070. extract(lAtts(array(
  3071. 'name' => '',
  3072. 'filename' => '',
  3073. 'lang' => 'IED_ALL',
  3074. 'wraptag' => '',
  3075. 'break' => '',
  3076. 'class' => '',
  3077. 'form' => '',
  3078. ), $atts));
  3079. if (!$name && !$filename) {
  3080. return;
  3081. }
  3082. if ($name) {
  3083. $theName = $name;
  3084. } else if ($filename) {
  3085. $theName = $filename;
  3086. }
  3087. $thing = (empty($form)) ? ((empty($thing)) ? '<txp:ied_plugin_info item="lang" />' : $thing) : fetch_form($form);
  3088. $langs = array();
  3089. $tp_prefixes = unserialize(get_pref('ied_plugin_tp_prefix', ''));
  3090. if (isset($tp_prefixes[$theName])) {
  3091. $strings = ied_plugin_textpack_grab($lang, $tp_prefixes[$theName]);
  3092. foreach ($strings as $row) {
  3093. if (array_search($row['lang'], $langs) === false) {
  3094. $langs[] = $row['lang'];
  3095. }
  3096. }
  3097. }
  3098. $out = array();
  3099. $ied_pd_saved = $ied_plugin_data;
  3100. $idx = 0;
  3101. $num_langs = count($langs);
  3102. foreach ($langs as $row) {
  3103. $ied_plugin_data['lang'] = $row;
  3104. $ied_plugin_data['first_lang'] = (($idx === 0) ? 1 : 0);
  3105. $ied_plugin_data['last_lang'] = (($idx === $num_langs - 1) ? 1 : 0);
  3106. $out[] = parse($thing);
  3107. $ied_plugin_data['lang'] = $ied_plugin_data['first_lang'] = $ied_plugin_data['last_lang'] = '';
  3108. $idx++;
  3109. }
  3110. $ied_plugin_data = $ied_pd_saved;
  3111. return doWrap($out, $wraptag, $break, $class);
  3112. }
  3113. /**
  3114. * Public tag: Download a plugin
  3115. */
  3116. function ied_plugin_download_link($atts, $thing = null)
  3117. {
  3118. extract(lAtts(array(
  3119. 'type' => 'compressed', // uncompressed, compressed, template, textpack
  3120. 'name' => '',
  3121. 'filename' => '',
  3122. 'label' => 'Download',
  3123. 'class' => '',
  3124. 'lang' => 'IED_ALL',
  3125. 'form' => '',
  3126. ), $atts));
  3127. if (!$name && !$filename) {
  3128. return;
  3129. }
  3130. if ($name) {
  3131. $theName = '&name='.urlencode($name);
  3132. } else if ($filename) {
  3133. $theName = '&filename='.urlencode($filename);
  3134. }
  3135. $theClass = '';
  3136. if ($class) {
  3137. $theClass = ' class="'.$class.'"';
  3138. }
  3139. $langopt = '';
  3140. if ($lang) {
  3141. $langs = do_list($lang);
  3142. $langopt = '&lang='.join(',', $langs);
  3143. }
  3144. $linkName = (empty($form)) ? ((empty($thing)) ? $label : parse($thing)) : parse_form($form);
  3145. if ($type === 'compressed') {
  3146. return href($linkName, '?ied_plugin_download=1'.$theName.'&type=zip'.$langopt, $theClass);
  3147. } else if ($type === 'uncompressed') {
  3148. return href($linkName, '?ied_plugin_download=1'.$theName.'&type=txt'.$langopt, $theClass);
  3149. } else if ($type === 'template') {
  3150. return href($linkName, '?ied_plugin_download=1'.$theName.'&type=php'.$langopt, $theClass);
  3151. } else if ($type === 'textpack') {
  3152. return href($linkName, '?ied_plugin_download=1'.$theName.'&type=textpack'.$langopt, $theClass);
  3153. }
  3154. }
  3155. /**
  3156. * Handles downloading plugin content
  3157. */
  3158. function ied_plugin_download() {
  3159. if (gps('ied_plugin_download')) {
  3160. $type = gps('type');
  3161. switch ($type) {
  3162. case 'zip':
  3163. case 'txt':
  3164. ied_plugin_save_as_file();
  3165. break;
  3166. case 'php':
  3167. ied_plugin_save_as_php_file();
  3168. break;
  3169. case 'textpack':
  3170. ied_plugin_save_as_textpack();
  3171. break;
  3172. }
  3173. }
  3174. }
  3175. # --- END PLUGIN CODE ---
  3176. if (0) {
  3177. ?>
  3178. <!--
  3179. # --- BEGIN PLUGIN HELP ---
  3180. <h1>Inspired plugin composer</h1>
  3181. <p>Creates a new page under the <i>Extensions</i> tab where you can edit and export plugins that are already installed in Textpattern, as well as create or upload new plugins created by the community.</p>
  3182. <h2>Features</h2>
  3183. <ul>
  3184. <li>Create, edit, upload or install plugin code and documentation, then publish your wares in the standard Textpattern plugin format.</li>
  3185. <li>Full support for the official zem_tpl.php plugin template and the modified &#8220;code first&#8221; version by <a href="http://txp-plugins.netcarving.com/plugins/modified-plugin-compiler">net-carver</a>, which also incorporates a &#8216;style&#8217; segment for your help text (although it should be rarely needed).</li>
  3186. <li>Optional syntax checker to protect the site / admin side from bad code.</li>
  3187. <li>Export plugins in a variety of formats, allowing you to switch between editing in the plugin composer or your favourite editing program. You can export:
  3188. <ul>
  3189. <li>a standard BASE64-encoded text file.</li>
  3190. <li>a gzipped version (useful for large plugins).</li>
  3191. <li>a php file in the standard template format.</li>
  3192. <li>Textpacks on their own &#8212; any combination of languages.</li>
  3193. </ul></li>
  3194. <li>Support for all plugin types: Library, Public, and Admin (with or without <span class="caps"><span class="caps">AJAX</span></span>).</li>
  3195. <li>Specify a recommended plugin load order if your plugin needs special powers.</li>
  3196. <li>Documentation can be written in Textile or <span class="caps"><span class="caps">HTML</span></span>.</li>
  3197. <li>Take advantage of the <a href="http://forum.textpattern.com/viewtopic.php?id=13089">TinyMCE <span class="caps"><span class="caps">WYSIWYG</span></span> editor</a> for the help section, or a variety of javascript syntax highlighters / editors for code. See the <a href="#ied_plugin_setup">setup</a> section for more on the available editors.</li>
  3198. <li>Built-in Textile help viewer (thanks to net-carver&#8217;s <a href="http://txp-plugins.netcarving.com/plugins/plugin-help-viewer">Plugin Help Viewer</a>) to allow you to preview your Help text during development.</li>
  3199. <li>Set a code &#8220;restore point&#8221; and roll back to that point if things go sideways. Also useful for returning plugin source code to its as-installed state.</li>
  3200. </ul>
  3201. <h2>Installation / Uninstallation</h2>
  3202. <p class="important">Requires Textpattern 4.5.0+</p>
  3203. <p>Download the plugin (v0.5 and above) from <a href="http://stefdawson.com/sw">stefdawson.com</a>, paste the code into Textpattern&#8217;s <em>Admin-&gt;Plugins</em> page, install and enable the plugin.</p>
  3204. <p>The default preferences are automatically created when you install the plugin or visit the Setup screen, by clicking the <b>Setup</b> button in the top right corner of the <i>Extensions-&gt;Plugin composer</i> panel. See the <a href="#ied_plugin_setup">setup section</a> for details.</p>
  3205. <p>To remove the plugin composer (noooo!) simply delete it as normal from the <i>Admin-&gt;Plugins</i> panel. All the preferences will automatically be removed as well. <span class="caps"><span class="caps">NOTE</span></span>: deleting the plugin from the plugin composer window itself <em>will not</em> delete the preferences unless you have set the plugin to respond to &#8216;delete&#8217; lifecycle events.</p>
  3206. <h2 id="ied_plugin_list">List panel</h2>
  3207. <p>At the top of the main page is a collapsible area labelled <code>Installation</code>. This is dealt with in the <a href="#ied_plugin_create">Creating plugins</a> section; the rest of the page lists all installed plugins. The columns are:</p>
  3208. <dl>
  3209. <dt><span>Plugin</span></dt>
  3210. <dd>Click the plugin name to <a href="#ied_plugin_edit">edit</a> it. If the plugin has preferences associated with it, you will also see an <b>[Options]</b> link.</dd>
  3211. <dt><span>Author</span></dt>
  3212. <dd>If available, click the author to visit their site.</dd>
  3213. <dt><span>Version (Modified)</span></dt>
  3214. <dd>Shows the current version number and whether the plugin has been modified from when it was either created or installed. If it has, you can click the version number to restore the plugin code to its installed state or last <a href="#ied_plugin_restore">restore point</a>.</dd>
  3215. <dt><span>Description</span></dt>
  3216. <dd>A brief one-line overview of what the plugin does.</dd>
  3217. <dt><span>Publish</span></dt>
  3218. <dd>Three links
  3219. <dl>
  3220. <dd><em>Publish</em> : exports the plugin as a .txt file for distribution to other Textpattern users.</dd>
  3221. <dd><em>Zip</em> : exports the plugin as a compressed (gzipped) .txt file for distribution.</dd>
  3222. <dd><em>Help</em> : displays the (textile processed) plugin documentation, if there is any.</dd>
  3223. </dl></dd>
  3224. <dd>All these tasks (and more) can be performed from the <a href="#ied_plugin_edit">Edit panel</a>.</dd>
  3225. <dt><span>Order</span></dt>
  3226. <dd>The recommended plugin load order from 1 (loaded first) to 9 (loaded last). 5 is default.</dd>
  3227. <dd>Note that if using the plugin cache directory this feature is <em>only available if the <code>$plugin[&amp;#39;order&amp;#39;]</code> string is in the template to begin with.</em> So if your load order keeps returning to &#8216;5&#8217;, edit your template directly to add that string, or paste your plugin into the relevant parts of the <a href="http://code.google.com/p/textpattern/source/browse/development/4.x-plugin-template/zem_plugin.php">empty plugin template</a> and upload it.</dd>
  3228. <dt><span>Active</span></dt>
  3229. <dd>Enable or disable a plugin by clicking entries in this column &#8212; this will only trigger any enabled/disabled notification event in your plugin if you have set the plugin up to do so.</dd>
  3230. </dl>
  3231. <p>Use the multi-edit checkboxes (you can shift click them to select more than one at a time) to delete plugins or make mass changes. Deletion will only trigger the &#8216;deleted&#8217; notification event if you have told the composer to allow plugins to respond to lifecycle events.</p>
  3232. <p>If you are using the plugin_cache_dir (<i>Admin-&gt;Advanced Prefs</i>), any plugins in the standard template format uploaded to this directory will be available immediately for editing and testing. You can edit and publish the plugins in the same manner as the regular, installed plugins above, with the following exceptions:</p>
  3233. <ol>
  3234. <li>There is no concept of &#8216;modified&#8217; or of restore points as the files always represent the most up-to-date version. Use your own external versioning.</li>
  3235. <li>Cached plugins are &#8220;always on&#8221; and do not need to be installed. To deactivate the plugin the file must be removed from the plugin cache directory.</li>
  3236. <li>It follows that the lifecycle notification events (installed, deleted, enabled, disabled) are not triggered from plugins in the cache directory.</li>
  3237. <li>The recommended load order cannot be changed from the list panel; it can only be altered via the <a href="#ied_plugin_edit">edit panel</a>. The specified load order is only written to the file when the plugin is exported. Load order is alphabetical for all plugins in the plugin_cache directory.</li>
  3238. </ol>
  3239. <h2 id="ied_plugin_create">Creating, installing and naming plugins</h2>
  3240. <p>There are a few ways of creating plugins from the composer&#8217;s list panel. Expand the &#8216;Installation&#8217; twisty at the top of the composer&#8217;s List panel to see them all.</p>
  3241. <p>The first is to create a blank, empty plugin; use the first text box for this and the <b>Create new plugin</b> button. Points to note:</p>
  3242. <ul>
  3243. <li>If you use a standard plugin name (e.g. <code>abc_my_plugin</code>) it will be created in the database.</li>
  3244. <li>If you add <code>.php</code> to the plugin name, it will be created in the plugin cache directory in the standard template format.</li>
  3245. <li>Avoid specifying the version of the plugin in the name &#8212; you should use the <b>Rename file</b> or <b>Export</b> options in the Edit panel to alter the filename into your preferred filename format (see <a href="#ied_plugin_setup">setup</a>)</li>
  3246. </ul>
  3247. <p>The second method of creating a plugin is to upload one using the Browse/upload box. Please note:</p>
  3248. <ul>
  3249. <li>You can upload code as either:
  3250. <ul>
  3251. <li>a standard plugin base64 .txt file.</li>
  3252. <li>a standard plugin template (either code-first or help-first, complete with <span class="caps"><span class="caps">CSS</span></span> areas).</li>
  3253. <li>a <span class="caps"><span class="caps">PHP</span></span> file containing raw code only.</li>
  3254. </ul></li>
  3255. <li>The plugin will always be installed in the database when using this method (upload template plugins manually via <span class="caps"><span class="caps">FTP</span></span> or create a new one with a .php extension and paste the code into it if you want it in the plugin cache).</li>
  3256. <li>If the plugin exists it will be updated with your new info.</li>
  3257. <li>This is the only place you can upload raw <span class="caps"><span class="caps">PHP</span></span> with the <code>&amp;lt;?php ?&amp;gt;</code> markers. Everywhere else you should use a full template.</li>
  3258. <li>If you add a plugin Name in the box above the upload widget before clicking <b>Upload</b>, your plugin will take that name.</li>
  3259. </ul>
  3260. <p>You may also install a plugin just like on the <i>Admin-&gt;Plugins</i> panel by copying and pasting the contents of a standard plugin .txt file into the Install textarea and hitting the <b>Install</b> button. Note there are a few differences from the core&#8217;s installer:</p>
  3261. <ul>
  3262. <li>There is no preview step; the plugin is installed immediately, so be sure you trust the code!</li>
  3263. <li>You can choose whether the plugin is allowed to run its &#8216;install&#8217; lifecycle event by using the radio button below the textarea.</li>
  3264. <li>You can elect to have the plugin auto-enable itself by selecting the appropriate radio entry below the textarea prior to clicking <em>Install</em>.</li>
  3265. <li>The radio buttons take on the default settings from the <a href="#ied_plugin_setup">setup panel</a>.</li>
  3266. </ul>
  3267. <p>By convention, every plugin should be created with a three-letter prefix, an underscore, then the name of the plugin. You are free to choose your own three-letter prefix (usually your initials) subject to <a href="http://www.textpattern.net/wiki/index.php?title=Reserved_Plugin_Prefixes">avoiding ones already taken by other plugin authors</a> so people can get to know your work. Plus, it groups your plugins together in the <a href="http://textpattern.org/plugins">plugin respository</a>. All functions, variables (including <span class="caps"><span class="caps">DOM</span></span> nodes), <span class="caps"><span class="caps">CSS</span></span> classes, Textpack strings and anything else you inject into the global scope should be prefixed by <strong>at least</strong> your three letter code; preferably the whole plugin name (or abridged version thereof) to avoid namespace clashes with your own and other plugins.</p>
  3268. <h2 id="ied_plugin_edit">Edit panel</h2>
  3269. <p>The Edit panel is where you craft your masterpiece. It is divided into various sections and boxes, as detailed here:</p>
  3270. <h3>Buttons and links</h3>
  3271. <ul>
  3272. <li><b>Save</b> : commits changes from the whole page. There are two identical buttons, for convenience; top and bottom.</li>
  3273. <li><b>[ Docs ]</b> : shows the documentation as users will see it (i.e. Textile processed into <span class="caps"><span class="caps">HTML</span></span>). Any help text over 64KB in length is truncated so you can verify that all your documentation fits into the users&#8217; destination database.</li>
  3274. </ul>
  3275. <h3>Meta information</h3>
  3276. <dl>
  3277. <dt><strong>Name</strong></dt>
  3278. <dd>The name of your plugin, which you can change at any time. Note that if you are editing a file from the plugin cache directory and the <code>$plugin[&amp;#39;name&amp;#39;]</code> row is commented out in the file, you will not be able to change the plugin name; it will always be the name of the file.</dd>
  3279. <dt><strong>Version</strong></dt>
  3280. <dd>The current version of your plugin. You are free to choose your own versioning convention.</dd>
  3281. <dd>To the right of these boxes will be some optional items that appear depending on the current mode of operation:
  3282. <dl>
  3283. <dd><strong>Restore point</strong> (in-database plugins only) : selecting this checkbox will (upon save) store the current code as a baseline to which you may &#8220;roll back&#8221; to at a later date. See <a href="#ied_plugin_restore">restore points</a>.</dd>
  3284. <dd><strong>Rename file</strong> (cache_dir plugins only) : by default, when you save a file in the plugin cache directory, it is overwritten with your changes. Once a plugin is released you would normally download a copy via <span class="caps"><span class="caps">FTP</span></span> from the plugin_cache_dir for safekeeping. If you then subsequently modify the plugin and increase the version number, you may wish to alter the filename as well. Checking this box will (upon save) rename the file in the plugin cache directory to reflect the current version number. See the <a href="#ied_plugin_setup">setup panel</a> for details on customising the filename format.</dd>
  3285. <dd><strong>Enable</strong> (in-database plugins only) : switch the plugin on or off after Save.</dd>
  3286. <dd><strong>(file name)</strong> (cache_dir plugins only) : the current filename you are editing.</dd>
  3287. </dl></dd>
  3288. <dt><strong>Description</strong></dt>
  3289. <dd>Very brief one-liner describing your plugin&#8217;s core function / reason for existence. 255 characters maximum.</dd>
  3290. <dt><strong>Author</strong> :</dt>
  3291. <dd>You!</dd>
  3292. <dt><strong>Website</strong></dt>
  3293. <dd>Your home page or plugin page. Will be hyperlinked to your Author name in the List panel.</dd>
  3294. <dt><strong>Plugin type</strong></dt>
  3295. <dd>Choose one of the types that best fits the intended use of your plugin. If you choose &#8216;Public&#8217; and try to access the admin side in your code, a warning will be issued when the plugin is saved so you can choose a more appropriate type.</dd>
  3296. <dt><strong>Flags</strong></dt>
  3297. <dd>Choose which plugin flags are to be associated with the plugin:
  3298. <dl>
  3299. <dd><strong>Has prefs</strong> : check this to indicate your plugin responds to the <i>plugin_prefs.your_plugin_name</i> event.</dd>
  3300. <dd><strong>Event notify</strong> : check this to indicate your plugin responds to the <i>plugin_lifecycle.your_plugin_name</i> event/steps.</dd>
  3301. </dl></dd>
  3302. <dt><strong>Load order</strong></dt>
  3303. <dd>Choose the recommended order in which you think your plugin should be loaded by users. Most of the time, the default of &#8216;5&#8217; is fine but for special cases where your plugin has to set up an environment or has to wait for other plugins to load first, you might require one of the numbers either side. Be aware that this is a <em>recommendation</em> and is overridable by the site administrator. If the plugin is already deployed on a site, the load order that is already set will be used regardless of the setting of this value; only new installations will be set to this value by default.</dd>
  3304. </dl>
  3305. <h3>Plugin code</h3>
  3306. <p>Your plugin code goes here. Write your masterpiece and either click the <b>Save</b> button at the top of the window (which saves everything: code, help, meta data, etc) or if you are simply updating just the code portion, use the <b>Save code</b> button at the top-right of the area for rapid background saving. During the save process, the textarea dims to show it is working, though you can still continue to type if you wish. When the textarea returns to full visibility, it means the save process is complete. Your code is (optionally) run through a rudimentary syntax checker when using the <b>Save Code</b> button and any error is highlighted. <span class="warning">If an error occurs, your plugin is not saved</span>.</p>
  3307. <p>The textarea is resizable by grabbing the <code>--- + ---</code> below the box and dragging. The box size will be remembered via a cookie for one year from your last adjustment.</p>
  3308. <p>Plugins are limited to 16Mb of code so there is also a character countdown just below the edit box. If you start approaching the limit(!), it might be worth considering splitting your plugin into a few parts or working for Micro$oft, where code bloat is acceptable.</p>
  3309. <p>In Firefox and IE7+ you can use the <em>Jump to line:</em> textbox. Enter a line number and press Enter to jump to that line in the code. In other browsers, ymmv.</p>
  3310. <h3>Textpack strings</h3>
  3311. <p>Before Textpattern 4.3.0, any time you displayed a plugin-specific string to the user it made sense to write your own <code>gTxt()</code> function which packaged up the strings into one area of your plugin. While convenient, this meant that anyone who wanted to use your plugin in another lanugage had to edit the plugin code and rewrite your strings into the target language, which caused upgrade hassles.</p>
  3312. <p>This is no longer an issue, as Textpattern now has Textpacks: redistributable text files that contain language strings for direct insertion into the database.</p>
  3313. <p class="warning">A word of caution: the Textpack area works a little differently to the rest of the Edit panel: most changes happen live as you type.</p>
  3314. <p>Before you begin you need to define a textpack prefix for the plugin. This is usually your three letter plugin prefix plus some unique identifier with which all strings in use by the plugin will begin. For example, the plugin composer uses <code>ied_plugin</code> (though it could have used <code>ied_pcomp</code> or <code>ied_pc</code>, etc). Note that using just your three letter prefix is probably not wise because your own future plugins might require a similarly-named replacement and the strings would clash. Of course, you might want to take advantage of this feature!</p>
  3315. <p>Once you enter the prefix and your cursor leaves the box, the composer will store the prefix and search the plugin code immediately for any references to such prefixed strings inside any function call with <code>gTxt</code> in it. Any it does find will be listed and you can immediately begin entering your replacement text in the currently selected language. Whenever the cursor leaves a text box its contents is saved directly to the database.</p>
  3316. <p>Textpack strings can be used on the Admin side, the Public site, or both. Choose the most appropriate location from the dropdown against each string.</p>
  3317. <p>If you create or rename a gTxt string in the code, when your cursor leaves the textarea the new string(s) will be created for you in the textpack area at the top of the list. Note however they are not written to the database until you supply a replacement string.</p>
  3318. <p>During the process of creating/renaming replacement strings, if it orphans another string then the orphan will be highlighted and an [x] button will appear next to it. If you wish to copy the old content out of the box and paste it into your renamed string, now is the time to do so. Once you&#8217;re sure you no longer need the string, hit the [x] button to immediately delete it from the database. It will be removed from <strong>all</strong> languages.</p>
  3319. <p>Please note:</p>
  3320. <ul>
  3321. <li>if you programmatically refer to strings (e.g. by concatenating string parts together to form the gTxt string name, or iterating over a loop and using variable substitution) then they will <em>not</em> be automatically be detected. You will have to use the &#8216;+&#8217; button to add such strings manually.</li>
  3322. <li>any programmatically-derived Textpack strings, or ones not inside a gTxt() function will show up with an [x] button. You should of course not delete these strings!</li>
  3323. <li>after you do a full save (i.e. not just a <b>Save Code</b>) all strings that are defined for the plugin will be displayed. Any derived strings will still be highlighted as potential orphans.</li>
  3324. <li>switching language from the select list will immediately load strings for that language.</li>
  3325. <li>clicking <b>Load</b> does the same task as switching language and is there in case you only have one language installed or wish to refresh the strings in the current language.</li>
  3326. </ul>
  3327. <p><strong>Importing strings from the current plugin</strong></p>
  3328. <p>The success of the automatic find facility relies on two things:</p>
  3329. <ul>
  3330. <li>that the strings are all prefixed.</li>
  3331. <li>your plugin&#8217;s Type is one of the <span class="caps"><span class="caps">AJAX</span></span> types (4 or 5).</li>
  3332. </ul>
  3333. <p>If you&#8217;re converting an old plugin to the Textpack methodology then you may have hundreds of strings that would be a bind to copy one by one. The composer can try to help you out, but it only works if you have a function or method in your plugin where the strings are replaced and returned. If you don&#8217;t have that, now might be the time to do so to save yourself some effort! It doesn&#8217;t have to be referenced in the code, it just has to exist and return a string for a given name.</p>
  3334. <p>Once you have that in place, here are some steps you can go through to convert your plugin and grab all the necessary strings en masse:</p>
  3335. <ol>
  3336. <li>inside your plugin&#8217;s gTxt() function, ensure that all keys are prefixed with your nominated plugin prefix.</li>
  3337. <li>globally replace any reference to <code>abc_plugin_gTxt(&amp;#39;</code> with <code>abc_plugin_gTxt(&amp;#39;abc_prefix_</code>, if not already.</li>
  3338. <li>save the plugin from the composer window.</li>
  3339. <li>enter the Textpack prefix if it&#8217;s not already set.</li>
  3340. <li>give the name of your gTxt function, e.g. <code>ied_plugin_gTxt</code> in the <i>Load strings from function</i> box. If your <code>gTxt()</code> method is inside a class, specify <code>class_name::method_name</code> instead.</li>
  3341. <li>hit &#8216;Go&#8217;.</li>
  3342. </ol>
  3343. <p>If the composer can execute your chosen function then it&#8217;ll do so and return the strings as defined inside the function, populate each Textpack string and save it for you in the database automatically. Once that&#8217;s done and all strings are populated you can then:</p>
  3344. <ol>
  3345. <li>globally replace any calls to <code>abc_plugin_gTxt</code> with the core <code>gTxt</code> in your plugin code.</li>
  3346. <li>delete your abc_plugin_gTxt function.</li>
  3347. </ol>
  3348. <p>Textpack strings are also written to the file of any plugin you are editing in the cache_dir when you Save the plugin, as long as you are using a recent template that has the <code>$plugin[&amp;#39;textpack&amp;#39;]</code> string in it.</p>
  3349. <p><strong>Switching language</strong></p>
  3350. <p>If at any time you want to see the installed textpack strings in other languages, simply use the select list to choose one. Any defined strings will be loaded into the textpack fields. You&#8217;ll see a counter whizzing up to show you how far it&#8217;s gone. As a translation aid, the equivalent string in your nominated default language (see <a href="#ied_plugin_setup">setup</a>) will be displayed as you hover over the textpack entry. You can choose to translate strings yourself or you can defer translation to other members of the community after the plugin is published. Textpacks can be linked to your textpattern.org plugin page by contributors and installed at any time from Textpattern&#8217;s <i>Languages</i> tab.</p>
  3351. <h3>Plugin help</h3>
  3352. <p>Documentation for detailing the plugin usage. Can (probably should!) be written using <a href="http://textpattern.com/textile-sandbox">Textile</a>. There are some <a href="http://textpattern.net/wiki/index.php?title=Creating_Plugin_Help_Guidelines">documentation guidelines</a> that serve as a good starting point. Note that the character countdown here is only approximate because when your plugin is saved and the help is converted to <span class="caps"><span class="caps">HTML</span></span>, it usually takes up more space than Textile; please check that your help file renders correctly when your plugin is exported.</p>
  3353. <p>The <em>Style</em> box is for any <span class="caps"><span class="caps">CSS</span></span> style rules you wish to apply to your documentation, although you should not need this with modern admin themes. You are encouraged to reuse the admin&#8217;s core <span class="caps"><span class="caps">CSS</span></span> rules as often as possible, but if there isn&#8217;t one that suits, it&#8217;s best to target your documentation specifically by surrounding the entire Plugin Help section with something like: <code>&amp;lt;div id=&amp;quot;abc_help&amp;quot;&amp;gt;h1. Docs go here...&amp;lt;/div&amp;gt;</code>.</p>
  3354. <p>Note that both the Plugin Help and Style are governed by a size limit. Since they are both stored in the same 64kB field, the size is shared between them. Styles are <em>not</em> passed through the Textile processor and you don&#8217;t need to add the <code>&amp;lt;style&amp;gt;</code> tags; the composer will do that for you (but see the <a href="#ied_plugin_notes">notes</a>).</p>
  3355. <h3>Distribution</h3>
  3356. <p>Once you have saved your plugin, there is a section right at the bottom that allows you to export your plugin in a variety of formats. Above the links is a language select box that governs how Textpacks are dealt with. If you choose the first (empty) item then no Textpack information is included in the exported file. The only exception is when you save Textpacks directly: the first item is a shortcut for &#8216;all languages&#8217; to save you having to select them all.</p>
  3357. <ul>
  3358. <li><strong>Plugin code for distribution</strong> : is a direct copy &#8216;n&#8217; paste area that contains your entire plugin + docs. You can take this entire area and paste it into the &#8216;Install plugin&#8217; box. Since this adds to the time it takes to do a full save of the plugin, this portion is optional and can be enabled from the <a href="#ied_plugin_setup">setup panel</a>.</li>
  3359. <li><strong>Export as abc_myplugin.txt</strong> : converts the plugin help to <span class="caps"><span class="caps">HTML</span></span> and saves the plugin to your computer as a redistributable text file for other Textpattern users to install. If you have chosen any Textpack languages from the adjacent select list, they will be bundled with your plugin and thus installed automatically along with the plugin.</li>
  3360. <li><strong>Export as abc_myplugin.txt (compressed)</strong> : converts the plugin help to <span class="caps"><span class="caps">HTML</span></span> and saves the plugin as a redistributable, gzipped text file for other Textpattern users to install. Useful for large plugins or to offer an alternative for people who have stringent size limits imposed by their host. Textpack strings are bundled too if you have chosen at least one language from the select list.</li>
  3361. <li><strong>Export as abc_myplugin.php</strong> : saves the plugin in a Textpattern standard template format. Useful for keeping the plugin for yourself &#8212; complete with Textiled help markup &#8212; so it can be later edited and re-issued / updated or shared with other developers who have the plugin composer or zem_tpl.php compiler. Textpack strings are bundled as part of the template if you choose.</li>
  3362. <li><strong>Export Textpack(s)</strong> : choose one or more languages from the select list to gather all your plugin&#8217;s Textpack strings and download them as a redistributable text file for other textpattern users to install or modify. You (or anyone else) can / will be able to upload links to Textpacks on textpattern.org so they are available from a central location.</li>
  3363. </ul>
  3364. <p>Note that when exporting as a standard plugin, the Textile processor attempts to decide if you have used Textile or not; it simply looks for a textiled header (<code>h1.</code> through <code>h6.</code>). Running pure <span class="caps"><span class="caps">HTML</span></span> through Textile may occasionally cause encoding issues depending on the original character set used so it is always best to try and stick to Textile as the documentation system.</p>
  3365. <h2 id="ied_plugin_restore">Restore points</h2>
  3366. <p>When a plugin is installed, a copy is kept in the database. If the plugin code (not help text) is subsequently edited, the plugin is considered &#8220;modified&#8221;; indicated in the plugin composer&#8217;s List panel. Sometimes you may wish to revert any changes back to the as-installed state.</p>
  3367. <p>Any time a plugin is marked as &#8216;modified&#8217;, the version number becomes clickable from the <a href="#ied_plugin_list">List panel</a>. Clicking it (and confirming you are sure) will wipe out any changes you made and return the plugin to its installed state.</p>
  3368. <p>During the editing process of your own plugins, it may be that at certain times you wish to put a stake in the ground and say &#8220;this is my current baseline that I might want to return to later&#8221;. Perhaps you are about to make some major edits or try something experimental and want an easy fallback mechanism. That&#8217;s where the &#8220;Restore point&#8221; checkbox comes in.</p>
  3369. <p>By checking the box when you save your plugin, the current code will become your new rollback point and the plugin will no longer be marked &#8220;modified&#8221;. Any changes made beforehand will not be recoverable so you will have to rely on your own backups if you wish to go back further. Any edits you make after creating a restore point can be undone by visiting the List panel and clicking the version number next to your plugin. Currently, only one rollback point can be stored in the database.</p>
  3370. <h2 id="ied_plugin_setup">Setup panel</h2>
  3371. <p>Clicking Setup from the main <a href="#ied_plugin_list">List panel</a> allows access to the plugin setting.</p>
  3372. <dl>
  3373. <dt><strong>Plugin editor</strong></dt>
  3374. <dd>You may choose to edit the code using a 3rd party syntax highlighter. Current support is for EditArea, CodeMirror and CodePress.</dd>
  3375. <dd>Choose the system here, and specify the <acronym title="s"><span class="caps"><span class="caps">URL</span></span></acronym> of the scripts and stylesheets in the box beneath. If your chosen editor requires more than one file to be loaded then list them all, separated by commas. If any of the files are stylesheets, prefix the name with <code>css:</code> so the plugin knows to insert a <code>&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; ...&amp;gt;</code> instead of a <code>&amp;lt;script&amp;gt;</code> tag.</dd>
  3376. <dd>If you want to supply any configuration options to the editor, type them in the <strong>Plugin editor configuration options</strong> textarea. Write them as name:value pairs separated by comma, exactly as you would if initialisaing the script according to their documentation.</dd>
  3377. <dt><strong>Plugin editor width</strong></dt>
  3378. <dd>The size of the textareas on the <a href="#ied_plugin_edit">edit panel</a>. Set to suit your screen resolution. Include the units (px, em, %, etc) if you wish; it&#8217;ll default to pixels if you omit them.</dd>
  3379. <dt><strong>Help editor</strong></dt>
  3380. <dd>You may choose to edit the help manually via Textile or use TinyMCE for a more <span class="caps"><span class="caps">WYSIWYG</span></span> experience.</dd>
  3381. <dd>In a similar manner to the plugin code editor you need to tell the composer the <span class="caps"><span class="caps">URL</span></span> to the javascript file. You can also supply configuration options. The defaults should give you a head start on how to use them: consult the TinyMCE documentation for more.</dd>
  3382. <dt><strong>Optional interface elements</strong></dt>
  3383. <dd>Choose which parts of the <a href="#ied_plugin_edit">Edit panel</a> you want to be visible in the interface.</dd>
  3384. <dt><strong>Perform lifecycle actions on</strong></dt>
  3385. <dd>When plugins are installed, enabled, disabled or deleted plugins can run code to perform installation or cleanup actions. Choose which ones you wish to permit when installing, enabling/disabling or deleting plugins from the composer&#8217;s <a href="#ied_plugin_list">List panel</a>.</dd>
  3386. <dd>The value of the &#8216;install&#8217; checkbox chosen here becomes the default option in the <em>Perform post-install actions</em> radio button under the <em>Install</em> textarea.</dd>
  3387. <dt><strong>Auto-enable plugins on install</strong></dt>
  3388. <dd>Whether to allow plugins to automatically switch on after installation, or to retain their previous status.</dd>
  3389. <dd>The setting chosen here becomes the default radio button selection under the <em>Install</em> textarea on the list panel.</dd>
  3390. <dt><strong>Syntax check on code save</strong></dt>
  3391. <dd>Whether to run the code through the syntax checker or not when you use the <b>Save Code</b> button. Highly recommended to save your admin side from breakage during development.</dd>
  3392. <dt><strong>Textpack language list</strong></dt>
  3393. <dd>Whether to only allow editing of strings from the currently installed languages, or all available Textpattern languages.</dd>
  3394. <dt><strong>Default Textpack language</strong></dt>
  3395. <dd>The primary language for your plugin strings (default = any). If you choose a language that is not installed (and you&#8217;re limiting the language list to only those installed) the default language will revert to your current admin-side language.</dd>
  3396. <dd>Note that if you set a language here, when you export Textpacks or build plugins, that will be the language expected to be installed in a user&#8217;s Textpattern. If you leave it at &#8216;any&#8217; the language marker is omitted from the Textpack and thus the strings bundled with the plugin (which may be in English) will install into whichever language is the user&#8217;s default (which may be something other than English). By forcing the default language, you force the language marker in the plugin. Thus if your users don&#8217;t have that language installed (e.g. someone only has nl-nl installed and not English) and they install your plugin, it will look at the installed language (nl-nl), compare it to the one in the plugin (en-gb), find it doesn&#8217;t match and will skip the Textpack installation. This will leave the user&#8217;s interface with lots of ugly <code>abc_plugin_some_item</code> strings instead of the actual translated content.</dd>
  3397. <dd>This could be handy if you are distributing Textpacks separately and have a good stock of them, or have loads of Textpacks bundled with the plugin, but for 95% of cases it is best to leave the default textpack language at <code>Any</code> so users of your plugin are guaranteed to get some translated strings, even if they are not in their &#8216;local&#8217; language. They can then at least translate them and make the Textpack available to other users.</dd>
  3398. <dt><strong><span class="caps"><span class="caps">PHP</span></span> export order</strong></dt>
  3399. <dd>When saving your plugin in the standard template format, this governs whether you prefer the code block to be at the top of the file and the help block below, or vice versa.</dd>
  3400. <dt><strong>Export plugin filename format</strong></dt>
  3401. <dt><strong>Export compressed filename format</strong></dt>
  3402. <dt><strong>Export template filename format</strong></dt>
  3403. <dt><strong>Textpack filename format</strong></dt>
  3404. <dd>These define the format of the filenames when you export plugins/Textpacks. The first is for when you export standard <span class="caps"><span class="caps">BASE</span></span>-64 plugins; the second is for compressed plugins; the third is for exporting a standard <span class="caps"><span class="caps">PHP</span></span> template, and finally for exporting Textpacks.</dd>
  3405. <dd>Wherever you type <code>{name}</code>, the plugin name will appear. Similarly, <code>{version}</code> will be replaced with the current plugin version number. And <code>{lang}</code> will be replaced with either 1) the chosen language code, like en-gb; 2) &#8216;all&#8217; if you chose to export all textpacks as one file; 3) an abbreviated list of countries to which the languages in the pack apply if you choose to export more than one, e.g. <code>en+fi+nl+fr+de</code>.</dd>
  3406. <dd>You can type anything you like in these boxes, but it&#8217;s more useful to include the replacement strings somewhere in each box so you don&#8217;t get name / version clashes. For example, if you don&#8217;t like the fact that zipped plugins are exported as <code>pfx_my_plugin_v0.1_zip.txt</code>, you can change it. Perhaps you may prefer <code>pfx_my_plugin-compressed-0.1.txt</code>. In which case, set the 2nd box to <code>{name}-compressed-{version}.txt</code>.</dd>
  3407. <dd>Note the extension should usually be specified so your system/browser knows the file&#8217;s type when it is exported, but it&#8217;s not mandatory as the <span class="caps"><span class="caps">MIME</span></span> type is given so (good) browsers should read that.</dd>
  3408. <dt><strong>Cache Textiled help path</strong></dt>
  3409. <dd>If you wish to take advantage of help cacheing, put the path to a temporary directory in the box. Empty the box if you prefer saves and exports to be slower!</dd>
  3410. <dd>Defaults to Textpattern&#8217;s temporary directory.</dd>
  3411. </dl>
  3412. <h2 id="ied_plugin_notes">Notes / known issues</h2>
  3413. <ol>
  3414. <li>When a plugin is saved to the plugin_cache_dir, if you have not put <code>&amp;lt;style&amp;gt;</code> markers in your <span class="caps"><span class="caps">CSS</span></span> block they will be added for you in the text area but <em>not</em> saved in the actual template until you save it again (exporting as a <span class="caps"><span class="caps">PHP</span></span> file is unaffected). So if you are in the habit of manually downloading the file from your <span class="caps"><span class="caps">FTP</span></span> client immediately after a save, just save the plugin again to be sure.</li>
  3415. <li>Loading Textpack strings from a gTxt function won&#8217;t work unless you (perhaps temporarily) switch your plugin to one of the <span class="caps"><span class="caps">AJAX</span></span> types.</li>
  3416. </ol>
  3417. <h2>Writing a plugin</h2>
  3418. <p>You should be aware of the <a href="http://forum.textpattern.com/viewtopic.php?id=9881">Plugin Author Resources</a> topic on the Textpattern Support Forum, and you might also want to have a look at the tutorials and guides for <a href="http://textpattern.net/wiki/index.php?title=Extending_Textpattern">Extending Textpattern</a> in the Textpattern documentation.</p>
  3419. <p>Happy plugin authoring :-)</p>
  3420. <h2>Authors</h2>
  3421. <p>Original plugin: <a href="http://inspired.kiev.ua/feedback/">Yura Linnyk</a><br />
  3422. Modifications (v0.5+): <a href="http://stefdawson.com/commentForm">Stef Dawson</a><br />
  3423. A touch of class: <a href="http://txp-plugins.netcarving.com/contact">Steve Dickinson</a><br />
  3424. Plus help from a host of forum contributors too numerous to mention. You know who you are ;-)</p>
  3425. <h2 id="changelog">Changelog</h2>
  3426. <ul>
  3427. <li>25 Feb 2006 | 0.1 | Initial release</li>
  3428. <li>25 Feb 2006 | 0.2 | Added &#8216;save as&#8217; option</li>
  3429. <li>17 Mar 2006 | 0.3 | Added &#8216;save as php&#8217;</li>
  3430. <li>10 Apr 2006 | 0.4 | Added support for plugin_cache_dir</li>
  3431. <li>04 Jan 2008 | 0.5 | Full support for standard template; compressed plugins; library plugins; Textile help and styling; integration with net-carver&#8217;s Plugin Help Viewer</li>
  3432. <li>07 Jan 2008 | 0.6 | Built-in help viewer (thanks net-carver); support for Edit Area &amp; CodePress(ish) (thanks variaas); Help block/Code block position switchable on export; added Setup prefs page; line break/style bugfixes</li>
  3433. <li>08 Jan 2008 | 0.7 | Re-importing plugins now retains style block; added &#8216;admin side plugin with public-side type&#8217; warning; changed button styling and positions (all thanks net-carver); gTxt() pref labels and &#8216;intelligent&#8217; prefs (go jQuery go!); cached plugins now also have direct export from Edit panel; &#8216;don&#8217;t textile <span class="caps"><span class="caps">HTML</span></span>&#8217; check; minor bugfixes</li>
  3434. <li>09 Jan 2008 | 0.71 | Textarea width can now be controlled from prefs, and height from a drag bar; default width increased to 110 chars; publish plugins from the list panel; Install button removed when prefs all correctly installed (all thanks variaas / iblastoff)</li>
  3435. <li>10 Jan 2008 | 0.72 | Fixed bug if plugin has no help; style section no longer stored/exported if it&#8217;s not in use (thanks the_ghost / iblastoff)</li>
  3436. <li>11 Jan 2008 | 0.73 | Added support for <em>reading</em> an optional revision from the template parser and appending it to the version (thanks net-carver); more gTxt() strings converted</li>
  3437. <li>28 Mar 2008 | 0.74 | Fixed empty plugin code if Style block left blank ; fixed strip/slash/encoding errors (thanks the_ghost/ruud) ; fixed crlf newlines in code block (thanks hakjoon/ruud)</li>
  3438. <li>28 May 2008 | 0.75 | Added &#8216;modified&#8217; to the Version column (thanks uli) ; added ability to rename files in the plugin cache dir when the version changes</li>
  3439. <li>03 Nov 2008 | 0.8 | Added support for recommended plugin load order ; added Admin-only plugin type; added ability to override filename format on export ; added restore point/rollback (thanks maverick) ; added character count (thanks pepebe) ; rationalised the list and edit panels ; sped up export/save routines ; fixed a few corner case bugs (e.g. empty plugin name, missing quotes in template options)</li>
  3440. <li>03 Jan 2009 | 0.81 | Added textile cacheing to improve performance with large help files ; profiled code and improved speed in various functions</li>
  3441. <li>23 Feb 2009 | 0.82 | Can now create new template files on the fly (thanks azw) ; fixed Textile limit on large help files ; fixed database calls for MySQL strict mode (thanks Gocom / azw)</li>
  3442. <li>11 Apr 2009 | 0.83 | Fixed help file <span class="caps"><span class="caps">CSS</span></span> output so it validates ; use <code>$prefs</code> instead of <code>$GLOBALS</code> internally ; check if plugin_cache_dir exists before trying to use it on the list panel</li>
  3443. <li>29 Aug 2009 | 0.9 | Requires Txp 4.2.0+ ; added support for plugin prefs/lifecycle and larger plugin code ; fixed <span class="caps"><span class="caps">CSS</span></span> delimiter for backwards compatibility ; new plugin template details used</li>
  3444. <li>11 Feb 2010 | 0.91 | Added Jump To Line capability &#8212; in most browsers (thanks thebombsite)</li>
  3445. <li>03 Nov 2010 | 0.92 | Fixed Options link for plugins from cache dir and fixed escaping of exported php files (both thanks maniqui) ; fixed escaping when importing from <span class="caps"><span class="caps">PHP</span></span> file ; cosmetic tweaks (plugin_cache_dir section only displayed if there are valid <span class="caps"><span class="caps">PHP</span></span> files, and View Help link adjusted) ; fixed jQuery on setup panel ; extended resizer cookie to one year expiry</li>
  3446. <li>23 Nov 2010 | 0.93 | Fixed setup screen &#8216;undefined&#8217; bug (thanks MarcoK)</li>
  3447. <li>16 Mar 2013 | 1.00 | Added Textpack support and management ; permitted more installation / creation options ; retooled the UI for Txp 4.5.x ; added dedicated <b>Save Code</b> button with syntax check for rapid saving via <span class="caps"><span class="caps">AJAX</span></span> ; made &#8216;distribution&#8217; and &#8216;style&#8217; blocks optional to speed up full saves ; added support for firing lifecycle events ; supports the two new <span class="caps"><span class="caps">AJAX</span></span> plugin types introduced in Txp 4.5.0 ; more options for multi edit changestatus</li>
  3448. <li>25 Sep 2013 | 1.01 | Fixed textpack display string bug on admin side</li>
  3449. <li>27 Sep 2013 | 1.02 | Added public tags ied_plugin_list (from DB only for now), ied_plugin_info, ied_plugin_textpacks.</li>
  3450. <li>28 Sep 2013 | 1.03 | Added public tags ied_plugin_download, ied_plugin_download_link</li>
  3451. <li>15 Oct 2013 | 1.04 | Fixed bug preventing help being packaged in downloaded plugin from public side</li>
  3452. <li>21 Oct 2013 | 1.05 | More sensible default for lang export from ied_plugin_download_link tag</li>
  3453. </ul>
  3454. # --- END PLUGIN HELP ---
  3455. -->
  3456. <?php
  3457. }
  3458. ?>