Advertisement
Guest User

WP Form Class

a guest
Sep 12th, 2011
261
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 19.30 KB | None | 0 0
  1. <?php
  2. /*
  3. Settings Helper for WordPress
  4. Copyright (C) 2011 William Barnes
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  19. */
  20.  
  21. /** WP Settings Form Helper
  22.   *
  23.   * Helper class for creating settings forms.
  24.   */
  25. if (!class_exists('WP_Settings_Form')):
  26.  
  27. class WP_Settings_Form
  28. {
  29.     /**
  30.      * Configuration
  31.      *
  32.      * Public so that you can make adjustments.
  33.      */
  34.     public $config = array();  
  35.     protected $_errors = array();
  36.    
  37.     // CONFIG
  38.    
  39.     public function __construct($config)
  40.     {
  41.         $this->config = $config;
  42.     }
  43.    
  44.     /**
  45.      * Gets the option
  46.      *
  47.      * In simple mode: returns the row from wp_options that corresponds to the
  48.      * option_name for the form.
  49.      *
  50.      * In multi mode: returns either the row from options array or if no key
  51.      * then the defaults.
  52.      *
  53.      * The force argument causes it to return the entire array when in multi
  54.      * mode.
  55.      */
  56.     public function get_option($force=false)
  57.     {
  58.         $config = $this->config;
  59.         $option = get_option($config['option_name']);
  60.         if (!is_array($option)):
  61.             $option = array();
  62.         endif;
  63.        
  64.         if ($force || !$this->is_multi()):
  65.             return $option;
  66.        
  67.         // MULTI: Insert
  68.         elseif ($this->get_request_type() == 'insert'):
  69.             return isset($config['defaults']) ? $config['defaults'] : array();
  70.  
  71.         // MULTI: Edit
  72.         else:
  73.             return $option[$_REQUEST['_wpform_key']];
  74.         endif;
  75.     }
  76.    
  77.     // QUERY
  78.     // Many of these are called only once, but this makes it easier to read the
  79.     // code later.
  80.    
  81.     public function is_multi()
  82.     {
  83.         return isset($this->config['multi']);
  84.     }
  85.    
  86.     public function is_form_submission()
  87.     {
  88.         return (isset($_POST['_wpform_option']) && ($_POST['_wpform_option'] == $this->config['option_name']));
  89.     }
  90.    
  91.     public function get_request_type()
  92.     {
  93.         if (!$this->is_multi()):
  94.             return 'edit'; // Always edit
  95.         elseif (!isset($_REQUEST['_wpform_key']) || empty($_REQUEST['_wpform_key'])):
  96.             return 'insert';
  97.         elseif (is_numeric($_GET['_wpform_key'])):
  98.             return 'edit';
  99.         elseif (is_array($_POST['_wpform_key']) && isset($_REQUEST['_wpform_action']) && ($_POST['_wpform_action'] == 'delete')):
  100.             return 'delete';
  101.         elseif (is_array($_POST['_wpform_key']) && isset($_REQUEST['_wpform_action'])):
  102.             // Custom action
  103.             return  $_REQUEST['_wpform_action'];
  104.         else:
  105.             return null;
  106.         endif;
  107.     }
  108.    
  109.     public function is_single_form()
  110.     {
  111.         // Simple mode always shows the form.
  112.         // Multi mode only shows it if we're editing or inserting a new item.
  113.         // In multi, make sure that it's *this* form.
  114.         return (
  115.                     ( !$this->is_multi() ) ||
  116.                     ( isset($_GET['_wpform_option']) && ($_GET['_wpform_option'] == $this->config['option_name']) )
  117.                 );
  118.     }
  119.    
  120.     /**
  121.      * Validate
  122.      *
  123.      * Provide it with a new option. It checks for variables based on the form
  124.      * definition, validates them, and returns the validated option.
  125.      *
  126.      */
  127.     public function validate($new_option_group)
  128.     {
  129.         $config = $this->config;
  130.         $option = $this->get_option();
  131.  
  132.         // Loop through the form definition and process only defined variables
  133.         $sections = $config['sections'];
  134.         foreach ($sections as $section):
  135.             $fields = $section['fields'];
  136.             foreach ($fields as $field):
  137.            
  138.                 // Check for field in submission
  139.                 if (array_key_exists($field['name'],$new_option_group)):
  140.                     $name       = $field['name'];
  141.                     $old_value  = isset($option[$name]) ? $option[$name] : null;
  142.                     $test_value = $new_option_group[$name];
  143.                    
  144.                     // User provided default validation
  145.                     if (!isset($field['validator'])):
  146.                         $option[$name] = call_user_func($config['validator'],$test_value,$old_value,$field);
  147.  
  148.                     // Use local validation
  149.                     elseif (is_array($field['validator']) && array_key_exists('type',$field['validator'])):
  150.                         $option[$name] = $this->validate_local($field['validator'],$test_value,$old_value,$field);
  151.                    
  152.                     // No validation
  153.                     elseif ($field['validator'] === false):
  154.                         $option[$name] = $test_value;
  155.                        
  156.                     // User provided field-specific validation
  157.                     else:
  158.                         $option[$name] = call_user_func($field['validator'],$test_value,$old_value,$field);
  159.                     endif;         
  160.                 endif;
  161.                
  162.             endforeach; // Fields
  163.         endforeach; // Sections
  164.        
  165.         return $option;
  166.     }
  167.    
  168.     /**
  169.      * Validation shortcuts
  170.      *
  171.      * So users don't have to write their own.
  172.      */
  173.     public function validate_local($validator,$test_value,$old_value,$field)
  174.     {
  175.         if ((!isset($field['required']) || $field['required'] == false) && $test_value == ''): // Allow empty values
  176.             return '';
  177.         endif;
  178.        
  179.         switch($validator['type']):
  180.             case "preg_replace":
  181.                 return preg_replace($validator['pattern'],$validator['replacement'],$test_value);
  182.                
  183.             case "preg_match":
  184.                 if (preg_match($validator['pattern'],$test_value)):
  185.                     return $test_value;
  186.                 else:
  187.                     $this->error($$field['label'] . ': incorrect format');
  188.                     return $old_value;
  189.                 endif;
  190.            
  191.             case "url": // http://daringfireball.net/2010/07/improved_regex_for_matching_urls
  192.                 $re = "%^(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'\".,<>?«»“”‘’]))$%";
  193.                 if (preg_match($re,$test_value)):
  194.                     return $test_value;
  195.                 else:
  196.                     $this->error($field['label'] . ': not a valid url');
  197.                     return $old_value;
  198.                 endif;
  199.                
  200.             case "email": // http://www.regular-expressions.info/email.html
  201.                 $re = "/^(?i)[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/";
  202.                 if (preg_match($re,$test_value)):
  203.                     return $test_value;
  204.                 else:
  205.                     $this->error($field['label'] . ': not a valid email');
  206.                     return $old_value;
  207.                 endif;
  208.                
  209.             case "numeric":
  210.                 if (is_numeric($test_value)):
  211.                     return $test_value;
  212.                 else:
  213.                     $this->error($field['label'] . ': must be a number');
  214.                     return $old_value;
  215.                 endif;
  216.            
  217.             default:
  218.                 // If you get this error, there's something wrong with your $config
  219.                 $this->error($field['label'] . ': validation type does not exist.');
  220.                 return $old_value;
  221.         endswitch;
  222.     }
  223.    
  224.     /**
  225.      * Register the form
  226.      */
  227.     public function register()
  228.     {
  229.         $config      = $this->config;
  230.         $option_name = $config['option_name'];
  231.        
  232.         // Is this a form submission?
  233.         if ($this->is_form_submission()):
  234.             $option = $this->get_option(true);
  235.  
  236.             // Adding or editing
  237.             if ($this->is_single_form() && check_admin_referer('_wpform_single','_wpform_nonce')):
  238.                 $key        = isset($_POST['_wpform_key']) ? $_POST['_wpform_key'] : '';
  239.                 $new_option = $this->validate( $_POST[$option_name] );
  240.  
  241.                 // Do nothing if there were validation errors
  242.                 if (empty($this->_errors)):
  243.                    
  244.                     // MULTI MODE: Adding
  245.                     if ($this->get_request_type() == 'insert'):
  246.                         // Defaults
  247.                         if (array_key_exists('defaults',$config)):
  248.                             $new_option += $config['defaults'];
  249.                         endif;
  250.                        
  251.                         // Add new item to end of array
  252.                         $key          = time(); // Use a timestamp so keys are always unique
  253.                         $option[$key] = $new_option;
  254.                        
  255.                         // Forward to the edit page for the item just added
  256.                         $redirect = add_query_arg(array(
  257.                             '_wpform_key'=>$key,
  258.                             '_wpform_success' => $config['option_name'],
  259.                             '_wpform_error'=>false
  260.                         ));
  261.        
  262.                     // MULTI MODE: Editing
  263.                     elseif ($this->is_multi() && ($this->get_request_type() == 'edit')):
  264.                         if (array_key_exists($key,$option)):
  265.                             $option[$key] = $new_option;
  266.                            
  267.                             // Return to the edit page
  268.                             $redirect = add_query_arg(array(
  269.                                 '_wpform_success' => $config['option_name'],
  270.                                 '_wpform_error'=>false
  271.                             ));
  272.            
  273.                         // Somebody requested a key that doesn't exist, do nothing
  274.                         else:
  275.                             $this->error('Key does not exist');
  276.                         endif;
  277.  
  278.                     // SIMPLE MODE: Editing
  279.                     else:
  280.                         $option = $new_option;
  281.                        
  282.                         // Back to edit page (index)
  283.                         $redirect = add_query_arg(array(
  284.                             '_wpform_success' => $config['option_name'],
  285.                             '_wpform_error'=>false
  286.                         ));
  287.                     endif;
  288.                 endif;
  289.                
  290.             // MULTI MODE ONLY: Deleting or other
  291.             elseif ($this->get_request_type() && check_admin_referer('_wpform_multi','_wpform_nonce')):
  292.                 $keys   = $_POST['_wpform_key'];
  293.                 $action = $this->get_request_type();
  294.                
  295.                 // Delete
  296.                 if ($action == 'delete'):
  297.                     foreach($keys as $key):
  298.                         if (array_key_exists($key,$option)) unset($option[$key]);
  299.                     endforeach;
  300.                    
  301.                     // Back to index
  302.                     $redirect = add_query_arg(array(
  303.                         '_wpform_success' => $config['option_name'],
  304.                         '_wpform_error'=>false
  305.                     ));
  306.                
  307.                 // Custom action
  308.                 // Loop through the provided keys and call the user-provided function on each row
  309.                 else:
  310.                     $buttons = isset($config['multi']['table_buttons']) ? $config['multi']['table_buttons'] : array();
  311.                     if (array_key_exists($action, $buttons)):
  312.                         foreach($keys as $key):
  313.                             if (array_key_exists($key, $option) && isset($buttons[$action]['callback'])):
  314.                                 $option[$key] = call_user_func($buttons[$action]['callback'], $option[$key], &$this);
  315.                             endif;
  316.                         endforeach;
  317.                        
  318.                         // Back to index
  319.                         $redirect = add_query_arg(array(
  320.                             '_wpform_success' => $config['option_name'],
  321.                             '_wpform_error' => false
  322.                         ));
  323.                        
  324.                     else:
  325.                         $this->error('Action does not exist');
  326.                     endif;
  327.                 endif;
  328.                
  329.             // Something weird happened
  330.             else:
  331.                 $this->error('Malformed request');
  332.             endif;
  333.            
  334.             // Save changes to database
  335.             if (empty($this->_errors)):
  336.                 update_option($config['option_name'],$option);
  337.            
  338.             // Print errors
  339.             else:
  340.                 $message = '';
  341.                 foreach($this->_errors as $error):
  342.                     $message .= '<li>' . $error . '</li>';
  343.                 endforeach;
  344.                 $redirect = add_query_arg(array(
  345.                     '_wpform_error'=>urlencode($message),
  346.                     '_wpform_success'=>false,
  347.                 ));
  348.             endif;
  349.            
  350.             wp_redirect($redirect);
  351.             exit;
  352.         endif;
  353.     }
  354.    
  355.     public function error($message)
  356.     {
  357.         $this->_errors[] = $message;
  358.     }
  359.    
  360.     /**
  361.      * Echos a form field
  362.      *
  363.      * TODO: Define more field types
  364.      * You can add field types yourself, it's not too hard
  365.      */
  366.     public function echo_field($field)
  367.     {
  368.         $config = $this->config;
  369.         $option = $this->get_option();
  370.        
  371.         // Attribute values
  372.         $option_name = $config['option_name'];
  373.         $field_name  = $field['name'];
  374.         $field_id    = $config['option_name'].'_'.$field['name'];
  375.         $field_value = isset($option[$field['name']]) ? $option[$field['name']] : '';
  376.         $field_size  = isset($field['size']) ? $field['size'] : '40';
  377.  
  378.         switch ($field['type']):
  379.             // Category Dropdown
  380.             case "authors":
  381.                 wp_dropdown_users("id={$field_id}&name={$option_name}[{$field_name}]&selected={$field_value}");
  382.                 break;
  383.                
  384.             // Boolean (checkbox, value=1)
  385.             case "boolean":
  386.                 echo "<input id='{$field_id}' name='{$option_name}[{$field_name}]' size='{$field_size}' type='checkbox' value='1'". (($field_value == 1) ? " checked='yes'" : '') ."' />";
  387.                 break;
  388.  
  389.             // Category Dropdown
  390.             case "categories":
  391.                 wp_dropdown_categories("id={$field_id}&name={$option_name}[{$field_name}]&selected={$field_value}");
  392.                 break;
  393.            
  394.             // Dropdown
  395.             case "dropdown":
  396.                 echo "<select id='{$field_id}' name='{$option_name}[{$field_name}]' />";
  397.                 foreach ($field['options'] as $key=>$value):
  398.                     echo "<option value='{$key}'".($value == $field_value ? "selected='selected'" : "").">{$value}</option>";
  399.                 endforeach;
  400.                 echo "</select>";
  401.                 break;
  402.            
  403.             // Password
  404.             case "password":
  405.                 echo "<input id='{$field_id}' name='{$option_name}[{$field_name}]' size='{$field_size}' type='password' value='{$field_value}' />";
  406.                 break;
  407.                
  408.             // Text field
  409.             case "text":
  410.             default:
  411.                 echo "<input id='{$field_id}' name='{$option_name}[{$field_name}]' size='{$field_size}' type='text' value='{$field_value}' />";
  412.                 break;
  413.         endswitch;
  414.        
  415.         // Description (if present)
  416.         if (isset($field['description'])) echo '<br />'.$field['description'];
  417.     }
  418.    
  419.     public function echo_settings_page()
  420.     {
  421.         $config = $this->config;
  422.  
  423.         if ($this->is_single_form()):
  424.             $this->echo_single_form();
  425.         else:
  426.             $this->echo_multi_index();
  427.         endif;
  428.     }
  429.    
  430.     /**
  431.      * Echo form
  432.      *
  433.      * Call this within the HTML of your settings page function.
  434.      */
  435.     public function echo_single_form()
  436.     {
  437.         $config = $this->config;
  438. ?>
  439.         <form action="<?php echo add_query_arg(array('_wpform_success'=>false,'_wpform_error'=>false)); ?>" method="post">
  440.             <div class="inner">
  441.                 <?php if (isset($_REQUEST['_wpform_error'])): ?>
  442.                     <div class="error">
  443.                         <ul>
  444.                             <?php echo $_REQUEST['_wpform_error']; ?>
  445.                         </ul>
  446.                     </div>
  447.                 <?php elseif (isset($_REQUEST['_wpform_success']) && ($_REQUEST['_wpform_success'] == $config['option_name'])): ?>
  448.                     <div class="updated">
  449.                         <p>Update successful.</p>
  450.                     </div>
  451. <?php
  452.                 endif;
  453.                 $sections = $config['sections'];
  454.                 foreach ($sections as $section):
  455. ?>
  456.                     <h3><?php echo $section['title']; ?></h3>
  457.                     <table class="form-table">
  458.                         <tbody>
  459. <?php
  460.                             $fields = $section['fields'];
  461.                             foreach ($fields as $field):
  462.                                 $field_id = $config['option_name'].'_'.$field['name'];
  463. ?>
  464.                                 <tr valign="top">
  465.                                     <th scope="row"><label for="<?php echo $field_id; ?>"><?php echo $field['label']; ?></label></th>
  466.                                     <td>
  467.                                         <?php $this->echo_field($field); ?>
  468.                                     </td>
  469.                                 </tr>
  470.                             <?php endforeach; ?>
  471.                         </tbody>
  472.                     </table>
  473.                 <?php endforeach; ?>
  474. <?php
  475.                 if (array_key_exists('hidden',$config)):
  476.                     foreach ($config['hidden'] as $input):
  477.                         echo "<input type='hidden' name='{$input['name']}' value='{$input['value']}' />";
  478.                     endforeach;
  479.                 endif;
  480.                
  481.                 if (isset($config['multi'])):
  482. ?>
  483.                     <input type="hidden" name="_wpform_key" value="<?php echo isset($_REQUEST['_wpform_key']) ? $_REQUEST['_wpform_key'] : ''; ?>" />
  484. <?php
  485.                 endif;
  486. ?>
  487.                 <input type="hidden" name="_wpform_option" value="<?php echo $config['option_name']; ?>" />
  488.                 <?php wp_nonce_field( '_wpform_single', '_wpform_nonce', true); ?>
  489.                 <p class="submit">
  490.                     <input name="Submit" class="button-primary" type="submit" value="Save Changes" />
  491.                 </p>
  492.             </div>
  493.         </form>
  494. <?php
  495.     }
  496.    
  497.     /**
  498.      * Echo table
  499.      *
  500.      * Prints a table of the items in a multi-dimensional option.
  501.      */
  502.     public function echo_multi_index()
  503.     {
  504.         $config = $this->config;
  505.         $fields = $config['multi']['table_fields'];
  506. ?>
  507.         <form action="<?php echo add_query_arg(array('_wpform_success'=>false,'_wpform_error'=>false)); ?>" method="post">
  508.             <?php if (isset($_REQUEST['_wpform_error'])): ?>
  509.                 <div class="error">
  510.                     <ul>
  511.                         <?php echo $_REQUEST['_wpform_error']; ?>
  512.                     </ul>
  513.                 </div>
  514.             <?php elseif (isset($_REQUEST['_wpform_success']) && ($_REQUEST['_wpform_success'] == $config['option_name'])): ?>
  515.                 <div class="update">
  516.                     <p>Update successful.</p>
  517.                 </div>
  518.             <?php endif; ?>
  519.             <?php if (isset($config['multi']['list_title'])): ?>
  520.                 <h3><?php echo $config['multi']['list_title']; ?></h3>
  521.             <?php endif; ?>
  522.             <table class="widefat">
  523.                 <colgroup>
  524.                     <col style="width:20px;"/>
  525.                     <col style="width:40px;"/>
  526. <?php
  527.                    
  528.                     foreach($fields as $field):
  529.                         echo '<col' . (isset($field['width']) ? ' style="width:' . $field['width'] . '"' : '') . " />";
  530.                     endforeach;
  531. ?>
  532.                 </colgroup>
  533.                 <thead><tr>
  534.                     <th scope="col" class="check-column"><input type="checkbox" /></th>
  535.                     <th scope="col">ID</th>
  536. <?php
  537.                     foreach($fields as $field):
  538.                         echo '<th scope="col">' . $field['label'] . '</th>';
  539.                     endforeach;
  540. ?>
  541.                 </tr></thead>
  542.                 <tbody>
  543. <?php
  544.                     $rows = $this->get_option(true);
  545.                     foreach ($rows as $id=>$row):
  546. ?>
  547.                         <tr>
  548.                             <th class="check-column" scope="row"><input type="checkbox" value="<?php echo $id; ?>" name="_wpform_key[]" /></th>
  549.                             <td><?php echo $id; ?></td>
  550. <?php
  551.                             foreach($fields as $field):
  552.                                 $type = isset($field['type']) ? $field['type'] : 'text';
  553.                                 $content = isset($row[$field['name']]) ? $row[$field['name']] : null;
  554.                                 switch ($type):
  555.                                     case "date":
  556.                                         $content = date('D, M j, Y',strtotime($content));
  557.                                         break;
  558.                                     case "datetime":
  559.                                         $content = date('D, M j, Y H:i:s e',strtotime($content));
  560.                                         break;
  561.                                     case "timestamp":
  562.                                         $content = date('D, M j, Y H:i:s e',$content);
  563.                                         break;
  564.                                     default:
  565.                                         $content = (!empty($content) || ($content === 0) ? $content : '&nbsp;');
  566.                                         break;
  567.                                 endswitch;
  568.                                
  569.                                 if (array_key_exists('link',$field) && $field['link']):
  570.                                     echo '<td><a href="'.add_query_arg(array('_wpform_option'=>$config['option_name'],'_wpform_key'=>$id,'_wpform_success'=>false,'_wpform_error'=>false)).'">'.$content.'</a></td>';
  571.                                 else:
  572.                                     echo "<td>$content</td>";
  573.                                 endif;
  574.                             endforeach;
  575. ?>
  576.                         </tr>
  577.                     <?php endforeach; ?>
  578.                 </tbody>
  579.             </table>
  580.             <input type="hidden" name="_wpform_option" value="<?php echo $config['option_name']; ?>" />
  581.             <?php wp_nonce_field( '_wpform_multi', '_wpform_nonce', true); ?>
  582.             <div class="tablenav">
  583.                 <div class="alignleft">
  584. <?php
  585.                     $buttons = isset($config['multi']['table_buttons']) ? $config['multi']['table_buttons'] : array();
  586.                     foreach ($buttons as $id=>$button):
  587.                         echo '<button type="submit" name="'. (isset($button['name']) ? $button['name'] : '_wpform_action') .'" value="'. (isset($button['value']) ? $button['value'] : $id) .'" class="button-'. (isset($button['priority']) ? $button['priority'] : 'secondary') .'">'. $button['label'] .'</button> ';
  588.                     endforeach;
  589. ?>
  590.                     <button type="submit" name="_wpform_action" value="delete" class="button-secondary delete"><?php _e('Delete'); ?></button>
  591.                 </div>
  592.                 <br class="clear" />
  593.             </div>
  594.         </form>
  595.         <form action="<?php echo add_query_arg(array('_wpform_success'=>false,'_wpform_error'=>false)); ?>" method="GET">
  596.             <div class="inner">
  597. <?php
  598.                 if (isset($config['multi']['new_fields'])):
  599. ?>
  600.                     <h3><?php echo $config['multi']['new_title'] ?></h3>
  601.                     <table class="form-table">
  602.                         <tbody>
  603. <?php
  604.                             $fields = $config['multi']['new_fields'];
  605.                             foreach ($fields as $field):
  606.                                 $field_id = $config['option_name'].'_'.$field['name'];
  607. ?>
  608.                                 <tr valign="top">
  609.                                     <th scope="row"><label for="<?php echo $field_id; ?>"><?php echo $field['label']; ?></label></th>
  610.                                     <td><?php $this->echo_field($field); ?></td>
  611.                                 </tr>
  612.                             <?php endforeach; ?>
  613.                         </tbody>
  614.                     </table>
  615. <?php
  616.                 endif;
  617. ?>
  618.                 <input type="hidden" name="_wpform_option" value="<?php echo $config['option_name']; ?>" />
  619.                 <input type="hidden" name="page" value="<?php echo $_GET['page']; ?>" />
  620.                 <p class="submit"><input class="button-primary" type="submit" value="<?php echo $config['multi']['new_title'] ?>" /></p>
  621.             </div>
  622.         </form>
  623. <?php
  624.     }
  625. }
  626.  
  627. endif; // class_exists
  628. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement