Content of themetoolkit.php from minimalissimplistic.zip

without / with line numbers
  1. <?php
  2. /*
  3. Hack Name: WordPress Theme Toolkit
  4. Hack URI: http://planetozh.com/blog/my-projects/wordpress-theme-toolkit-admin-menu/
  5. What is does: Helps theme authors set up an admin menu. Helps theme users customise the theme.
  6. Version: 1.2
  7. Author: Ozh
  8. Author URI: http://planetOzh.com/
  9. */

  10. /************************************************************************************
  11. *                             DO NOT MODIFY THIS FILE !
  12. ************************************************************************************/

  13. /* RELEASE HISTORY :
  14. * 1.0 : initial release
  15. * 1.1 : update for WordPress 2.0 compatibility
  16. * 1.11 : added {separator} template
  17. * 1.12 : more or less minor bug fixing (one when no plugin activated, other with rare mod_security issue) and better compliancy with WP 2.0 roles
  18. * 1.2 : check for WordPress 2.3, a few improvements & typos, better error handling, improved security, added 'select', added localization
  19. */

  20. if (!function_exists('themetoolkit')) {
  21.     function themetoolkit($theme='',$array='',$file='') {
  22.         global ${$theme};
  23.         if ($theme == '' or $array == '' or $file == '') {
  24.             die ($this->__('No theme name, theme option, or parent defined in Theme Toolkit'));
  25.         }
  26.         ${$theme} = new ThemeToolkit($theme,$array,$file);
  27.     }
  28. }

  29. if (!class_exists('ThemeToolkit')) {
  30.     class ThemeToolkit{

  31.         var $option, $infos;

  32.         function ThemeToolkit($theme,$array,$file){
  33.             
  34.             global $wp_version;
  35.             // is it WP 2.0+ and do we have plugins like "../themes/foo/functions.php" running ?
  36.             if ( $wp_version >= 2 and count(@preg_grep('#^\.\./themes/[^/]+/functions.php$#', get_settings('active_plugins'))) > 0 ) {
  37.                 wp_cache_flush();
  38.                 $this->upgrade_toolkit();
  39.             }
  40.             
  41.             $this->infos['path'] = '../themes/' . basename(dirname($file));
  42.             load_plugin_textdomain('themetoolkit', 'wp-content/themes/'.basename(dirname($file)));
  43.             // I use load_plugin_textdomain() and not load_theme_domain() to be able to specify a filename, ie themetoolkit-{locale}.mo

  44.             /* Create some vars needed if an admin menu is to be printed */
  45.             if ($array['debug']) {
  46.                 if ((basename($file)) == $_GET['page']) $this->infos['debug'] = 1;
  47.                 unset($array['debug']);
  48.             }
  49.             if ((basename($file)) == $_GET['page']){
  50.                 $this->infos['menu_options'] = $array;
  51.                 $this->infos['classname'] = $theme;
  52.             }
  53.             $this->option=array();

  54.             /* Check this file is registered as a plugin, do it if needed */
  55.             $this->pluginification();

  56.             /* Get infos about the theme and particularly its 'shortname'
  57.              * which is used to name the entry in wp_options where data are stored */
  58.             $this->do_init();

  59.             /* Read data from options table */
  60.             $this->read_options();

  61.             /* Nonce wrapper for old WP versions */
  62.             if ( !function_exists('wp_nonce_field') ) {
  63.                 $this->nonce = -1;
  64.             } else {
  65.                 $this->nonce = 'theme-toolkit';
  66.             }
  67.             add_action('explain_nonce_theme-toolkit', array(&$this,'nonce_explain'));

  68.             /* Are we in the admin area ? Add a menu then ! */
  69.             $this->file = $file;
  70.             add_action('admin_menu', array(&$this, 'add_menu'));
  71.             

  72.         }

  73.         /* Nonce confirmation */
  74.         function nonce_explain() {
  75.             return( sprintf($this->__('You are about to modify your preferences or settings for your theme "%s". Are you sure you want to do this ?'), $this->infos['theme_name']) );
  76.         }

  77.         /* Nonce wrapper function, for older WP compatibility */
  78.         function nonce_field($action = -1) {
  79.             if ( !function_exists('wp_nonce_field') ) {
  80.                 return;
  81.             } else {
  82.                 return wp_nonce_field($action);
  83.             }
  84.         }
  85.         
  86.         /* Wrappers for translating functions */
  87.         function __($string) {
  88.             return __($string, 'themetoolkit');
  89.         }

  90.         /* Add an entry to the admin menu area */
  91.         function add_menu() {
  92.             global $wp_version;
  93.             if ( $wp_version >= 2 ) {
  94.                 $level = 'edit_themes';
  95.             } else {
  96.                 $level = 9;
  97.             }
  98.             add_theme_page(sprintf($this->__('Configure %s') , $this->infos['theme_name']), $this->__($this->infos['theme_name']), 'edit_themes', basename($this->file), array(&$this,'admin_menu'));
  99.             /* Thank you MCincubus for the array($this,'admin_menu') bit */
  100.         }

  101.         /* Get infos about this theme */
  102.         function do_init() {
  103.             $themes = get_themes();
  104.             $shouldbe= basename($this->infos['path']);
  105.             foreach ($themes as $theme) {
  106.                 $current= basename($theme['Template Dir']);
  107.                 if ($current == $shouldbe) {
  108.                     if (get_settings('template') == $current) {
  109.                         $this->infos['active'] = TRUE;
  110.                     } else {
  111.                         $this->infos['active'] = FALSE;
  112.                     }
  113.                 $this->infos['theme_name'] = $theme['Name'];
  114.                 $this->infos['theme_shortname'] = $current;
  115.                 $this->infos['theme_site'] = $theme['Title'];
  116.                 $this->infos['theme_version'] = $theme['Version'];
  117.                 $this->infos['theme_author'] = preg_replace("#>\s*([^<]*)</a>#", ">\\1</a>", $theme['Author']);
  118.                 }
  119.             }
  120.         }

  121.         /* Read theme options as defined by user and populate the array $this->option */
  122.         function read_options() {
  123.             $options = get_option('theme-'.$this->infos['theme_shortname'].'-options');
  124.             $options['_________junk-entry________'] = 'ozh is my god';
  125.             foreach ($options as $key=>$val) {
  126.                 $this->option["$key"] = stripslashes($val);
  127.             }
  128.             array_pop($this->option);
  129.             return $this->option;
  130.             /* Curious about this "junk-entry" ? :) A few explanations then.
  131.              * The problem is that get_option always return an array, even if
  132.              * no settings has been previously saved in table wp_options. This
  133.              * junk entry is here to populate the array with at least one value,
  134.              * removed afterwards, so that the foreach loop doesn't go moo. */
  135.         }
  136.         
  137.         /* Prepare options for display */
  138.         function sanitize_options() {
  139.             foreach($this->option as $key=>$val) {
  140.                 $this->option["$key"] = $this->sanitize($val);
  141.             }
  142.         }

  143.         /* Write theme options as defined by user in database */
  144.         function store_options($array) {
  145.             // Remove nonce informations before storing
  146.             if (isset($array['_wpnonce'])) unset($array['_wpnonce']);
  147.             if (isset($array['_wp_http_referer'])) unset($array['_wp_http_referer']);
  148.             
  149.             update_option('theme-'.$this->infos['theme_shortname'].'-options','');
  150.             if (update_option('theme-'.$this->infos['theme_shortname'].'-options',$array)) {
  151.                 return $this->__('Options successfully stored');
  152.             } else {
  153.                 return $this->__('Could not save options !');
  154.             }
  155.         }

  156.         /* Delete options from database */
  157.         function delete_options() {
  158.             /* Remove entry from database */
  159.             delete_option('theme-'.$this->infos['theme_shortname'].'-options');
  160.             /* Unregister this file as a plugin (therefore remove the admin menu) */
  161.             $this->depluginification();
  162.             /* Revert theme back to default theme if this theme was activated */
  163.             if ($this->infos['active']) {
  164.                 update_option('template', 'default');
  165.                 update_option('stylesheet', 'default');
  166.                 do_action('switch_theme', 'Default');
  167.             }
  168.             /* Go back to Theme admin */
  169.             print '<meta http-equiv="refresh" content="0;URL=themes.php?activated=true">';
  170.             echo "<script> self.location(\"themes.php?activated=true\");</script>";
  171.             exit;
  172.         }

  173.         /* Check if the theme has been loaded at least once (so that this file has been registered as a plugin) */
  174.         function is_installed() {
  175.             global $wpdb;
  176.             $where = 'theme-'.$this->infos['theme_shortname'].'-options';
  177.             $check = $wpdb->get_var("SELECT COUNT(*) FROM $wpdb->options WHERE option_name = '$where'");
  178.             if ($check == 0) {
  179.                 return FALSE;
  180.             } else {
  181.                 return TRUE;
  182.             }
  183.         }

  184.         /* Theme used for the first time (create blank entry in database) */
  185.         function do_firstinit() {
  186.             global $wpdb;
  187.             $options = array();
  188.             foreach(array_keys($this->option) as $key) {
  189.                 $options["$key"]='';
  190.             }
  191.             add_option('theme-'.$this->infos['theme_shortname'].'-options',$options, 'Options for theme '.$this->infos['theme_name']);
  192.             $this->firstinit = true;
  193.             return sprintf($this->__('Theme options added in database (1 entry in table "%s"'), $wpdb->options);
  194.         }

  195.         /* The mother of them all : the Admin Menu printing func */
  196.         function admin_menu () {
  197.             global $cache_settings, $wpdb;
  198.             
  199.             /* Process things when things are to be processed */
  200.             if (@$_POST['action']) {
  201.                 check_admin_referer($this->nonce);
  202.                 if (@$_POST['action'] == 'store_option') {
  203.                     unset($_POST['action']);
  204.                     $msg = $this->store_options($_POST);
  205.                 } elseif (@$_POST['action'] == 'delete_options') {
  206.                     $this->delete_options();
  207.                 }
  208.             } elseif (!$this->is_installed()) {
  209.                 $msg = $this->do_firstinit();
  210.             }
  211.             
  212.             // ze debug print_r
  213.             // echo "<pre>";print_r($this);echo "</pre>";

  214.             if (@$msg) print "<div class='updated'><p><b>" . $msg . "</b></p></div>\n";

  215.             echo '<div class="wrap">';
  216.             if ($this->firstinit) {
  217.                 echo '<h2>'.$this->__('Thank you !').'</h2>';
  218.                 echo '<p>';
  219.                 printf($this->__('Thank you for installing "%s", a theme for WordPress.'),  $this->infos['theme_site'] ); 
  220.                 printf($this->__('This theme was made by %s'), $this->infos['theme_author']);
  221.                 echo "</p>\n";

  222.                 if (!$this->infos['active']) { /* theme is not active */
  223.                     echo '<p>('.$this->__('Please note that this theme is currently <strong>not activated</strong> on your site as the default theme.').'</p>';
  224.                 }
  225.             }

  226.             $cache_settings = '';
  227.             $check = $this->read_options();
  228.             $this->sanitize_options();
  229.             
  230.             echo '<h2>'.$this->__('Configure').' <em>'.$this->__($this->infos['theme_name']).'</em> </h2>';
  231.             echo '<p>'.$this->__('This theme allows you to configure some variables to suit your blog, which are :').'</p>
  232.             <form action="" name="ttkform" id="ttkform" method="post">';
  233.             echo '
  234.             <table cellspacing="2" cellpadding="5" border="0" width=100% class="editform">';

  235.             /* Print form, here comes the fun part :) */
  236.             
  237.             $firstelement = '';        
  238.             foreach ($this->infos['menu_options'] as $key=>$val) {
  239.                 if (!$firstelement) $firstelement = $key;
  240.                 $items='';
  241.                 preg_match('/\s*([^{#]*)\s*({([^}]*)})*\s*([#]*\s*(.*))/', $val, $matches);
  242.                 if ($matches[3]) {
  243.                     $items = split("\|", $matches[3]);
  244.                 }

  245.             echo "<tr valign='top'><th scope='row' width='33%'>\n";
  246.                 if (@$items) {
  247.                     $type = array_shift($items);
  248.                     switch ($type) {
  249.                     
  250.                     case 'text':
  251.                         $s = array_shift($items);
  252.                         $m = array_shift($items);
  253.                         echo "<label for='$key'>".$this->__($matches[1])."</label></th>\n<td>";
  254.                         echo "<input type='text' name='$key' id='$key' size='$s' maxlength='$m' value='" . $this->option[$key] . "' />";
  255.                         break;
  256.                     
  257.                     case 'separator':
  258.                         echo '<h3>'.$this->__($matches[1])."</h3></th>\n<td>&nbsp;</td>";
  259.                         break;

  260.                     case 'select':
  261.                     case 'dropdown':
  262.                         echo $this->__($matches[1])."</th>\n<td>";
  263.                         echo "<select id='${key}' name='${key}'>";
  264.                         while ($items) {
  265.                             $v=array_shift($items);
  266.                             $t=array_shift($items);
  267.                             $selected='';
  268.                             if ($v == $this->option[$key]) $selected=' selected';
  269.                             echo "<option value='$v' $selected>".$this->__($t)."</option>\n";
  270.                         }
  271.                         echo "</select>\n";
  272.                         break;

  273.                     case 'radio':
  274.                         echo $this->__($matches[1])."</th>\n<td>";
  275.                         while ($items) {
  276.                             $v=array_shift($items);
  277.                             $t=array_shift($items);
  278.                             $checked='';
  279.                             if ($v == $this->option[$key]) $checked='checked';
  280.                             echo "<label for='${key}${v}'><input type='radio' id='${key}${v}' name='$key' value='$v' $checked />".$this->__($t).'</label>';
  281.                             if (@$items) echo "<br />\n";
  282.                         }
  283.                         break;

  284.                     case 'textarea':
  285.                         $rows=array_shift($items);
  286.                         $cols=array_shift($items);
  287.                         echo "<label for='$key'>".$this->__($matches[1])."</label></th>\n<td>";
  288.                         echo "<textarea name='$key' id='$key' rows='$rows' cols='$cols'>" . $this->option[$key] . "</textarea>";
  289.                         break;

  290.                     case 'checkbox':
  291.                         echo $this->__($matches[1])."</th>\n<td>";
  292.                         while ($items) {
  293.                             $k=array_shift($items);
  294.                             $v=array_shift($items);
  295.                             $t=array_shift($items);
  296.                             $checked='';
  297.                             if ($v == $this->option[$k]) $checked='checked';
  298.                             echo "<label for='${k}${v}'><input type='checkbox' id='${k}${v}' name='$k' value='$v' $checked />".$this->__( $t).'</label>';
  299.                             if (@$items) echo "<br />\n";
  300.                         }
  301.                         break;
  302.                     
  303.                     default:
  304.                         echo '<span style="color:red">'.$this->__('Unrecognized token').'</span></th><td class="updated"> '.$matches[2];
  305.                         break;
  306.                     }
  307.                 } else {
  308.                     // default to plain input text field
  309.                     echo "<label for='$key'>".$this->__($matches[1])."</label></th>\n<td>";
  310.                     echo "<input type='text' name='$key' id='$key' value='" . $this->option[$key] . "' />";
  311.                 }

  312.                 if ($matches[5]) echo '<br/>'. $this->__($matches[5]);
  313.                 echo "</td></tr>\n";
  314.             }
  315.             echo '</table>
  316.             <p class="submit"><input type="submit" value="'.$this->__('Store Options').' &raquo;" /></p>';
  317.             $this->nonce_field($this->nonce);
  318.             echo '
  319.             <input type="hidden" name="action" value="store_option">
  320.             </form>';

  321.             if ($this->infos['debug'] and $this->option) {
  322.                 $g = '<span style="color:#006600">';
  323.                 $b = '<span style="color:#0000CC">';
  324.                 $o = '<span style="color:#FF9900">';
  325.                 $r = '<span style="color:#CC0000">';
  326.                 echo '<h2>'.$this->__("Programmer's corner").'</h2>';
  327.                 echo '<p>'.sprintf($this->__('The array <em>$%s->option</em> is actually populated with the following keys and values'),$this->infos['classname']).' :</p>
  328.                 <p><pre class="updated">';
  329.                 $count = 0;
  330.                 foreach ($this->option as $key=>$val) {
  331.                     $val=str_replace('<','&lt;',$val);
  332.                     if ($val) {
  333.                         print '<span class="ttkline">'.$g.'$'.$this->infos['classname'].'</span>'.$b.'-></span>'.$g.'option</span>'.$b.'[</span>'.$g.'\'</span>'.$r.$key.'</span>'.$g.'\'</span>'.$b.']</span>'.$g.' = "</span>'. $o.$val.'</span>'.$g."\"</span></span>\n";
  334.                         $count++;
  335.                     }
  336.                 }
  337.                 if (!$count) print "\n\n";
  338.                 echo '</pre><p>'.$this->__('To disable this report (for example before packaging your theme and making it available for download), remove the line "&nbsp;<em>\'debug\' => \'debug\'</em>&nbsp;" in the array you edited at the beginning of this file.').'</p>';
  339.             }

  340.             echo '<h2>'.$this->__('Delete Theme options').'</h2>
  341.             <p>'.sprintf($this->__('To completely remove these theme options from your database (reminder: they are all stored in a single entry, in WordPress options table <em>%s</em>), click on the following button. You will be then redirected to the <a href="themes.php">Themes admin interface</a>'), $wpdb->options);
  342.             if ($this->infos['active']) {
  343.                 echo ' '.$this->__('and the Default theme will have been activated');
  344.             }
  345.             echo '.</p>';
  346.             echo '<p>'.$this->__('<strong>Special notice for people allowing their readers to change theme</strong> (i.e. using a Theme Switcher on their blog)');
  347.             echo '<br/>';
  348.             echo $this->__('Unless you really remove the theme files from your server, this theme will still be available to users, and therefore will self-install again as soon as someone selects it.').' ';
  349.             echo $this->__('Also, all custom variables as defined in the above menu will be blank, this could lead to unexpected behaviour.').' ';
  350.             echo $this->__('Press "Delete" only if you intend to remove the theme files right after this.').'</p>
  351.             <form action="" method="post">';
  352.             $this->nonce_field($this->nonce);
  353.             echo '
  354.             <input type="hidden" name="action" value="delete_options">
  355.             <p class="submit"><input type="submit" value="'.$this->__('Delete Options').' &raquo;" onclick="return confirm(\''.$this->__('Are you really sure you want to delete ?').'\');"/></p>
  356.             </form>';
  357.             
  358.             // Little javascript bit to set focus on first element, if applicable
  359.             echo "
  360.             <script type='text/javascript'>
  361.             if (document.getElementById('$firstelement')) {
  362.                 document.getElementById('$firstelement').focus();
  363.             }
  364.             </script>";
  365.             
  366.             echo '<h2>'.$this->__('Credits').'</h2>';
  367.             echo '<p>'.sprintf($this->__('%s has been created by %s'),$this->infos['theme_site'],$this->infos['theme_author']).'. ';
  368.             printf($this->__('This administration menu uses <a href="%s">WordPress Theme Toolkit</a> by %s.'),'http://planetozh.com/blog/my-projects/wordpress-theme-toolkit-admin-menu/','<a href="http://planetozh.com/" title="planetOzh">Ozh</a>');
  369.             echo '</p>
  370.             </div>';
  371.         }

  372.         /***************************************
  373.          * Self-Pluginification
  374.          *
  375.          * Prior to WP 2.0 this files registers
  376.          * itself as a plugin. Ow, that's smart.
  377.          **************************************/
  378.         function pluginification () {
  379.             global $wp_version;
  380.             if ($wp_version<2) {        
  381.                 $us = $this->infos['path'].'/functions.php';
  382.                 $them = get_settings('active_plugins');
  383.                 /* Now, are we members of the PPC (Plugins Private Club) yet ? */
  384.                 if (!in_array($us,$them)) {
  385.                     /* No ? Jeez, claim member card ! */
  386.                     $them[]=$us;
  387.                     update_option('active_plugins',$them); 
  388.                     /* Wow. We're l33t now. */ 
  389.                     return TRUE; 
  390.                 } else { 
  391.                     return FALSE; 
  392.                 } 
  393.             }
  394.         } 
  395.          
  396.         /*************************************** 
  397.          * De-Pluginification
  398.          **************************************/ 
  399.         function depluginification () {
  400.             global $wp_version;
  401.             if ($wp_version<2) {
  402.                 $us = $this->infos['path'].'/functions.php';
  403.                 $them = get_settings('active_plugins');
  404.                 if (in_array($us,$them)) {
  405.                     $here = array_search($us,$them);
  406.                     unset($them[$here]);
  407.                     update_option('active_plugins',$them);
  408.                     return TRUE;
  409.                 } else {
  410.                     return FALSE;
  411.                 }
  412.             }
  413.         }
  414.         
  415.         /* attribute_escape() compatibility for older installs */
  416.         function sanitize($text) {
  417.             if (function_exists('attribute_escape')) {
  418.                 return attribute_escape($text);
  419.             } else {
  420.                 // All this taken from wp_specialchars()
  421.                 $text = str_replace('&&', '&#038;&', $text);
  422.                 $text = str_replace('&&', '&#038;&', $text);
  423.                 $text = preg_replace('/&(?:$|([^#])(?![a-z1-4]{1,8};))/', '&#038;$1', $text);
  424.                 $text = str_replace('<', '&lt;', $text);
  425.                 $text = str_replace('>', '&gt;', $text);
  426.                 $text = str_replace('"', '&quot;', $text);
  427.                 $text = str_replace("'", '&#039;', $text);
  428.                 return $text;
  429.             }            
  430.         }

  431.         /* Clean plugins lists in order to work with WordPress 2.0 */
  432.         function upgrade_toolkit () {
  433.             $plugins=get_settings('active_plugins');
  434.             $delete=@preg_grep('#^\.\./themes/[^/]+/functions.php$#', $plugins);
  435.             $result=array_diff($plugins,$delete);
  436.             $temp = array();
  437.             foreach($result as $item) $temp[]=$item;
  438.             $result = $temp;
  439.             update_option('active_plugins',$result);
  440.             wp_cache_flush;
  441.         }
  442.     }
  443. }

  444. /***** Pretend we're a plugin to abuse WordPress updater *****/

  445. /*
  446. Plugin Name: WordPress Theme Toolkit
  447. Plugin URI: http://planetozh.com/blog/my-projects/wordpress-theme-toolkit-admin-menu/
  448. Description: <strong>This is not a plugin</strong>, do not put in <code>/wp-content/plugins/</code>, do not activate!</span>
  449. */



  450. ?>
  451.  

Return to listing of minimalissimplistic.zip