Статьи

Разрешить пользователям отправлять изображения на ваш сайт WordPress

В этом руководстве вы узнаете, как создать плагин, который позволит пользователям отправлять изображения и загружать их в медиатеку WordPress. Вы также узнаете, как правильно удалять изображения из медиатеки WordPress, а также проведете базовую проверку загруженных изображений.


Этот учебник по запросу от некоторых пользователей, которые нашли мой учебник по плагину цитат интересным, но особенно хотели узнать, как можно использовать ту же технику для загрузки изображений из внешнего интерфейса. Итак, вот повторение этого урока, который делает именно это. Подробную информацию о настройке плагинов, шорткодах и одноразовых номерах смотрите в предыдущем руководстве.

Плагин будет:

  • отобразить форму загрузки изображения с помощью шорткода
  • принимать только изображения определенного типа и максимального размера
  • добавить пользовательский тип записи для пользовательских изображений
  • добавить изображения в медиатеку WordPress с правильной надписью вложения
  • отображать неопубликованные изображения
  • разрешить пользователям удалять свои неопубликованные изображения

Мы будем использовать встроенное в метаполе WordPress thumbnail (aka Featured Image) для хранения изображения для каждого сообщения. Это также облегчает отображение и работу с нашим изображением, поскольку мы можем использовать функции post_thumbnail .

Вот к чему мы стремимся:

Весь код доступен в исходном коде плагина в верхней части этого урока.


Создайте файл плагина с именем submit_user_images.php в submit_user_images.php wp-content/plugins/submit-user-images .

Обратитесь к источнику плагина для информации заголовка плагина.


Мы собираемся создать пользовательский тип записи с именем user_images для хранения пользовательских изображений и пользовательскую таксономию с именем user_image_category. Это позволит более четко администрировать изображения, чем просто назначать их обычным сообщениям и категориям.

Мы будем использовать следующий код инициализации для создания нашего пользовательского типа записи и пользовательской таксономии:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
add_action(‘init’, ‘sui_plugin_init’);
 
function sui_plugin_init(){
 
  $image_type_labels = array(
    ‘name’ => _x(‘User images’, ‘post type general name’),
    ‘singular_name’ => _x(‘User Image’, ‘post type singular name’),
    ‘add_new’ => _x(‘Add New User Image’, ‘image’),
    ‘add_new_item’ => __(‘Add New User Image’),
    ‘edit_item’ => __(‘Edit User Image’),
    ‘new_item’ => __(‘Add New User Image’),
    ‘all_items’ => __(‘View User Images’),
    ‘view_item’ => __(‘View User Image’),
    ‘search_items’ => __(‘Search User Images’),
    ‘not_found’ => __(‘No User Images found’),
    ‘not_found_in_trash’ => __(‘No User Images found in Trash’),
    ‘parent_item_colon’ => »,
    ‘menu_name’ => ‘User Images’
  );
   
  $image_type_args = array(
    ‘labels’ => $image_type_labels,
    ‘public’ => true,
    ‘query_var’ => true,
    ‘rewrite’ => true,
    ‘capability_type’ => ‘post’,
    ‘has_archive’ => true,
    ‘hierarchical’ => false,
    ‘map_meta_cap’ => true,
    ‘menu_position’ => null,
    ‘supports’ => array(‘title’, ‘editor’, ‘author’, ‘thumbnail’)
  );
   
  register_post_type(‘user_images’, $image_type_args);
 
  $image_category_labels = array(
    ‘name’ => _x( ‘User Image Categories’, ‘taxonomy general name’ ),
    ‘singular_name’ => _x( ‘User Image’, ‘taxonomy singular name’ ),
    ‘search_items’ => __( ‘Search User Image Categories’ ),
    ‘all_items’ => __( ‘All User Image Categories’ ),
    ‘parent_item’ => __( ‘Parent User Image Category’ ),
    ‘parent_item_colon’ => __( ‘Parent User Image Category:’ ),
    ‘edit_item’ => __( ‘Edit User Image Category’ ),
    ‘update_item’ => __( ‘Update User Image Category’ ),
    ‘add_new_item’ => __( ‘Add New User Image Category’ ),
    ‘new_item_name’ => __( ‘New User Image Name’ ),
    ‘menu_name’ => __( ‘User Image Categories’ ),
  );
 
  $image_category_args = array(
    ‘hierarchical’ => true,
    ‘labels’ => $image_category_labels,
    ‘show_ui’ => true,
    ‘query_var’ => true,
    ‘rewrite’ => array( ‘slug’ => ‘user_image_category’ ),
  );
   
  register_taxonomy(‘sui_image_category’, array(‘user_images’), $image_category_args);
   
  $default_image_cats = array(‘humor’, ‘landscapes’, ‘sport’, ‘people’);
   
  foreach($default_image_cats as $cat){
   
    if(!term_exists($cat, ‘sui_image_category’)) wp_insert_term($cat, ‘sui_image_category’);
     
  }
     
}

Теперь у нас есть панель «Изображения пользователя» в нашей панели администратора и способ администрирования пользовательских изображений и их категорий.


Нам нужно выполнить некоторую базовую проверку, поэтому давайте определим две константы для последующего использования:

1
2
3
4
5
6
define(‘MAX_UPLOAD_SIZE’, 200000);
define(‘TYPE_WHITELIST’, serialize(array(
  ‘image/jpeg’,
  ‘image/png’,
  ‘image/gif’
  )));

Мы определим короткий код , который позволит нам отображать (и обрабатывать) форму отправки пользовательских изображений в сообщении или на странице:

1
add_shortcode(‘sui_form’, ‘sui_form_shortcode’);

Поскольку наш плагин принимает данные от пользователя, мы реализуем следующие механизмы безопасности:

  • только зарегистрированные пользователи имеют доступ к форме отправки изображений
  • мы используем одноразовые номера, чтобы убедиться, что формы были сгенерированы нашим плагином
  • Изображения передаются с помощью wp_insert_post, который очищает данные перед сохранением в базе данных.
  • пользователи могут просматривать только свои изображения, а одноразовые номера не позволяют им удалять сообщения других пользователей.

Это функция, вызываемая нашим шорткодом. Он отображает и обрабатывает форму отправки изображения и форму списка / удаления изображения. Мы разберем его по кусочкам и на шаге 6 рассмотрим вспомогательные функции.

1
2
3
4
5
6
7
8
9
function sui_form_shortcode(){
 
  if(!is_user_logged_in()){
   
    return ‘<p>You need to be logged in to submit an image.</p>’;
 
  }
 
  global $current_user;
  • проверьте, вошел ли пользователь в систему
  • захватите переменную WordPress $ current_user, которая нам понадобится для получения идентификатора пользователя
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
if(isset( $_POST[‘sui_upload_image_form_submitted’] ) && wp_verify_nonce($_POST[‘sui_upload_image_form_submitted’], ‘sui_upload_image_form’) ){
 
  $result = sui_parse_file_errors($_FILES[‘sui_image_file’], $_POST[‘sui_image_caption’]);
   
  if($result[‘error’]){
   
    echo ‘<p>ERROR: ‘ .
   
  }else{
 
    $user_image_data = array(
      ‘post_title’ => $result[‘caption’],
      ‘post_status’ => ‘pending’,
      ‘post_author’ => $current_user->ID,
      ‘post_type’ => ‘user_images’
    );
     
    if($post_id = wp_insert_post($user_image_data)){
     
      sui_process_image(‘sui_image_file’, $post_id, $result[‘caption’]);
     
      wp_set_object_terms($post_id, (int)$_POST[‘sui_image_category’], ‘sui_image_category’);
     
    }
  }
}
  • если форма изображения была отправлена, будет поле sui_upload_image_form_submitted, которое было сгенерировано нашей функцией wp_nonce_field. Затем мы можем проверить одноразовый номер и приступить к обработке отправленного изображения
  • выполнить некоторую проверку, передав входной файл (где хранятся загруженные данные изображения) и входные данные заголовка в функцию проверки, sui_parse_file_errors, и отобразите все возвращенные ошибки
  • создать массив, устанавливающий статус сообщения в ожидании (теперь администратор должен будет утвердить его для публикации), установить тип сообщения user_images (наш пользовательский тип сообщения) и установить автора сообщения с изображением для текущего вошедшего в систему пользователя
  • если сообщение с изображением было успешно вставлено, сохраните изображение в медиатеке WordPress (sui_process_image) и, наконец, установите категорию для сообщения с изображением и отобразите сообщение об успехе.
01
02
03
04
05
06
07
08
09
10
11
if (isset( $_POST[‘sui_form_delete_submitted’] ) && wp_verify_nonce($_POST[‘sui_form_delete_submitted’], ‘sui_form_delete’)){
 
  if(isset($_POST[‘sui_image_delete_id’])){
   
    if($user_images_deleted = sui_delete_user_images($_POST[‘sui_image_delete_id’])){
     
      echo ‘<p>’ .
       
    }
  }
}
  • если была отправлена ​​форма удаления изображения, будет поле sui_form_delete_submitted, сгенерированное нашей функцией wp_nonce_field. Затем мы можем проверить одноразовый номер и приступить к обработке массива изображений, проверенных на удаление.
  • мы проверяем, что на самом деле некоторые изображения проверяются на удаление, проверяя $ _POST [‘sui_image_delete_id’]. Если это так, мы передаем их функции sui_delete_user_images (см. Шаг 6)
  • если изображения были удалены, мы показываем сообщение об успехе
1
2
3
4
5
6
7
echo sui_get_upload_image_form($sui_image_caption = $_POST[‘sui_image_caption’], $sui_image_category = $_POST[‘sui_image_category’]);
 
if($user_images_table = sui_get_user_images_table($current_user->ID)){
 
  echo $user_images_table;
   
}
  • выводим форму загрузки изображения
  • наконец, мы выводим форму списка / удаления изображений, передавая идентификатор пользователя в функцию sui_get_user_images_table (см. Шаг 6)

Здесь мы рассмотрим функции, которые генерируют формы, добавят изображения в медиатеку и функцию, которая удаляет выбранные изображения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
function sui_get_upload_image_form($sui_image_caption = », $sui_image_category = 0){
 
  $out = »;
  $out .= ‘<form id=»sui_upload_image_form» method=»post» action=»» enctype=»multipart/form-data»>’;
 
  $out .= wp_nonce_field(‘sui_upload_image_form’, ‘sui_upload_image_form_submitted’);
   
  $out .= ‘<label for=»sui_image_caption»>Image Caption — Letters, Numbers and Spaces</label><br/>’;
  $out .= ‘<input type=»text» id=»sui_image_caption» name=»sui_image_caption» value=»‘ . $sui_image_caption . ‘»/><br/>’;
  $out .= ‘<label for=»sui_image_category»>Image Category</label><br/>’;
  $out .= sui_get_image_categories_dropdown(‘sui_image_category’, $sui_image_category) .
  $out .= ‘<label for=»sui_image_file»>Select Your Image — ‘ .
  $out .= ‘<input type=»file» size=»60″ name=»sui_image_file» id=»sui_image_file»><br/>’;
     
  $out .= ‘<input type=»submit» id=»sui_submit» name=»sui_submit» value=»Upload Image»>’;
 
  $out .= ‘</form>’;
 
  return $out;
   
}
  • функция принимает 2 необязательных аргумента для повторного заполнения полей формы. Это удобно для пользователя.
  • выводится одноразовое поле, которое мы проверяем при отправке формы
  • мы выводим раскрывающийся список для категорий изображений, вызывая sui_get_image_categories_dropdown (см. следующую функцию)
1
2
3
4
5
function sui_get_image_categories_dropdown($taxonomy, $selected){
 
  return wp_dropdown_categories(array(‘taxonomy’ => $taxonomy, ‘name’ => ‘sui_image_category’, ‘selected’ => $selected, ‘hide_empty’ => 0, ‘echo’ => 0));
 
}
  • функция принимает 2 аргумента, включая идентификатор элемента текущей выбранной категории
  • мы используем функцию WordPress wp_dropdown_categories для создания выпадающего списка, в котором перечислены категории изображений пользователей из таксономии user_image_category (наша пользовательская таксономия)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function sui_get_user_images_table($user_id){
 
  $args = array(
    ‘author’ => $user_id,
    ‘post_type’ => ‘user_images’,
    ‘post_status’ => ‘pending’
  );
   
  $user_images = new WP_Query($args);
 
  if(!$user_images->post_count) return 0;
   
  $out = »;
  $out .= ‘<p>Your unpublished images — Click to see full size</p>’;
   
  $out .= ‘<form method=»post» action=»»>’;
   
  $out .= wp_nonce_field(‘sui_form_delete’, ‘sui_form_delete_submitted’);
   
  $out .= ‘<table id=»user_images»>’;
  $out .= ‘<thead><th>Image</th><th>Caption</th><th>Category</th><th>Delete</th></thead>’;
     
  foreach($user_images->posts as $user_image){
   
    $user_image_cats = get_the_terms($user_image->ID, ‘sui_image_category’);
     
    foreach($user_image_cats as $cat){
     
      $user_image_cat = $cat->name;
     
    }
     
    $post_thumbnail_id = get_post_thumbnail_id($user_image->ID);
 
    $out .= wp_nonce_field(‘sui_image_delete_’ . $user_image->ID, ‘sui_image_delete_id_’ . $user_image->ID, false);
        
    $out .= ‘<tr>’;
    $out .= ‘<td>’ .
    $out .= ‘<td>’ .
    $out .= ‘<td>’ .
    $out .= ‘<td><input type=»checkbox» name=»sui_image_delete_id[]» value=»‘ . $user_image->ID . ‘» /></td>’;
    $out .= ‘</tr>’;
     
  }
 
  $out .= ‘</table>’;
     
  $out .= ‘<input type=»submit» name=»sui_delete» value=»Delete Selected Images» />’;
  $out .= ‘</form>’;
   
  return $out;
 
}
  • принять идентификатор пользователя, потому что нам нужно получить список изображений пользователей только для текущего пользователя
  • создайте $ args, чтобы указать нашего пользователя, тип сообщения user_images и изображения пользователя, ожидающие рассмотрения (еще не опубликованные администратором)
  • выполнить пользовательский запрос с помощью WP_Query
  • вернуть false, если наш запрос не возвращает пользовательских изображений
  • запустить форму и создать одноразовый номер для формы
  • перебирайте посты с изображениями, убедившись, что мы также выбираем категорию постов
  • сгенерировать одноразовый номер для флажка удаления изображения, назначив уникальное имя для одноразового номера путем объединения идентификатора публикации изображения пользователя
  • вывести строку таблицы, содержащую информацию о публикации изображения, а также флажок удаления

В браузере можно манипулировать формами, чтобы отправлять неожиданные данные. В нашем случае каждому флажку удаления присваивается значение сообщения. Но что, если злонамеренный пользователь изменил это значение и заставил нашу функцию удаления удалить сообщение, которого в действительности не было в списке?

Один из способов избежать этого — использовать одноразовые номера для каждой строки данных публикации, гарантируя, что одноразовые номера имеют уникальные имена с удаленным значением сообщения. Затем мы проверяем одноразовый номер при отправке формы, чтобы убедиться, что это подлинное возвращаемое значение.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function sui_delete_user_images($images_to_delete){
 
  $images_deleted = 0;
 
  foreach($images_to_delete as $user_image){
 
    if (isset($_POST[‘sui_image_delete_id_’ . $user_image]) && wp_verify_nonce($_POST[‘sui_image_delete_id_’ . $user_image], ‘sui_image_delete_’ . $user_image)){
     
      if($post_thumbnail_id = get_post_thumbnail_id($user_image)){
 
        wp_delete_attachment($post_thumbnail_id);
 
      }
 
      wp_trash_post($user_image);
       
      $images_deleted ++;
 
    }
  }
 
  return $images_deleted;
 
}
  • функция принимает массив идентификаторов записей изображений для удаления
  • каждый идентификатор сообщения изображения проверяется, чтобы увидеть, был ли создан одноразовый номер для него
  • если проверяется одноразовый номер, мы удаляем вложение изображения, которое существует в библиотеке мультимедиа, передавая идентификатор миниатюры публикации изображения в функцию WordPress wp_delete_attachment
  • мы также удаляем пост изображения, используя функцию WordPress wp_trash_post

Нет, и это потому, что WordPress хранит вложения как обычные сообщения в таблице базы данных сообщений. Посмотрите сами: все вложения хранятся в таблице постов с типом вложения post_type. Простое удаление сообщения типа user_images не удаляет его миниатюру вложения. Он остается в библиотеке мультимедиа для будущего использования, если мы специально не удалим его с помощью wp_delete_attachment. Для наших целей я подумал, что лучше всего удалить вложение, когда пост пользователя удален.


Давайте вспомним, как будет выглядеть вывод html-файла, когда он отправляет изображение в ваш скрипт:

1
2
3
4
5
6
7
8
Array
(
    [name] => ref_blind.jpg
    [type] => image/jpeg
    [tmp_name] => /tmp/php79xI4e
    [error] => 0
    [size] => 106290
)

Мы передаем этот массив в функцию sui_process_image вместе с идентификатором сохраненного поста с пользовательским изображением и заголовком сантифицированного изображения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
function sui_process_image($file, $post_id, $caption){
  
  require_once(ABSPATH . «wp-admin» . ‘/includes/image.php’);
  require_once(ABSPATH . «wp-admin» . ‘/includes/file.php’);
  require_once(ABSPATH . «wp-admin» . ‘/includes/media.php’);
  
  $attachment_id = media_handle_upload($file, $post_id);
  
  update_post_meta($post_id, ‘_thumbnail_id’, $attachment_id);
 
  $attachment_data = array(
    ‘ID’ => $attachment_id,
    ‘post_excerpt’ => $caption
  );
   
  wp_update_post($attachment_data);
 
  return $attachment_id;
 
}
  • нам нужно включить сценарии администратора WordPress, которые обрабатывают загрузку изображений за кулисы
  • мы вызываем функцию media_handle_upload (которая является частью media.php), передавая ей загруженный массив файлов и идентификатор записи
  • теперь у нас есть идентификатор вложения, который мы можем использовать с update_post_meta, чтобы назначить вложение в качестве его миниатюры. Примечание: «_thumbnail_id» относится к внутреннему мета-полю миниатюр (Featured Image). Внутренние поля WordPress начинаются с подчеркивания.
  • затем мы используем идентификатор вложения, чтобы обновить заголовок вложения с помощью функции wp_update_post

Поскольку вложения — это просто обычные записи, если мы обновляем поле post_excerpt для вложения, мы фактически обновляем поле заголовка вложения, как показано на экране редактирования библиотеки мультимедиа.

Мы также проверяем массив файлов и предоставленный пользователем заголовок изображения с помощью функции sui_parse_file_errors.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function sui_parse_file_errors($file = », $image_caption){
 
  $result = array();
  $result[‘error’] = 0;
   
  if($file[‘error’]){
   
    $result[‘error’] = «No file uploaded or there was an upload error!»;
     
    return $result;
   
  }
 
  $image_caption = trim(preg_replace(‘/[^a-zA-Z0-9\s]+/’, ‘ ‘, $image_caption));
   
  if($image_caption == »){
 
    $result[‘error’] = «Your caption may only contain letters, numbers and spaces!»;
     
    return $result;
   
  }
   
  $result[‘caption’] = $image_caption;
 
  $image_data = getimagesize($file[‘tmp_name’]);
   
  if(!in_array($image_data[‘mime’], unserialize(TYPE_WHITELIST))){
   
    $result[‘error’] = ‘Your image must be a jpeg, png or gif!’;
     
  }elseif(($file[‘size’] > MAX_UPLOAD_SIZE)){
   
    $result[‘error’] = ‘Your image was ‘ .
     
  }
     
  return $result;
 
}
  • проверить элемент error массива files на наличие ошибки загрузки html, если найден, вернуть массив результатов с ошибкой
  • выполните некоторое регулярное выражение для заголовка изображения, чтобы удалить все, кроме буквенно-цифровых данных и пробелов, заменив их пробелами для удобства чтения
  • если после очистки мы получим пустой заголовок, мы вернем ошибку
  • проверьте внутренний тип изображения (не доверяйте расширению файла) с помощью функции PHP getimagesize с константой TYPE_WHITELIST
  • сравните размер изображения с константой MAX_UPLOAD_SIZE

Просто поместите эту информацию о стиле в файл style.css в папке вашей темы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
#sui_upload_image_form #sui_image_caption{
  width:500px;
}
#user_images{
  font-size:12px;
}
#user_images th{
  text-align:left;
}
#user_images td{
  vertical-align:middle;
}
#user_images td input{
  margin:0px;
}

Активируйте плагин, вставьте шорткод на страницу, войдите на свой сайт и протестируйте его. Когда вы загрузите изображение, вы увидите новое сообщение в меню администратора User Images. Это будет в ожидании публикации. Вы также увидите новое изображение в вашей медиатеке, прикрепленное к новому сообщению с подписью, как указано.

Полный исходный код плагина и ссылка на демонстрационный сайт перечислены в верхней части этого руководства.

Исходная папка также содержит шаблон страницы WordPress с пользовательским циклом, который отображает опубликованные изображения для всех пользователей.


Возможно, вы захотите разместить более строгие проверки на загрузку изображений. Помните, что вы принимаете данные от пользователей, которые могут случайно или злонамеренно загрузить неподходящие файлы. Проверка типа и размера файла — хорошее начало.

Кроме того, мы создали заголовок вложения, обновив поле post_excerpt вложения. Вы также можете установить описание вложения, используя поле post_content вложения.