Advertisement
Guest User

Wordpress Meta Key Revision Manager

a guest
Jul 12th, 2015
428
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 12.93 KB | None | 0 0
  1. <?php
  2. /*
  3. Plugin Name: Meta Key Revision Manager
  4. Plugin URI: https://core.trac.wordpress.org/ticket/20564
  5. Description: Select which meta keys we want to keep a Revision record of. Also adds meta items and their values to the revision browser screen.
  6. Version: 1.0
  7. Author: Crowd Favorite
  8. */
  9. if (!class_exists('cf_revisions')) { // double-load check
  10.  
  11. define('CF_REVISIONS_DEBUG', false);
  12.  
  13. function cfr_register_metadata($postmeta_key, $display_func = '') {
  14.     static $cfr;
  15.     if (empty($cfr)) {
  16.         $cfr = cf_revisions::get_instance();
  17.     }
  18.     return $cfr->register($postmeta_key, $display_func);
  19. }
  20.  
  21. class cf_revisions
  22. {
  23.     private static $_instance;
  24.     protected $postmeta_keys = array();
  25.  
  26.     public function __construct()
  27.     {
  28.         if (is_admin())
  29.         {      
  30.             # revision display
  31.             global $pagenow;
  32.             if ($pagenow == 'revision.php') {
  33.                 add_filter('_wp_post_revision_fields', array($this, 'post_revision_fields'), 10, 1);
  34.                 add_filter('_wp_post_revision_field_postmeta', array($this, 'post_revision_field'), 1, 2);
  35.             }
  36.         }
  37.        
  38.         add_filter( 'wp_post_revision_meta_keys', function( $keys )
  39.         {
  40.             $keys = array_merge($keys, cf_revisions_admin::selected_keys());
  41.             return $keys;
  42.         });
  43.  
  44.         add_filter( 'wp_get_revision_ui_diff', function($return, $compare_from, $compare_to)
  45.         {
  46.             $template = '
  47.                 <table class="diff">
  48.                     <colgroup>
  49.                         <col class="content diffsplit left">
  50.                         <col class="content diffsplit middle">
  51.                         <col class="content diffsplit right">
  52.                     </colgroup>
  53.                     <tbody>
  54.                         <tr>
  55.                             <td>{COMPARE_FROM_VALUE}</td>
  56.                             <td></td>
  57.                             <td>{COMPARE_TO_VALUE}</td>
  58.                         </tr>
  59.                     </tbody>
  60.                 </table>';
  61.  
  62.             $metas_from = [];
  63.             foreach(get_post_custom_keys($compare_from->ID) as $key) {
  64.                 $metas_from[$key] = get_post_meta($compare_from->ID, $key, true);
  65.             }
  66.  
  67.             $template = str_replace('{COMPARE_FROM_VALUE}', '<pre>' . print_r($metas_from, true) . '</pre>', $template);
  68.  
  69.             if( $compare_to )
  70.             {
  71.                 $metas_to = [];
  72.                 foreach(get_post_custom_keys($compare_to->ID) as $key) {
  73.                     $metas_to[$key] = get_post_meta($compare_to->ID, $key, true);
  74.                 }
  75.  
  76.                 $template = str_replace('{COMPARE_TO_VALUE}', '<pre>' . print_r($metas_to, true) . '</pre>', $template);
  77.             }
  78.  
  79.             $return[] = array(
  80.                 'id'    => 'meta',
  81.                 'name'  => 'Meta',
  82.                 'diff'  => $template
  83.             );
  84.            
  85.             return $return;
  86.  
  87.         }, 10, 3);
  88.     }
  89.  
  90.     public function register($postmeta_key, $display_func = '') {
  91.         if (!in_array($postmeta_key, $this->postmeta_keys, true)) {
  92.             $this->postmeta_keys[] = compact('postmeta_key', 'display_func');
  93.         }
  94.         return true;
  95.     }
  96.  
  97.     /**
  98.      * This is a paranoid check. There will be no object to register the
  99.      * actions and filters if nobody adds any postmeta to be handled
  100.      *
  101.      * @return bool
  102.      */
  103.     public function have_keys() {
  104.         return (bool) count($this->postmeta_keys);
  105.     }
  106.    
  107.     /**
  108.      * Return only the keys.
  109.      */
  110.     public function registered_keys() {
  111.         $keys = array();
  112.         if (count($this->postmeta_keys)) {
  113.             foreach ($this->postmeta_keys as $key) {
  114.                 extract($key);
  115.                 $keys[] = $postmeta_key;
  116.             }
  117.         }
  118.         return array_unique($keys);
  119.     }
  120.  
  121.     public function post_revision_fields($fields) {
  122.         $fields['postmeta'] = 'Post Meta';
  123.         return $fields;
  124.     }
  125.  
  126.     public function post_revision_field($field_id, $field) {
  127.         if ($field != 'postmeta' || !$this->have_keys()) {
  128.             return;
  129.         }
  130.    
  131.         remove_filter('_wp_post_revision_field_postmeta', 'htmlspecialchars', 10, 2);
  132.            
  133.         $html = '<ul style="white-space: normal; margin-left: 1.5em; list-style: disc outside;">';
  134.         foreach ($this->postmeta_keys as $postmeta_type) {
  135.             $postmeta_key = $postmeta_type['postmeta_key'];
  136.             $postmeta = maybe_unserialize(get_metadata('post', intval($_GET['revision']), $postmeta_key, true));
  137.  
  138.             if (!empty($postmeta)) {
  139.                 if (!empty($postmeta_type['display_func']) && function_exists($postmeta_type['display_func'])) {
  140.                     $postmeta_html = $postmeta_type['display_func']($postmeta);
  141.                 }
  142.                 else {
  143.                     $postmeta_rendered = (is_array($postmeta) || is_object($postmeta) ? print_r($postmeta, true) : $postmeta);
  144.                     $postmeta_html = apply_filters('_wp_post_revision_field_postmeta_display', htmlspecialchars($postmeta_rendered), $postmeta_key, $postmeta);
  145.                 }
  146.             }
  147.             else {
  148.                 $postmeta_html = '*empty postmeta value*';
  149.             }
  150.        
  151.             $html .= '
  152.                 <li>
  153.                     <h3><a href="#postmeta-'.$postmeta_key.'" onclick="jQuery(\'#postmeta-'.$postmeta_key.'\').slideToggle(); return false;">'.$postmeta_key.'</a></h3>
  154.                     <div id="postmeta-'.$postmeta_key.'" style="display: none;">'.$postmeta_html.'</div>
  155.                 </li>
  156.                 ';
  157.         }
  158.         $html .= '</ul>';
  159.    
  160.         return $html;
  161.     }
  162.  
  163.     /**
  164.      * Singleton
  165.      *
  166.      * @return object
  167.      */
  168.     public function get_instance() {
  169.         if (!(self::$_instance instanceof cf_revisions)) {
  170.             self::$_instance = new cf_revisions;
  171.         }
  172.         return self::$_instance;
  173.     }
  174.  
  175.     protected function log($message) {
  176.         if (CF_REVISIONS_DEBUG) {
  177.             error_log($message);
  178.         }
  179.     }
  180.    
  181.     static function meta_keys() {
  182.         global $wpdb;
  183.         return $wpdb->get_col("
  184.             SELECT DISTINCT `meta_key`
  185.             FROM $wpdb->postmeta
  186.             ORDER BY `meta_key`
  187.         ");
  188.     }
  189. }
  190.  
  191. // admin form
  192.  
  193. load_plugin_textdomain('revision_manager');
  194.  
  195. class cf_revisions_admin {
  196.     static function admin_menu() {
  197.         add_options_page(
  198.             __('Revision Manager', 'revision_manager'),
  199.             __('Revision Manager', 'revision_manager'),
  200.             'manage_options',
  201.             basename(__FILE__),
  202.             array('cf_revisions_admin', 'admin_form')
  203.         );
  204.     }
  205.  
  206.     static function admin_form() {
  207.         $required_keys = cf_revisions_admin::required_keys();
  208.         $keys = array_diff(cf_revisions::meta_keys(), cf_revisions_admin::excluded_keys(), $required_keys);
  209. ?>
  210. <div class="wrap">
  211.     <h2><?php _e('CF Revision Manager', 'revision-manager'); ?></h2>
  212. <?php
  213.         if (!count($keys)) {
  214.             echo '<p>'.__('No custom fields found.', 'revision-manager').'</p>';
  215.         }
  216.         else {
  217.             echo '<form id="cfr_revision_manager_form" name="cfr_revision_manager_form" action="'.admin_url('options-general.php').'" method="post">
  218.                 <p>'.__('A plugin or theme has specified that the following custom fields need to included in revisions.', 'revision-manager').'</p>';
  219.             if (count($required_keys)) {
  220.                 echo '<div>
  221.                 <ul id="cfr_revision_manager_keys_required">';
  222.                 foreach ($required_keys as $key) {
  223.                     $checked = $key;
  224.                     $disabled = $key;
  225.                     $id = 'cf_revision_manager_key_'.esc_attr($key);
  226.                     echo '<li>
  227.                         <input type="checkbox" name="revision_manager_keys[]" id="'.$id.'" value="'.esc_attr($key).'" '.checked($key, $checked, false).' '.disabled($key, $disabled, false).' />
  228.                         <label for="'.$id.'">'.esc_html($key).'</label>
  229.                     </li>';
  230.                 }
  231.                 echo '</ul>
  232.                 </div>';
  233.             }
  234.             echo '<p class="clearfix">'.__('Below is a list of selectable custom fields for this site. Choose the ones you would like to have included in your revisions.', 'revision-manager').'</p>
  235.                 <div>
  236.                 <ul id="cfr_revision_manager_keys">';
  237.             foreach ($keys as $key) {
  238.                 $checked = (in_array($key, cf_revisions_admin::selected_keys()) ? $key : '');
  239.                 $disabled = '';
  240.                 $id = 'cf_revision_manager_key_'.esc_attr($key);
  241.                 echo '<li>
  242.                     <input type="checkbox" name="revision_manager_keys[]" id="'.$id.'" value="'.esc_attr($key).'" '.checked($key, $checked, false).' '.disabled($key, $disabled, false).' />
  243.                     <label for="'.$id.'">'.esc_html($key).'</label>
  244.                 </li>';
  245.             }
  246.             echo '</ul>
  247.                 </div>
  248.                 <p class="submit">
  249.                 <input type="submit" name="submit_button" class="button-primary" value="'.__('Save').'" />
  250.                 </p>
  251.                 <input type="hidden" name="cf_action" value="cfr_save_keys" class="hidden" style="display: none;" />
  252.                 '.wp_nonce_field('cfr_save_keys', '_wpnonce', true, false).wp_referer_field(false).'
  253.             </form>';
  254.         }
  255. ?>
  256. </div>
  257. <?php
  258.     }
  259.    
  260.     static function required_keys() {
  261.         global $CFR_KEYS_REQUIRED; // note, this is set in the register_meta() method
  262.         return $CFR_KEYS_REQUIRED;
  263.     }
  264.  
  265.     static function excluded_keys() {
  266.         return apply_filters(
  267.             'cf_revision_manager_excluded_keys',
  268.             array(
  269.                 '_edit_last',
  270.                 '_edit_lock',
  271.             )
  272.         );
  273.     }
  274.  
  275.     static function selected_keys() {
  276.         $selected = get_option('cf_revision_manager_meta_keys');
  277.         if (empty($selected)) {
  278.             $selected = array();
  279.         }
  280.         return $selected;
  281.     }
  282.  
  283.     static function register_meta() {
  284.         if (function_exists('cfr_register_metadata')) {
  285. cfr_register_metadata('foo');
  286.             global $CFR_KEYS_REQUIRED;
  287.             $cfr = cf_revisions::get_instance();
  288.             $CFR_KEYS_REQUIRED = $cfr->registered_keys();
  289.             $keys = cf_revisions_admin::selected_keys();
  290.             if (count($keys)) {
  291.                 foreach ($keys as $key) {
  292.                     cfr_register_metadata($key);
  293.                 }
  294.             }
  295.         }
  296.     }
  297.    
  298.     static function request_handler() {
  299.         if (isset($_POST['cf_action'])) {
  300.             switch ($_POST['cf_action']) {
  301.                 case 'cfr_save_keys':
  302.                     if (!current_user_can('manage_options') || !wp_verify_nonce($_POST['_wpnonce'], 'cfr_save_keys')) {
  303.                         wp_die('Oops, please try again.');
  304.                     }
  305.                     $keys = (isset($_POST['revision_manager_keys']) && is_array($_POST['revision_manager_keys'])) ? $_POST['revision_manager_keys'] : array();
  306.                     cf_revisions_admin::save_settings($keys);
  307.                     wp_redirect(admin_url('options-general.php?page='.basename(__FILE__)).'&cf_admin_notice=cfr-1');
  308.                     break;
  309.             }
  310.         }
  311.     }
  312.    
  313.     static function save_settings($keys) {
  314.         update_option('cf_revision_manager_meta_keys', (array) $keys);
  315.     }
  316.    
  317.     static function admin_notices() {
  318.         $notice = '';
  319.         $class = 'updated';
  320.         if (isset($_GET['cf_admin_notice'])) {
  321.             switch ($_GET['cf_admin_notice']) {
  322.                 case 'cfr-1':
  323.                     $notice = 'Selected meta keys to be versioned have been updated.';
  324.                     break;
  325.             }
  326.         }
  327.         if (!empty($notice)) {
  328.             echo '<div id="message" class="'.$class.'"><p>'.$notice.'</p></div>
  329. ';
  330.         }
  331.     }
  332.    
  333.     static function admin_head() {
  334.         if (!empty($_GET['page']) && $_GET['page'] == basename(__FILE__)) {
  335. ?>
  336. <style type="text/css">
  337. #cfr_revision_manager_form .cf-col {
  338.     float: left;
  339.     overflow: hidden;
  340.     width: 33%;
  341. }
  342. #cfr_revision_manager_form .submit {
  343.     clear: both;
  344. }
  345. #cfr_revision_manager_form .clearfix {
  346.     clear: both;
  347. }
  348. </style>
  349. <script type="text/javascript">
  350. /**
  351.  * ColumnizeLists
  352.  * @version 0.1
  353.  * @requires jQuery
  354.  * Copyright 2010, Crowd Favorite
  355.  *
  356.  * Break lists into chunks so you can build top-to-bottom-left-to-right columned lists
  357.  * Usage: $('ul.my-columnizer-class').columnizeLists({});
  358.  */
  359. ;(function($) {
  360.  
  361.     $.fn.columnizeLists = function(args) {
  362.         // Merge our default args and the user's args
  363.         var args = $.extend({}, $.fn.columnizeLists.args, args);
  364.  
  365.         // Loop through every target
  366.         this.each(function(){
  367.             var $this = $(this);
  368.            
  369.             var cont = $this.parent();
  370.             var items = $this.find('li');
  371.             var items_count = items.size();
  372.            
  373.             if (args.preserveOriginalClass) {
  374.                 var originalClass = ' class="' + $this.attr('class') + '"';
  375.             } else {
  376.                 var originalClass = '';
  377.             };
  378.  
  379.             // If we have the column preference, figure out how many rows we should have, then do rows
  380.             if (args.pref == 'cols') {
  381.                 var rem = items_count % args.cols;
  382.                 args.rows = Math.floor(items_count / args.cols);
  383.                 rem ? extra = 1 : extra = 0;
  384.             }
  385.  
  386.             // Put a wrapper around our new divs we're creating
  387.             cont.append('<div class="' + args.divWrapperClass + '"></div>');
  388.             // Find classes as compound class selectors
  389.             var div_wrapper = cont.find('.' + args.divWrapperClass.replace(' ', '.'));
  390.  
  391.             // Loop through each list item
  392.             var i = 0;
  393.             var col_num = 0;
  394.             items.each(function() {
  395.                 // fancy-pants math to see if we should append an extra row till we have no remainder
  396.                 (extra && col_num <= rem) ? row_count = args.rows + extra : row_count = args.rows;
  397.                 if (i % row_count == 0) {
  398.                     col_num++;
  399.                     i = 0;
  400.                     var colClasses = args.colClass.replace(' ', '-');
  401.                     cur_col = colClasses + '-' + col_num.toString();
  402.                     div_wrapper.append('<div class="' + args.colClass + ' ' + cur_col + '"><ul' + originalClass + '></ul></div>');
  403.                 }
  404.  
  405.                 $(this).appendTo(div_wrapper.find('.' + cur_col + ' ul'));
  406.                 i++;
  407.             });
  408.  
  409.             // Now add the container class, and remove the initial ul
  410.             if (args.containerClass) {
  411.                 cont.addClass(args.containerClass)
  412.             };
  413.             cont.children("ul").remove();
  414.         });
  415.        
  416.     };
  417.    
  418.     /**
  419.      * Default settings
  420.      */
  421.     $.fn.columnizeLists.args = {
  422.         pref: 'cols',
  423.         rows: 10,
  424.         cols: 4,
  425.         containerClass: 'clearfix',
  426.         colClass: 'cf-col',
  427.         divWrapperClass: 'div-wrapper',
  428.         preserveOriginalClass: false
  429.     }
  430. })(jQuery);
  431. jQuery(function($) {
  432.     $('#cfr_revision_manager_keys, #cfr_revision_manager_keys_required').columnizeLists({
  433.         'cols': 3
  434.     });
  435. });
  436. </script>
  437. <?php
  438.         }
  439.     }
  440.    
  441. }
  442. add_action('admin_menu', array('cf_revisions_admin', 'admin_menu'));
  443. add_action('init', array('cf_revisions_admin', 'register_meta'), 999);
  444. add_action('admin_init', array('cf_revisions_admin', 'request_handler'));
  445. add_action('admin_notices', array('cf_revisions_admin', 'admin_notices'));
  446. add_action('admin_head', array('cf_revisions_admin', 'admin_head'));
  447.  
  448. } // end double-load check
  449.  
  450. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement