Advertisement
Guest User

frontend-uploader.php

a guest
Aug 12th, 2013
463
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 34.31 KB | None | 0 0
  1. <?php
  2. /*
  3. Plugin Name: Frontend Uploader
  4. Description: Allow your visitors to upload content and moderate it.
  5. Author: Rinat Khaziev, Daniel Bachhuber
  6. Version: 0.5.8.1
  7. Author URI: http://digitallyconscious.com
  8.  
  9. GNU General Public License, Free Software Foundation <http://creativecommons.org/licenses/GPL/2.0/>
  10.  
  11. This program is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 2 of the License, or
  14. (at your option) any later version.
  15.  
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with this program; if not, write to the Free Software
  23. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  24.  
  25. */
  26.  
  27. // Define consts and bootstrap and dependencies
  28. define( 'FU_VERSION', '0.6-working' );
  29. define( 'FU_ROOT' , dirname( __FILE__ ) );
  30. define( 'FU_FILE_PATH' , FU_ROOT . '/' . basename( __FILE__ ) );
  31. define( 'FU_URL' , plugins_url( '/', __FILE__ ) );
  32.  
  33. require_once FU_ROOT . '/lib/php/class-frontend-uploader-wp-media-list-table.php';
  34. require_once FU_ROOT . '/lib/php/class-frontend-uploader-wp-posts-list-table.php';
  35. require_once FU_ROOT . '/lib/php/class-html-helper.php';
  36. require_once FU_ROOT . '/lib/php/settings-api/class.settings-api.php';
  37. require_once FU_ROOT . '/lib/php/functions.php';
  38. require_once FU_ROOT . '/lib/php/frontend-uploader-settings.php';
  39.  
  40. class Frontend_Uploader {
  41.  
  42.     public $allowed_mime_types;
  43.     public $html;
  44.     public $settings;
  45.     public $settings_slug = 'frontend_uploader_settings';
  46.     public $is_debug = false;
  47.     public $form_fields = array();
  48.     public $request_form_fields = array();
  49.     protected $manage_permissions = array();
  50.  
  51.     /**
  52.      * Here we go
  53.      *
  54.      * Instantiating the plugin, adding actions, filters, and shortcodes
  55.      */
  56.     function __construct() {
  57.         // Hooking to wp_ajax
  58.  
  59.        
  60.  
  61.         // Init
  62.         add_action( 'init', $this->_a(  'action_init' ) );
  63.  
  64.         // HTML helper to render HTML elements
  65.         $this->html = new Html_Helper;
  66.  
  67.         // Either use default settings if no setting set, or try to merge defaults with existing settings
  68.         // Needed if new options were added in upgraded version of the plugin
  69.         $this->settings = array_merge( $this->settings_defaults(), (array) get_option( $this->settings_slug, $this->settings_defaults() ) );
  70.         register_activation_hook( __FILE__, $this->_a(  'activate_plugin' ) );
  71.  
  72.         /**
  73.          * Should consist of fields to be proccessed automatically on content submission
  74.          *
  75.          * @todo this is just a pass one
  76.          *
  77.          * Example field:
  78.          *  array(
  79.          *  'name' => '{form name}',
  80.          *  'element' => HTML element,
  81.          *  'context' => {title|description|file|meta} )
  82.          * @var array
  83.          */
  84.         $this->form_fields = array();
  85.     }
  86.    
  87.    
  88.     // Custom action to do any additional logic after attachment is uploaded
  89.     function my_fu_after_upload( $media_ids, $success ) {
  90.         // Iterate array with attachment ids
  91.         foreach( (array) $media_ids as $media_id ) {
  92.    
  93.             // Term is just an example, all $_POST variables should be available to you
  94.             $term = sanitize_text_field( $_POST['post_tags'] );
  95.    
  96.             wp_set_object_terms( $media_id, $term, 'imagetype', true );
  97.         }
  98.     }
  99.    
  100.     /**
  101.      *  Load languages and a bit of paranoia
  102.      */
  103.     function action_init() {
  104.         load_plugin_textdomain( 'frontend-uploader', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
  105.         $this->allowed_mime_types = $this->_get_mime_types();
  106.         // Configuration filter to change manage permissions
  107.         $this->manage_permissions = apply_filters( 'fu_manage_permissions', 'edit_posts' );
  108.         // Debug mode filter
  109.         $this->is_debug = (bool) apply_filters( 'fu_is_debug', defined( 'WP_DEBUG' ) && WP_DEBUG );
  110.  
  111.         add_filter( 'upload_mimes', $this->_a(  '_get_mime_types' ), 999 );
  112.        
  113.         add_action( 'wp_ajax_approve_ugc', $this->_a(  'approve_photo' ) );
  114.         add_action( 'wp_ajax_approve_ugc_post', $this->_a(  'approve_post' ) );
  115.         add_action( 'wp_ajax_delete_ugc', $this->_a(  'delete_post' ) );
  116.  
  117.         add_action( 'wp_ajax_upload_ugc', $this->_a(  'upload_content' ) );
  118.         add_action( 'wp_ajax_nopriv_upload_ugc', $this->_a(  'upload_content' ) );
  119.  
  120.         // Adding media submenu
  121.         add_action( 'admin_menu', $this->_a(  'add_menu_items' ) );
  122.        
  123.         add_action( 'fu_after_upload', 'my_fu_after_upload', 10, 2 );
  124.  
  125.         // Currently supported shortcodes
  126.         add_shortcode( 'fu-upload-form', $this->_a(  'upload_form' ) );
  127.         add_shortcode( 'input', $this->_a(  'shortcode_content_parser' ) );
  128.         add_shortcode( 'textarea', $this->_a(  'shortcode_content_parser' ) );
  129.         add_shortcode( 'select', $this->_a(  'shortcode_content_parser' ) );
  130.  
  131.         // Static assets
  132.         add_action( 'wp_enqueue_scripts', $this->_a(  'enqueue_scripts' ) );
  133.         add_action( 'admin_enqueue_scripts', $this->_a(  'admin_enqueue_scripts' ) );
  134.  
  135.         // Unautop the shortcode
  136.         add_filter( 'the_content', 'shortcode_unautop', 100 );
  137.         // Hiding not approved attachments from Media Gallery
  138.         // @since core 3.5-beta-1
  139.         add_filter( 'posts_where', $this->_a(  'filter_posts_where' ) );
  140.        
  141.     }
  142.  
  143.     function _get_mime_types() {
  144.         // Use wp_get_mime_types if available, fallback to get_allowed_mime_types()
  145.         $mime_types = function_exists( 'wp_get_mime_types' ) ? wp_get_mime_types() : get_allowed_mime_types() ;
  146.         $fu_mime_types = fu_get_mime_types();
  147.         // Workaround for IE
  148.         $mime_types['jpg|jpe|jpeg|pjpg'] = 'image/pjpeg';
  149.         $mime_types['png|xpng'] = 'image/x-png';
  150.         // Iterate through default extensions
  151.         foreach ( $fu_mime_types as $extension => $details ) {
  152.             // Skip if it's not in the settings
  153.             if ( !in_array( $extension, $this->settings['enabled_files'] ) )
  154.                 continue;
  155.  
  156.             // Iterate through mime-types for this extension
  157.             foreach ( $details['mimes'] as $ext_mime ) {
  158.                 $mime_types[ $extension . '|' . $extension . sanitize_title_with_dashes( $ext_mime ) ] = $ext_mime;
  159.             }
  160.         }
  161.         // Configuration filter: fu_allowed_mime_types should return array of allowed mime types (see readme)
  162.         $mime_types = apply_filters( 'fu_allowed_mime_types', $mime_types );
  163.  
  164.         foreach ( $mime_types as $ext_key => $mime ) {
  165.             // Check for php just in case
  166.             if ( false !== strpos( $mime, 'php' ) )
  167.                 unset( $mime_types[$ext_key] );
  168.         }
  169.         return $mime_types;
  170.     }
  171.  
  172.     /**
  173.      * Ensure we're not producing any notices by supplying the defaults to get_option
  174.      *
  175.      * @return array $defaults
  176.      */
  177.     function settings_defaults() {
  178.         $defaults = array();
  179.         $settings = Frontend_Uploader_Settings::get_settings_fields();
  180.         foreach ( $settings[$this->settings_slug] as $setting ) {
  181.             $defaults[ $setting['name'] ] = $setting['default'];
  182.         }
  183.         return $defaults;
  184.     }
  185.  
  186.     function activate_plugin() {
  187.         global $wp_version;
  188.         if ( version_compare( $wp_version, '3.3', '<' ) ) {
  189.             wp_die( __( 'Frontend Uploader requires WordPress 3.3 or newer. Please upgrade.', 'frontend-uploader' ) );
  190.         }
  191.         $defaults = $this->settings_defaults();
  192.         $existing_settings = (array) get_option( $this->settings_slug, $this->settings_defaults() );
  193.         update_option( $this->settings_slug, array_merge( $defaults, (array) $existing_settings ) );
  194.     }
  195.  
  196.     /**
  197.      * Since WP 3.5-beta-1 WP Media interface shows private attachments as well
  198.      * We don't want that, so we force WHERE statement to post_status = 'inherit'
  199.      *
  200.      * @since  0.3
  201.      *
  202.      * @param string  $where WHERE statement
  203.      * @return string WHERE statement
  204.      */
  205.     function filter_posts_where( $where ) {
  206.         if ( !is_admin() )
  207.             return $where;
  208.  
  209.         $screen = get_current_screen();
  210.         if ( ! defined( 'DOING_AJAX' ) && $screen->base == 'upload' && ( !isset( $_GET['page'] ) || $_GET['page'] != 'manage_frontend_uploader' ) ) {
  211.             $where = str_replace( "post_status = 'private'", "post_status = 'inherit'", $where );
  212.         }
  213.         return $where;
  214.     }
  215.  
  216.     /**
  217.      * Determine if we should autoapprove the submission or not
  218.      *
  219.      * @return boolean [description]
  220.      */
  221.     function _is_public() {
  222.         return  ( current_user_can( 'read' ) && 'on' == $this->settings['auto_approve_user_files'] ) ||  ( 'on' == $this->settings['auto_approve_any_files'] );
  223.     }
  224.  
  225.     /**
  226.      * Handle uploading of the files
  227.      *
  228.      * @since  0.4
  229.      *
  230.      * @uses media_handle_sideload Don't even know why
  231.      *
  232.      * @param int     $post_id Parent post id
  233.      * @return array Combined result of media ids and errors if any
  234.      */
  235.     function _handle_files( $post_id ) {
  236.         $media_ids = $errors = array();
  237.         // Bail if there are no files
  238.         if ( empty( $_FILES ) )
  239.             return false;
  240.  
  241.         // File field name could be user defined, so we just get the first file
  242.         $files = current( $_FILES );
  243.  
  244.         for ( $i = 0; $i < count( $files['name'] ); $i++ ) {
  245.             $fields = array( 'name', 'type', 'tmp_name', 'error', 'size' );
  246.             foreach ( $fields as $field ) {
  247.                 $k[$field] = $files[$field][$i];
  248.             }
  249.  
  250.             $k['name'] = sanitize_file_name( $k['name'] );
  251.  
  252.             // Skip to the next file if upload went wrong
  253.             if ( $k['tmp_name'] == "" ) {
  254.                 continue;
  255.             }
  256.  
  257.             preg_match( '/.(?P<ext>[a-zA-Z0-9]+)$/', $k['name'], $ext_match );
  258.             // Add an error message if MIME-type is not allowed
  259.             if ( ! in_array( $k['type'], (array) $this->allowed_mime_types ) ) {
  260.                 $errors['fu-disallowed-mime-type'][] = array( 'name' => $k['name'], 'mime' => $k['type'] );
  261.                 continue;
  262.             }
  263.  
  264.             // Setup some default values
  265.             // However, you can make additional changes on 'fu_after_upload' action
  266.             $caption = '';
  267.  
  268.             // Try to set post caption if the field is set on request
  269.             // Fallback to post_content if the field is not set
  270.             // @todo remove this in v0.6 when automatic handling of shortcode attributes is implemented
  271.             if ( isset( $_POST['caption'] ) )
  272.                 $caption = sanitize_text_field( $_POST['caption'] );
  273.             elseif ( isset( $_POST['post_content'] ) )
  274.                 $caption = sanitize_text_field( $_POST['post_content'] );
  275.             // @todo remove or refactor
  276.             $post_overrides = array(
  277.                 'post_status' => $this->_is_public() ? 'publish' : 'private',
  278.                 'post_title' => isset( $_POST['post_title'] ) && ! empty( $_POST['post_title'] ) ? sanitize_text_field( $_POST['post_title'] ) : 'Unnamed',
  279.                 'post_content' => empty( $caption ) ? __( 'Unnamed', 'frontend-uploader' ) : $caption,
  280.                 'post_excerpt' => empty( $caption ) ? __( 'Unnamed', 'frontend-uploader' ) :  $caption,
  281.             );
  282.  
  283.             // Trying to upload the file
  284.             $upload_id = media_handle_sideload( $k, (int) $post_id, $post_overrides['post_title'], $post_overrides );
  285.             if ( !is_wp_error( $upload_id ) )
  286.                 $media_ids[] = $upload_id;
  287.             else
  288.                 $errors['fu-error-media'][] = $k['name'];
  289.         }
  290.  
  291.         $success = empty( $errors ) && !empty( $media_ids ) ? true : false;
  292.         // Allow additional setup
  293.         // Pass array of attachment ids
  294.         do_action( 'fu_after_upload', $media_ids, $success );
  295.         return array( 'success' => $success, 'media_ids' => $media_ids, 'errors' => $errors );
  296.     }
  297.  
  298.     /**
  299.      * Handle post uploads
  300.      *
  301.      * @since 0.4
  302.      */
  303.     function _upload_post() {
  304.         $errors = array();
  305.         $success = true;
  306.  
  307.         // Construct post array;
  308.         $post_array = array(
  309.             'post_type' =>  isset( $_POST['post_type'] ) && in_array( $_POST['post_type'], $this->settings['enabled_post_types'] ) ? $_POST['post_type'] : 'post',
  310.             'post_title'    => isset( $_POST['caption'] ) ? sanitize_text_field( $_POST['caption'] )  : sanitize_text_field( $_POST['post_title'] ),
  311.             'post_content'  => wp_filter_post_kses( $_POST['post_content'] ),
  312.             'post_status'   => $this->_is_public() ? 'publish' : 'private',
  313.         );
  314.  
  315.         // Determine if we have a whitelisted category
  316.         $allowed_categories = array_filter( explode( ",", str_replace( " ", "",  $this->settings['allowed_categories'] ) ) );
  317.  
  318.         if (  isset( $_POST['post_category'] ) && in_array( $_POST['post_category'], $allowed_categories ) ) {
  319.             $post_array = array_merge( $post_array, array( 'post_category' => array( (int) $_POST['post_category'] ) ) );
  320.         }
  321.  
  322.         $author = isset( $_POST['post_author'] ) ? sanitize_text_field( $_POST['post_author'] ) : '';
  323.         $users = get_users( array(
  324.                 'search' => $author,
  325.                 'fields' => 'ID'
  326.             ) );
  327.  
  328.         if ( isset( $users[0] ) ) {
  329.             $post_array['post_author'] = (int) $users[0];
  330.         }
  331.  
  332.         $post_id = wp_insert_post( $post_array, true );
  333.         // Something went wrong
  334.         if ( is_wp_error( $post_id ) ) {
  335.             $errors[] = 'fu-error-post';
  336.             $success = false;
  337.         } else {
  338.             do_action( 'fu_after_create_post', $post_id );
  339.             // If the author name is not in registered users
  340.             // Save the author name if it was filled and post was created successfully
  341.             if ( $author )
  342.                 add_post_meta( $post_id, 'author_name', $author );
  343.         }
  344.  
  345.         return array( 'success' => $success, 'post_id' => $post_id, 'errors' => $errors );
  346.     }
  347.  
  348.     /**
  349.      * Handle post, post+media, or just media files
  350.      *
  351.      * @since  0.4
  352.      */
  353.     function upload_content() {
  354.         $fields = array();
  355.         // @todo sanity check
  356.         $time = $_POST['request_time'];
  357.  
  358.         $this->request_form_fields =  json_decode( urldecode( stripslashes( $_POST['form_fields'] ) ) );
  359.  
  360.         // Bail if something fishy is going on
  361.         if ( !wp_verify_nonce( $_POST['fu_nonce'], FU_FILE_PATH ) ) {
  362.             wp_safe_redirect( add_query_arg( array( 'response' => 'fu-error', 'errors' =>  'nonce-failure' ), wp_get_referer() ) );
  363.             exit;
  364.         }
  365.  
  366.  
  367.  
  368.         $layout = isset( $_POST['form_layout'] ) && !empty( $_POST['form_layout'] ) ? $_POST['form_layout'] : 'image';
  369.         switch ( $layout ) {
  370.         case 'post':
  371.  
  372.             $result = $this->_upload_post();
  373.             break;
  374.         case 'post_image':
  375.         case 'post_media';
  376.             $response = $this->_upload_post();
  377.             if ( ! is_wp_error( $response['post_id'] ) ) {
  378.                 $result = $this->_handle_files( $response['post_id'] );
  379.                 $result = array_merge( $result, $response );
  380.             }
  381.             break;
  382.         case 'image':
  383.         case 'media':
  384.  
  385.             if ( isset( $_POST['post_ID'] ) && 0 !== $pid = (int) $_POST['post_ID'] ) {
  386.                 $result = $this->_handle_files( $pid );
  387.             }
  388.  
  389.             break;
  390.         }
  391.  
  392.         $this->_notify_admin( $result );
  393.         $this->_handle_result( $result );
  394.         exit;
  395.     }
  396.  
  397.     /**
  398.      * Notify site administrator by email
  399.      */
  400.     function _notify_admin( $result = array() ) {
  401.         // Notify site admins of new upload
  402.         if ( ! ( 'on' == $this->settings['notify_admin'] && $result['success'] ) )
  403.             return;
  404.         // @todo It'd be nice to add the list of upload files
  405.         $to = !empty( $this->settings['notification_email'] ) && filter_var( $this->settings['notification_email'], FILTER_VALIDATE_EMAIL ) ? $this->settings['notification_email'] : get_option( 'admin_email' );
  406.         $subj = __( 'New content was uploaded on your site', 'frontend-uploader' );
  407.         wp_mail( $to, $subj, $this->settings['admin_notification_text'] );
  408.  
  409.     }
  410.  
  411.     /**
  412.      * Process response from upload logic
  413.      *
  414.      * @since  0.4
  415.      */
  416.     function _handle_result( $result = array() ) {
  417.         // Redirect to referrer if repsonse is malformed
  418.         if ( empty( $result ) || !is_array( $result ) ) {
  419.             wp_safe_redirect( wp_get_referer() );
  420.             return;
  421.         }
  422.  
  423.         $errors_formatted = array();
  424.         // Either redirect to success page if it's set and valid
  425.         // Or to referrer
  426.         $url = isset( $_POST['success_page'] ) && filter_var( $_POST['success_page'], FILTER_VALIDATE_URL ) ? $_POST['success_page'] : wp_get_referer();
  427.  
  428.         // $query_args will hold everything that's needed for displaying notices to user
  429.         $query_args = array();
  430.  
  431.         // Account for successful uploads
  432.         if ( isset( $result['success'] ) && $result['success'] ) {
  433.             // If it's a post
  434.             if ( isset( $result['post_id'] ) )
  435.                 $query_args['response'] = 'fu-post-sent';
  436.             // If it's media uploads
  437.             if ( isset( $result['media_ids'] ) && !isset( $result['post_id'] ) )
  438.                 $query_args['response'] = 'fu-sent';
  439.         }
  440.  
  441.  
  442.         // Some errors happened
  443.         // Format a string to be passed as GET value
  444.         if ( !empty( $result['errors'] ) ) {
  445.             $query_args['response'] = 'fu-error';
  446.             $_errors = array();
  447.  
  448.             // Iterate through key=>value pairs of errors
  449.             foreach ( $result['errors'] as $key => $error ) {
  450.  
  451.                 // Do not display mime-types in production
  452.                 if ( !$this->is_debug && isset( $error[0]['mime'] ) )
  453.                     unset( $error[0]['mime'] );
  454.                 if ( isset( $error[0] ) )
  455.                     $_errors[$key] = join( ',,,', (array) $error[0] );
  456.             }
  457.  
  458.             foreach ( $_errors as $key => $value ) {
  459.                 $errors_formatted[] = "{$key}:{$value}";
  460.             }
  461.  
  462.             $query_args['errors'] = join( ';', $errors_formatted );
  463.         }
  464.  
  465.         wp_safe_redirect( add_query_arg( array( $query_args ) , $url ) );
  466.     }
  467.  
  468.     /**
  469.      * Render various admin template files
  470.      *
  471.      * @param string  $view file slug
  472.      * @since 0.4
  473.      */
  474.     function render( $view = '' ) {
  475.         if ( empty( $view ) )
  476.             return;
  477.  
  478.         $file = FU_ROOT . "/lib/views/{$view}.tpl.php";
  479.         if ( file_exists( $file ) )
  480.             require $file;
  481.     }
  482.  
  483.     /**
  484.      * Display media list table
  485.      *
  486.      * @return [type] [description]
  487.      */
  488.     function admin_list() {
  489.         $this->render( 'manage-ugc-media' );
  490.     }
  491.  
  492.     /**
  493.      * Display posts/custom post types table
  494.      *
  495.      * @return [type] [description]
  496.      */
  497.     function admin_posts_list() {
  498.         $this->render( 'manage-ugc-posts' );
  499.     }
  500.  
  501.     /**
  502.      * Add submenu items
  503.      */
  504.     function add_menu_items() {
  505.         add_media_page( __( 'Manage UGC', 'frontend-uploader' ), __( 'Manage UGC', 'frontend-uploader' ), $this->manage_permissions, 'manage_frontend_uploader', $this->_a(  'admin_list' ) );
  506.         foreach ( (array) $this->settings['enabled_post_types'] as $cpt ) {
  507.             if ( $cpt == 'post' ) {
  508.                 add_posts_page( __( 'Manage UGC Posts', 'frontend-uploader' ), __( 'Manage UGC', 'frontend-uploader' ), $this->manage_permissions, 'manage_frontend_uploader_posts', $this->_a(  'admin_posts_list' ) );
  509.                 continue;
  510.             }
  511.  
  512.             add_submenu_page( "edit.php?post_type={$cpt}", __( 'Manage UGC Posts', 'frontend-uploader' ), __( 'Manage UGC', 'frontend-uploader' ), $this->manage_permissions, "manage_frontend_uploader_{$cpt}s", $this->_a(  'admin_posts_list' ) );
  513.         }
  514.     }
  515.  
  516.     /**
  517.      * Approve a media file
  518.      *
  519.      * @todo refactor in 0.6
  520.      * @return [type] [description]
  521.      */
  522.     function approve_photo() {
  523.         // Check permissions, attachment ID, and nonce
  524.         if ( ! $this->_check_perms_and_nonce() || 0 !== (int) $_GET['id'] )
  525.             wp_safe_redirect( get_admin_url( null, 'upload.php?page=manage_frontend_uploader&error=id_or_perm' ) );
  526.  
  527.         $post = get_post( $_GET['id'] );
  528.  
  529.         if ( is_object( $post ) && $post->post_status == 'private' ) {
  530.             $post->post_status = 'inherit';
  531.             wp_update_post( $post );
  532.             $this->update_35_gallery_shortcode( $post->post_parent, $post->ID );
  533.             wp_safe_redirect( get_admin_url( null, 'upload.php?page=manage_frontend_uploader&approved=1' ) );
  534.         }
  535.  
  536.         wp_safe_redirect( get_admin_url( null, 'upload.php?page=manage_frontend_uploader' ) );
  537.         exit;
  538.     }
  539.  
  540.     /**
  541.      *
  542.      *
  543.      * @todo refactor in 0.6
  544.      * @return [type] [description]
  545.      */
  546.     function approve_post() {
  547.         // check for permissions and id
  548.         $url = get_admin_url( null, 'edit.php?page=manage_frontend_uploader_posts&error=id_or_perm' );
  549.         if ( !current_user_can( $this->manage_permissions ) || intval( $_GET['id'] ) == 0  )
  550.             wp_safe_redirect( $url );
  551.  
  552.         $post = get_post( $_GET['id'] );
  553.  
  554.         if ( !is_wp_error( $post ) ) {
  555.             $post->post_status = 'publish';
  556.             wp_update_post( $post );
  557.  
  558.             // Check if there's any UGC attachments
  559.             $attachments = get_children( 'post_type=attachment&post_parent=' . $post->ID );
  560.             foreach ( (array) $attachments as $image_id => $attachment ) {
  561.                 $attachment->post_status = "inherit";
  562.                 wp_update_post( $attachment );
  563.             }
  564.  
  565.             // Override query args
  566.             $qa = array(
  567.                 'page' => "manage_frontend_uploader_{$post->post_type}s",
  568.                 'approved' => 1,
  569.                 'post_type' => $post->post_type != 'post' ? $post->post_type : '',
  570.             );
  571.  
  572.             $url = add_query_arg( $qa, get_admin_url( null, "edit.php" ) );
  573.         }
  574.  
  575.         wp_safe_redirect( $url );
  576.         exit;
  577.     }
  578.  
  579.     /**
  580.      * Delete post and redirect to referrer
  581.      * @return [type] [description]
  582.      */
  583.     function delete_post() {
  584.         if ( $this->_check_perms_and_nonce() && 0 !== (int) $_GET['id'] ) {
  585.                         if ( wp_delete_post( (int) $_GET['id'], true ) )
  586.                             $args['deleted'] = 1;
  587.         }
  588.  
  589.         wp_safe_redirect( add_query_arg( $args, wp_get_referer() ) );
  590.         exit;
  591.     }
  592.  
  593.     /**
  594.      * Handles security checks
  595.      * @return bool
  596.      */
  597.     function _check_perms_and_nonce() {
  598.         return current_user_can( $this->manage_permissions ) && wp_verify_nonce( $_REQUEST['fu_nonce'], FU_FILE_PATH );
  599.     }
  600.  
  601.     /**
  602.      * Shortcode callback for inner content of [fu-upload-form] shortcode
  603.      *
  604.      * @param array   $atts    shortcode attributes
  605.      * @param unknown $content not used
  606.      * @param string  $tag
  607.      */
  608.     function shortcode_content_parser( $atts, $content = null, $tag ) {
  609.         $atts = shortcode_atts( array(
  610.                 'id' => '',
  611.                 'name' => '',
  612.                 'description' => '',
  613.                 'value' => '',
  614.                 'type' => '',
  615.                 'class' => '',
  616.                 'multiple' => false,
  617.                 'values' => '',
  618.                 'wysiwyg_enabled' => false,
  619.                 'context' => 'meta'
  620.             ), $atts );
  621.  
  622.         extract( $atts );
  623.  
  624.         $this->form_fields[] = array(
  625.             'name' => $name,
  626.             'context' => $context,
  627.             'tag' => $tag,
  628.             'type' => $type,
  629.             'value' => $value
  630.         );
  631.  
  632.         $callback = $this->_a(  "_render_{$tag}" );
  633.         if ( is_callable( $callback ) )
  634.             return call_user_func( $callback, $atts );
  635.     }
  636.  
  637.     /**
  638.      * Input shortcode
  639.      *
  640.      * @param array   shortcode attributes
  641.      * @return [type]       [description]
  642.      */
  643.     function _render_input( $atts ) {
  644.         extract( $atts );
  645.         $atts = array( 'id' => $id, 'class' => $class, 'multiple' => $multiple );
  646.         // Workaround for HTML5 multiple attribute
  647.         if ( $multiple == 'false' )
  648.             unset( $atts['multiple'] );
  649.  
  650.         // Allow multiple file upload by default.
  651.         // To do so, we need to add array notation to name field: []
  652.         if ( !strpos( $name, '[]' ) && $type == 'file' )
  653.             $name = 'files' . '[]';
  654.  
  655.         $element = $this->html->element( 'label', $description . $this->html->input( $type, $name, $value, $atts ) , array( 'for' => $id ), false );
  656.  
  657.         return $this->html->element( 'div', $element, array( 'class' => 'ugc-input-wrapper' ), false );
  658.     }
  659.  
  660.     /**
  661.      * Textarea shortcode
  662.      *
  663.      * @param array   shortcode attributes
  664.      * @return [type]       [description]
  665.      */
  666.     function _render_textarea( $atts ) {
  667.         extract( $atts );
  668.         // Render WYSIWYG textara
  669.         if ( ( isset( $this->settings['wysiwyg_enabled'] ) && 'on' == $this->settings['wysiwyg_enabled'] ) || $wysiwyg_enabled == true ) {
  670.             ob_start();
  671.             wp_editor( '', $id, array(
  672.                     'textarea_name' => $name,
  673.                     'media_buttons' => false,
  674.                     'teeny' => true,
  675.                     'quicktags' => false
  676.                 ) );
  677.             $tiny = ob_get_clean();
  678.             $label =  $this->html->element( 'label', $description , array( 'for' => $id ), false );
  679.             return $this->html->element( 'div', $label . $tiny, array( 'class' => 'ugc-input-wrapper' ), false ) ;
  680.         }
  681.         // Render plain textarea
  682.         $element = $this->html->element( 'label', $description . $this->html->element( 'textarea', '', array(
  683.                     'name' => $name,
  684.                     'id' => $id,
  685.                     'class' => $class
  686.                 ) ), array( 'for' => $id ), false );
  687.  
  688.         return $this->html->element( 'div', $element, array( 'class' => 'ugc-input-wrapper' ), false );
  689.     }
  690.  
  691.     /**
  692.      * Checkboxes shortcode
  693.      *
  694.      * @param array   shortcode attributes
  695.      * @return [type]       [description]
  696.      */
  697.     function _render_checkboxes( $atts ) {
  698.         extract( $atts );
  699.         return;
  700.     }
  701.  
  702.     /**
  703.      * Radio buttons shortcode
  704.      *
  705.      * @param array   shortcode attributes
  706.      * @return [type]       [description]
  707.      */
  708.     function _render_radio( $atts ) {
  709.         extract( $atts );
  710.         return;
  711.     }
  712.  
  713.     /**
  714.      * Select shortcode
  715.      *
  716.      * @param array   shortcode attributes
  717.      * @return [type]       [description]
  718.      */
  719.     function _render_select( $atts ) {
  720.         extract( $atts );
  721.         $atts = array( 'values' => $values );
  722.         $values = explode( ',', $values );
  723.         $options = '';
  724.         //Build options for the list
  725.         foreach ( $values as $option ) {
  726.             $options .= $this->html->element( 'option', $option, array( 'value' => $option ), false );
  727.         }
  728.         //Render select field
  729.         $element = $this->html->element( 'label', $description . $this->html->element( 'select', $options, array(
  730.                     'name' => $name,
  731.                     'id' => $id,
  732.                     'class' => $class
  733.                 ), false ), array( 'for' => $id ), false );
  734.         return $this->html->element( 'div', $element, array( 'class' => 'ugc-input-wrapper' ), false );
  735.     }
  736.  
  737.     /**
  738.      * Display the upload post form
  739.      *
  740.      * @todo Major refactoring for this before releasing 0.6
  741.      *
  742.      * @param array   $atts    shortcode attributes
  743.      * @param string  $content content that is encloded in [fu-upload-form][/fu-upload-form]
  744.      */
  745.     function upload_form( $atts, $content = null ) {
  746.  
  747.         // Reset postdata in case it got polluted somewhere
  748.         wp_reset_postdata();
  749.         extract( shortcode_atts( array(
  750.                     'description' => '',
  751.                     'title' => __( 'Submit a new post', 'frontend-uploader' ),
  752.                     'type' => '',
  753.                     'class' => 'validate',
  754.                     'category' => '1',
  755.                     'success_page' => '',
  756.                     'form_layout' => 'image',
  757.                     'post_id' => get_the_ID(),
  758.                     'post_type' => 'post',
  759.                 ), $atts ) );
  760.         $post_id = (int) $post_id;
  761.  
  762.         switch ( $form_layout ) {
  763.         case 'image':
  764.         case 'media':
  765.             $title = __( 'Submit a media file', 'frontend-uploader' );
  766.             break;
  767.         case 'post':
  768.         case 'post_media':
  769.             break;
  770.         default:
  771.         }
  772.  
  773.         ob_start();
  774. ?>
  775.     <form action="<?php echo admin_url( 'admin-ajax.php' ) ?>" method="post" id="ugc-media-form" class="<?php echo esc_attr( $class )?>" enctype="multipart/form-data">
  776.       <div class="ugc-inner-wrapper">
  777.           <h2><?php echo esc_html( $title ) ?></h2>
  778. <?php
  779.         if ( !empty( $_GET ) )
  780.             $this->_display_response_notices( $_GET );
  781.  
  782.         $textarea_desc = __( 'Description', 'frontend-uploader' );
  783.         $file_desc = __( 'Your Media Files', 'frontend-uploader' );
  784.         $submit_button = __( 'Submit', 'frontend-uploader' );
  785.         // Display title field
  786.         echo do_shortcode ( '[input type="text" context="title" name="post_title" id="ug_post_title" description="' . __( 'Title', 'frontend-uploader' ) . '" class="required"]' );
  787.  
  788.         // Display default form fields
  789.         switch ( $form_layout ) {
  790.         case 'post_image':
  791.         case 'post_media':
  792.         case 'image':
  793.         case 'media':
  794.             echo do_shortcode( '
  795.             [textarea name="post_content" context="content" class="textarea" id="ug_content" class="required" description="'. __( 'Post content or file description', 'frontend-uploader' ) .'"]
  796.             [input type="file" name="files" id="ug_photo" description="'. $file_desc .'" multiple=""]
  797.             [input type="text" name="post_tags" id="ug_tags" description="Mot-clef" multiple=""]
  798.             ' );
  799.             break;
  800.         case 'post':
  801.             echo do_shortcode( '[textarea name="post_content" context="content" class="textarea" id="ug_content" class="required" description="'. __( 'Post content', 'frontend-uploader' ) .'"]' );
  802.             break;
  803.             break;
  804.         }
  805.  
  806.         // Show author field
  807.         if ( isset( $this->settings['show_author'] )  && $this->settings['show_author'] == 'on' )
  808.             echo do_shortcode ( '[input type="text" name="post_author" id="ug_post_author" description="' . __( 'Author', 'frontend-uploader' ) . '" class=""]' );
  809.  
  810.         // Parse nested shortcodes
  811.         if ( $content )
  812.             echo do_shortcode( $content );
  813.  
  814.  
  815.         echo do_shortcode ( '[input type="submit" class="btn" value="'. $submit_button .'"]' );
  816.  
  817.         echo do_shortcode ( '[input type="hidden" name="action" value="upload_ugc" context="internal"]' );
  818.         echo do_shortcode ( '[input type="hidden" name="post_ID" value="' . $post_id . '" context="internal"]' );
  819.         echo do_shortcode ( '[input type="hidden" name="post_category" value="' . $category . '" context="internal"]' );
  820.         echo do_shortcode ( '[input type="hidden" name="success_page" value="' . $success_page . '" context="internal"]' );
  821.         echo do_shortcode ( '[input type="hidden" name="form_layout" value="' . $form_layout . '" context="internal"]' );
  822.         // @todo 0.6
  823.         ?> <input type="hidden" name="form_fields" value="<?php echo esc_attr( json_encode( $this->form_fields ) ) ?>" /> <?php
  824.  
  825.         if ( in_array( $form_layout, array( "post_image", "post" ) ) )
  826.             echo do_shortcode ( '[input type="hidden" name="post_type" value="' . $post_type . '" context="hidden"]' );
  827.  
  828.         // Allow a little customization
  829.         do_action( 'fu_additional_html' );
  830.         $time = time();
  831.  
  832.         // @todo this probably won't work
  833.         wp_cache_add( "fu_upload:{$time}", $this->form_fields, 'frontend-uploader', 600 );
  834. ?>
  835. <input type="hidden" name="request_time" value="<?php echo $time ?>" />
  836.           <?php wp_nonce_field( FU_FILE_PATH, 'fu_nonce' ); ?>
  837.           <div class="clear"></div>
  838.       </div>
  839.       </form>
  840. <?php
  841.         return ob_get_clean();
  842.     }
  843.  
  844.     /**
  845.      * Returns html chunk of single notice
  846.      *
  847.      * @since 0.4
  848.      *
  849.      * @param string  $message Text of the message
  850.      * @param string  $class   Class of container
  851.      * @return string          [description]
  852.      */
  853.     function _notice_html( $message, $class ) {
  854.         if ( empty( $message ) || empty( $class ) )
  855.             return;
  856.         return sprintf( '<p class="ugc-notice %1$s">%2$s</p>', $class, $message );
  857.     }
  858.  
  859.     /**
  860.      * Handle response notices
  861.      *
  862.      * @since 0.4
  863.      *
  864.      * @param array   $res [description]
  865.      * @return [type]      [description]
  866.      */
  867.     function _display_response_notices( $res = array() ) {
  868.         if ( empty( $res ) )
  869.             return;
  870.  
  871.         $output = '';
  872.         $map = array(
  873.             'fu-sent' => array(
  874.                 'text' => __( 'Your file was successfully uploaded!', 'frontend-uploader' ),
  875.                 'class' => 'success',
  876.             ),
  877.             'fu-post-sent' => array(
  878.                 'text' => __( 'Your post was successfully uploaded!', 'frontend-uploader' ),
  879.                 'class' => 'success',
  880.             ),
  881.             'fu-error' => array(
  882.                 'text' => __( 'There was an error with your submission', 'frontend-uploader' ),
  883.                 'class' => 'failure',
  884.             ),
  885.         );
  886.  
  887.         if ( isset( $res['response'] ) && isset( $map[ $res['response'] ] ) )
  888.             $output .= $this->_notice_html( $map[ $res['response'] ]['text'] , $map[ $res['response'] ]['class'] );
  889.  
  890.         if ( !empty( $res['errors' ] ) )
  891.             $output .= $this->_display_errors( $res['errors' ] );
  892.  
  893.         echo $output;
  894.     }
  895.     /**
  896.      * Handle errors
  897.      *
  898.      * @since 0.4
  899.      * @param string  $errors [description]
  900.      * @return string HTML
  901.      */
  902.     function _display_errors( $errors ) {
  903.         $errors_arr = explode( ';', $errors );
  904.         $output = '';
  905.         $map = array(
  906.             'nonce-failure' => array(
  907.                 'text' => __( 'Security check failed!', 'frontend-uploader' ),
  908.             ),
  909.             'fu-disallowed-mime-type' => array(
  910.                 'text' => __( 'This kind of file is not allowed. Please, try again selecting other file.', 'frontend-uploader' ),
  911.                 'format' => $this->is_debug ? '%1$s: <br/> File name: %2$s <br/> MIME-TYPE: %3$s' : '%1$s: <br/> %2$s',
  912.             ),
  913.             'fu-invalid-post' => array(
  914.                 'text' =>__( 'The content you are trying to post is invalid.', 'frontend-uploader' ),
  915.             ),
  916.             'fu-error-media' => array(
  917.                 'text' =>__( "Couldn't upload the file", 'frontend-uploader' ),
  918.             ),
  919.             'fu-error-post' => array(
  920.                 'text' =>__( "Couldn't create the post", 'frontend-uploader' ),
  921.             ),
  922.         );
  923.  
  924.         foreach ( $errors_arr as $error ) {
  925.             $error_type = explode( ':', $error );
  926.             $error_details = explode( '|', $error_type[1] );
  927.             // Iterate over different errors
  928.             foreach ( $error_details as $single_error ) {
  929.  
  930.                 // And see if there's any additional details
  931.                 $details = isset( $single_error ) ? explode( ',,,', $single_error ) : explode( ',,,', $single_error );
  932.                 // Add a description to our details array
  933.                 array_unshift( $details, $map[ $error_type[0] ]['text']  );
  934.                 // If we have a format, let's format an error
  935.                 // If not, just display the message
  936.                 if ( isset( $map[ $error_type[0] ]['format'] ) )
  937.                     $message = vsprintf( $map[ $error_type[0] ]['format'], $details );
  938.                 else
  939.                     $message = $map[ $error_type[0] ]['text'];
  940.             }
  941.             $output .= $this->_notice_html( $message, 'failure' );
  942.         }
  943.  
  944.         return $output;
  945.     }
  946.  
  947.     /**
  948.      * Enqueue our assets
  949.      */
  950.     function enqueue_scripts() {
  951.         wp_enqueue_style( 'frontend-uploader', FU_URL . 'lib/css/frontend-uploader.css' );
  952.         wp_enqueue_script( 'jquery-validate', FU_URL .' lib/js/validate/jquery.validate.js ', array( 'jquery' ) );
  953.         wp_enqueue_script( 'frontend-uploader-js', FU_URL . 'lib/js/frontend-uploader.js', array( 'jquery', 'jquery-validate' ) );
  954.         // Include localization strings for default messages of validation plugin
  955.         // Filter is needed for wordpress.com
  956.         $wplang = apply_filters( 'fu_wplang', defined( 'WPLANG' ) ? WPLANG : '' );
  957.         if ( $wplang ) {
  958.             $lang = explode( '_', $wplang );
  959.             $relative_path = "lib/js/validate/localization/messages_{$lang[0]}.js";
  960.             $url = FU_URL . $relative_path;
  961.             if ( file_exists(  FU_ROOT . "/{$relative_path}" ) )
  962.                 wp_enqueue_script( 'jquery-validate-messages', $url, array( 'jquery' ) );
  963.         }
  964.  
  965.     }
  966.  
  967.     /**
  968.      * Enqueue scripts for admin
  969.      */
  970.     function admin_enqueue_scripts() {
  971.         wp_enqueue_script( 'wp-ajax-response' );
  972.         wp_enqueue_script( 'jquery-ui-draggable' );
  973.         wp_enqueue_script( 'media' );
  974.     }
  975.  
  976.     /**
  977.      * 3.5 brings new Media UI
  978.      * Unfortunately, we have to specify ids of approved attachments explicitly,
  979.      * Otherwise, editors have to pick photos after they have already approved them in "Manage UGC"
  980.      *
  981.      * This method will search a parent post with a regular expression, and update gallery shortcode with freshly approved attachment ID
  982.      *
  983.      * @return post id/wp_error
  984.      */
  985.     function update_35_gallery_shortcode( $post_id, $attachment_id ) {
  986.         global $wp_version;
  987.         // Bail of wp is older than 3.5
  988.         if ( version_compare( $wp_version, '3.5', '<' ) )
  989.             return;
  990.  
  991.         $parent = get_post( $post_id );
  992.  
  993.         /**
  994.          * Parse the post content:
  995.          * Before the shorcode,
  996.          * Before ids,
  997.          * Ids,
  998.          * After ids
  999.          */
  1000.         preg_match( '#(?<way_before>.*)(?<before>\[gallery(.*))ids=(\'|")(?<ids>[0-9,]*)(\'|")(?<after>.*)#ims', $parent->post_content, $matches ) ;
  1001.  
  1002.         // No gallery shortcode, no problem
  1003.         if ( !isset( $matches['ids'] ) )
  1004.             return;
  1005.  
  1006.         $content = '';
  1007.         $if_prepend = apply_filters( 'fu_update_gallery_shortcode_prepend', false );
  1008.         // Replace ids element with actual string of ids, adding the new att id
  1009.         $matches['ids'] = $if_prepend ? "ids=\"{$attachment_id},{$matches['ids']}\"" : "ids=\"{$matches['ids']},{$attachment_id}\"";
  1010.         $deconstructed = array( 'way_before', 'before', 'ids', 'after' );
  1011.         // Iterate through match elements and reconstruct the post
  1012.         foreach ( $deconstructed as $match_key ) {
  1013.             if ( isset( $matches[$match_key] ) ) {
  1014.                 $content .= $matches[$match_key];
  1015.             }
  1016.         }
  1017.  
  1018.         // Update the post
  1019.         $post_to_update = array(
  1020.             'ID' => (int) $post_id,
  1021.             'post_content' => $content,
  1022.         );
  1023.         return wp_update_post( $post_to_update );
  1024.     }
  1025.  
  1026.     function _a( $method ) {
  1027.         return array( $this, $method );
  1028.     }
  1029.    
  1030.  
  1031. }
  1032. global $frontend_uploader;
  1033. $frontend_uploader = new Frontend_Uploader;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement