Crop-Thumbnails - Version 1.2.0

Version Description

  • the used cropping data are now stored in the image after the crop, making it possible to code a plugin for restoring the cropped region on new image-sizes
  • fix for hiding crop sizes is not working when the image_size_names_choose-filter is used for that post-type
  • change permission from 'upload_files' to 'edit_files' (Attention: authors will no longer able to crop the thumbnails)
  • add a filter function to override the permission to crop thumbnails (crop_thumbnails_user_permission_check)
  • add settings-section to set if users can crop thumbnails with capability "edit_files" or "upload_files"
  • get featured image panel button working in wordpress v5
Download this release

Release Info

Developer Volkmar Kantor
Plugin Icon Crop-Thumbnails
Version 1.2.0
Comparing to
See all releases

Code changes from version 1.1.3 to 1.2.0

app/app.css CHANGED
@@ -82,7 +82,7 @@
82
  padding-bottom: 0;
83
  }
84
  /**** button icon ****/
85
- a.cropThumbnailsLink .wp-media-buttons-icon::before {
86
  content: "\F165";
87
  font: normal 12px/1 dashicons;
88
  -webkit-font-smoothing: antialiased;
82
  padding-bottom: 0;
83
  }
84
  /**** button icon ****/
85
+ .cropThumbnailsLink .wp-media-buttons-icon::before {
86
  content: "\F165";
87
  font: normal 12px/1 dashicons;
88
  -webkit-font-smoothing: antialiased;
app/app.js CHANGED
@@ -564,6 +564,14 @@ CROP_THUMBNAILS_VUE.components.cropeditor = {
564
  that.lang = that.cropData.lang;
565
  that.nonce = that.cropData.nonce;
566
  delete that.cropData.nonce;
 
 
 
 
 
 
 
 
567
  }).always(function () {
568
  that.loading = false;
569
  });
@@ -789,7 +797,7 @@ CROP_THUMBNAILS_VUE.components.cropeditor = {
789
  /* 16 */
790
  /***/ (function(module, exports) {
791
 
792
- module.exports = "<div class=\"cptEditorInner\" v-if=\"cropData && lang\" :class=\"{loading:loading,cropEditorActive:croppingApi}\">\n\t\n\t<div class=\"cptWaitingWindow\" v-if=\"loading\">\n\t\t<div class=\"msg\">\n\t\t\t{{ lang.waiting }}\n\t\t\t<div>\n\t\t\t\t<div class=\"cptLoadingSpinner\"></div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\t\n\t<div class=\"cptWaitingWindow cptCropDisabledMsg\" v-if=\"cropData.hiddenOnPostType\">\n\t\t<div class=\"msg\">{{lang.cropDisabled}}</div>\n\t</div>\n\t\n\t\n\t<div class=\"mainWindow\" v-if=\"!cropData.hiddenOnPostType\">\n\t\t\n\t\t<div class=\"cptSelectionPane\" :class=\"{ cptImagesAreSelected : (activeImageSizes.length>0) }\">\n\t\t\t<div class=\"cptSelectionPaneInner\">\n\t\t\t\t<message v-if=\"sourceImageHasOrientation\">{{lang.message_image_orientation}}</message>\n\t\t\t\t<p>\n\t\t\t\t\t<label class=\"cptSameRatioLabel\">\n\t\t\t\t\t\t<input type=\"checkbox\" v-model=\"selectSameRatio\" />\n\t\t\t\t\t\t{{lang.label_same_ratio}}\n\t\t\t\t\t</label>\n\t\t\t\t\t<button type=\"button\" class=\"button\" @click=\"makeAllInactive()\">{{lang.label_deselect_all}}</button>\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cptImageSizelist\">\n\t\t\t\t\t<li v-for=\"i in filteredImageSizes\" :class=\"imageSizeClass(i)\" @click=\"toggleActive(i)\">\n\t\t\t\t\t\t<section class=\"cptImageSizeInner\">\n\t\t\t\t\t\t\t<header>{{i.nameLabel}}</header>\n\t\t\t\t\t\t\t<div class=\"lowResWarning\" v-if=\"isLowRes(i)\" :title=\"lang.lowResWarning\"><span>!</span></div>\n\t\t\t\t\t\t\t<div class=\"notYetCropped\" v-if=\"!isLowRes(i) && i.url === cropData.sourceImage.full.url\" :title=\"lang.notYetCropped\"><span class=\"dashicons dashicons-image-crop\"></span></div>\n\t\t\t\t\t\t\t<div class=\"dimensions\">{{ lang.dimensions }} {{i.width}} x {{i.height}} {{ lang.pixel }}</div>\n\t\t\t\t\t\t\t<div class=\"ratio\">{{ lang.ratio }} {{i.printRatio}}</div>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t<loadingcontainer :image=\"i.url+'?cacheBreak='+i.cacheBreak\">\n\t\t\t\t\t\t\t\t<div class=\"cptImageBgContainer\" :style=\"{'background-image': 'url('+i.url+'?cacheBreak='+i.cacheBreak+')'}\"></div>\n\t\t\t\t\t\t\t</loadingcontainer>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"cptCropPane\">\n\t\t\t<div class=\"info\">\n\t\t\t\t<h3>{{ lang.rawImage }}</h3>\n\t\t\t\t<div class=\"dimensions\">{{ lang.dimensions }} {{cropData.sourceImage.full.width}} x {{cropData.sourceImage.full.height}} {{ lang.pixel }}</div>\n\t\t\t\t<div class=\"ratio\">{{ lang.ratio }} {{cropData.sourceImage.full.printRatio}}</div>\n\t\t\t</div>\n\t\t\t<button type=\"button\" class=\"button cptGenerate\" :class=\"{'button-primary':croppingApi}\" @click=\"cropThumbnails()\" :disabled=\"!croppingApi\">{{ lang.label_crop }}</button>\n\t\t\t\n\t\t\t<div class=\"cropContainer\">\n\t\t\t\t<img class=\"cptCroppingImage\" :src=\"cropImage.url\" />\n\t\t\t</div>\n\t\n\t\t\t<h4>{{ lang.instructions_header }}</h4>\n\t\t\t<ul class=\"step-info\">\n\t\t\t\t<li>{{ lang.instructions_step_1 }}</li>\n\t\t\t\t<li>{{ lang.instructions_step_2 }}</li>\n\t\t\t\t<li>{{ lang.instructions_step_3 }}</li>\n\t\t\t</ul>\n\n\t\t\t<div>\n\t\t\t\t<button type=\"button\" class=\"button\" v-if=\"cropData.options.debug_js\" @click=\"showDebugClick('js')\">show JS-Debug</button>\n\t\t\t\t<button type=\"button\" class=\"button\" v-if=\"cropData.options.debug_data && dataDebug!==null\" @click=\"showDebugClick('data')\">show Data-Debug</button>\n\t\t\t\t<pre v-if=\"showDebugType==='data'\">{{ dataDebug }}</pre>\n\t\t\t\t<pre v-if=\"showDebugType==='js'\"><br />cropImage:{{cropImage}}<br />cropData:{{ cropData }}</pre>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
793
 
794
  /***/ }),
795
  /* 17 */
564
  that.lang = that.cropData.lang;
565
  that.nonce = that.cropData.nonce;
566
  delete that.cropData.nonce;
567
+ }).fail(function (data) {
568
+ that.cropData = data.responseJSON;
569
+ that.lang = that.cropData.lang;
570
+ that.nonce = that.cropData.nonce;
571
+ delete that.cropData.nonce;
572
+ if (data.status === 403) {
573
+ that.cropData.noPermission = true;
574
+ }
575
  }).always(function () {
576
  that.loading = false;
577
  });
797
  /* 16 */
798
  /***/ (function(module, exports) {
799
 
800
+ module.exports = "<div class=\"cptEditorInner\" v-if=\"cropData && lang\" :class=\"{loading:loading,cropEditorActive:croppingApi}\">\n\t\n\t<div class=\"cptWaitingWindow\" v-if=\"loading\">\n\t\t<div class=\"msg\">\n\t\t\t{{ lang.waiting }}\n\t\t\t<div>\n\t\t\t\t<div class=\"cptLoadingSpinner\"></div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n\t\n\t<div class=\"cptWaitingWindow cptCropDisabledMsg\" v-if=\"cropData.hiddenOnPostType\">\n\t\t<div class=\"msg\">{{lang.cropDisabled}}</div>\n\t</div>\n\t\n\t<div class=\"cptWaitingWindow cptNoPermissionMsg\" v-if=\"cropData.noPermission\">\n\t\t<div class=\"msg\">{{lang.noPermission}}</div>\n\t</div>\n\n\t<div class=\"mainWindow\" v-if=\"!cropData.hiddenOnPostType && !cropData.noPermission\">\n\t\t\n\t\t<div class=\"cptSelectionPane\" :class=\"{ cptImagesAreSelected : (activeImageSizes.length>0) }\">\n\t\t\t<div class=\"cptSelectionPaneInner\">\n\t\t\t\t<message v-if=\"sourceImageHasOrientation\">{{lang.message_image_orientation}}</message>\n\t\t\t\t<p>\n\t\t\t\t\t<label class=\"cptSameRatioLabel\">\n\t\t\t\t\t\t<input type=\"checkbox\" v-model=\"selectSameRatio\" />\n\t\t\t\t\t\t{{lang.label_same_ratio}}\n\t\t\t\t\t</label>\n\t\t\t\t\t<button type=\"button\" class=\"button\" @click=\"makeAllInactive()\">{{lang.label_deselect_all}}</button>\n\t\t\t\t</p>\n\t\t\t\t<ul class=\"cptImageSizelist\">\n\t\t\t\t\t<li v-for=\"i in filteredImageSizes\" :class=\"imageSizeClass(i)\" @click=\"toggleActive(i)\">\n\t\t\t\t\t\t<section class=\"cptImageSizeInner\">\n\t\t\t\t\t\t\t<header>{{i.nameLabel}}</header>\n\t\t\t\t\t\t\t<div class=\"lowResWarning\" v-if=\"isLowRes(i)\" :title=\"lang.lowResWarning\"><span>!</span></div>\n\t\t\t\t\t\t\t<div class=\"notYetCropped\" v-if=\"!isLowRes(i) && i.url === cropData.sourceImage.full.url\" :title=\"lang.notYetCropped\"><span class=\"dashicons dashicons-image-crop\"></span></div>\n\t\t\t\t\t\t\t<div class=\"dimensions\">{{ lang.dimensions }} {{i.width}} x {{i.height}} {{ lang.pixel }}</div>\n\t\t\t\t\t\t\t<div class=\"ratio\">{{ lang.ratio }} {{i.printRatio}}</div>\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t<loadingcontainer :image=\"i.url+'?cacheBreak='+i.cacheBreak\">\n\t\t\t\t\t\t\t\t<div class=\"cptImageBgContainer\" :style=\"{'background-image': 'url('+i.url+'?cacheBreak='+i.cacheBreak+')'}\"></div>\n\t\t\t\t\t\t\t</loadingcontainer>\n\t\t\t\t\t\t</section>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t</div>\n\t\t</div>\n\t\t<div class=\"cptCropPane\">\n\t\t\t<div class=\"info\">\n\t\t\t\t<h3>{{ lang.rawImage }}</h3>\n\t\t\t\t<div class=\"dimensions\">{{ lang.dimensions }} {{cropData.sourceImage.full.width}} x {{cropData.sourceImage.full.height}} {{ lang.pixel }}</div>\n\t\t\t\t<div class=\"ratio\">{{ lang.ratio }} {{cropData.sourceImage.full.printRatio}}</div>\n\t\t\t</div>\n\t\t\t<button type=\"button\" class=\"button cptGenerate\" :class=\"{'button-primary':croppingApi}\" @click=\"cropThumbnails()\" :disabled=\"!croppingApi\">{{ lang.label_crop }}</button>\n\t\t\t\n\t\t\t<div class=\"cropContainer\">\n\t\t\t\t<img class=\"cptCroppingImage\" :src=\"cropImage.url\" />\n\t\t\t</div>\n\t\n\t\t\t<h4>{{ lang.instructions_header }}</h4>\n\t\t\t<ul class=\"step-info\">\n\t\t\t\t<li>{{ lang.instructions_step_1 }}</li>\n\t\t\t\t<li>{{ lang.instructions_step_2 }}</li>\n\t\t\t\t<li>{{ lang.instructions_step_3 }}</li>\n\t\t\t</ul>\n\n\t\t\t<div>\n\t\t\t\t<button type=\"button\" class=\"button\" v-if=\"cropData.options.debug_js\" @click=\"showDebugClick('js')\">show JS-Debug</button>\n\t\t\t\t<button type=\"button\" class=\"button\" v-if=\"cropData.options.debug_data && dataDebug!==null\" @click=\"showDebugClick('data')\">show Data-Debug</button>\n\t\t\t\t<pre v-if=\"showDebugType==='data'\">{{ dataDebug }}</pre>\n\t\t\t\t<pre v-if=\"showDebugType==='js'\"><br />cropImage:{{cropImage}}<br />cropData:{{ cropData }}</pre>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n"
801
 
802
  /***/ }),
803
  /* 17 */
crop-thumbnails.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin URI: https://wordpress.org/extend/plugins/crop-thumbnails/
5
  * Author: Volkmar Kantor
6
  * Author URI: https://www.totalmedial.de
7
- * Version: 1.1.3
8
  * Description: The easy way to adjust your cropped image sizes.
9
  *
10
  *
@@ -26,7 +26,7 @@
26
  */
27
 
28
 
29
- define('CROP_THUMBNAILS_VERSION','1.1.3');
30
 
31
 
32
  function cptLoadLanguage() {
4
  * Plugin URI: https://wordpress.org/extend/plugins/crop-thumbnails/
5
  * Author: Volkmar Kantor
6
  * Author URI: https://www.totalmedial.de
7
+ * Version: 1.2.0
8
  * Description: The easy way to adjust your cropped image sizes.
9
  *
10
  *
26
  */
27
 
28
 
29
+ define('CROP_THUMBNAILS_VERSION','1.2.0');
30
 
31
 
32
  function cptLoadLanguage() {
functions/backendpreparer.php CHANGED
@@ -5,7 +5,7 @@
5
  */
6
  class CropPostThumbnailsBackendPreparer {
7
 
8
- private $allowedMime = array('image/jpeg','image/png');
9
 
10
  public function __construct() {
11
  if ( is_admin() ) {
@@ -29,7 +29,7 @@ class CropPostThumbnailsBackendPreparer {
29
  * });
30
  * </code>
31
  */
32
- private function shouldCropThumbnailsBeActive() {
33
  global $pagenow;
34
  $result = ($pagenow == 'post.php'
35
  || $pagenow == 'post-new.php'
@@ -92,7 +92,7 @@ class CropPostThumbnailsBackendPreparer {
92
  /**
93
  * Check if on the current admin page (posttype) the crop-button should be visible in the featured image Box.
94
  */
95
- private static function showCropButtonOnThisAdminPage() {
96
  $screenData = get_current_screen();
97
  $settings = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
98
  $showFeaturedImageCropButton = false;
@@ -120,7 +120,7 @@ jQuery(document).ready(function($) {
120
  CROP_THUMBNAILS_CURRENT_POST_ID = null;
121
 
122
  /**
123
- * Adds a button to the featured image metabox.
124
  * The button will be visible only if a featured image is set.
125
  */
126
  function handleFeaturedImageBox() {
@@ -128,9 +128,7 @@ jQuery(document).ready(function($) {
128
  * add link to featured image box
129
  */
130
  var baseElem = $('#postimagediv');
131
- if(!baseElem.length) {
132
- return;
133
- }
134
 
135
  var featuredImageLinkButton = '';
136
  featuredImageLinkButton+= '<p class="cropFeaturedImageWrap hidden">';
@@ -165,6 +163,52 @@ jQuery(document).ready(function($) {
165
 
166
  updateCropFeaturedImageButton( parseInt(wp.media.featuredImage.get()) );
167
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
  <?php if(self::showCropButtonOnThisAdminPage()) : ?>
170
  /** add link on posts and pages **/
@@ -176,6 +220,7 @@ jQuery(document).ready(function($) {
176
  CROP_THUMBNAILS_CURRENT_POST_ID = post_id_hidden;
177
 
178
  handleFeaturedImageBox();
 
179
 
180
  $('body').on('cropThumbnailModalClosed',function() {
181
  //lets cache-break the crop-thumbnail-preview-box
5
  */
6
  class CropPostThumbnailsBackendPreparer {
7
 
8
+ protected $allowedMime = array('image/jpeg','image/png');
9
 
10
  public function __construct() {
11
  if ( is_admin() ) {
29
  * });
30
  * </code>
31
  */
32
+ protected function shouldCropThumbnailsBeActive() {
33
  global $pagenow;
34
  $result = ($pagenow == 'post.php'
35
  || $pagenow == 'post-new.php'
92
  /**
93
  * Check if on the current admin page (posttype) the crop-button should be visible in the featured image Box.
94
  */
95
+ protected static function showCropButtonOnThisAdminPage() {
96
  $screenData = get_current_screen();
97
  $settings = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
98
  $showFeaturedImageCropButton = false;
120
  CROP_THUMBNAILS_CURRENT_POST_ID = null;
121
 
122
  /**
123
+ * Adds a button to the featured image metabox (Wordpress < 5).
124
  * The button will be visible only if a featured image is set.
125
  */
126
  function handleFeaturedImageBox() {
128
  * add link to featured image box
129
  */
130
  var baseElem = $('#postimagediv');
131
+ if(!baseElem.length) { return; }//this is not wordpress < 5
 
 
132
 
133
  var featuredImageLinkButton = '';
134
  featuredImageLinkButton+= '<p class="cropFeaturedImageWrap hidden">';
163
 
164
  updateCropFeaturedImageButton( parseInt(wp.media.featuredImage.get()) );
165
  }
166
+
167
+ /**
168
+ * Adds a button to the featured image panel (Wordpress >= 5)
169
+ *
170
+ * I know this way to add the button is quite dirty - will refactoring it when i learned the api-way to do it.
171
+ */
172
+ function handleFeaturedImagePanel() {
173
+ // @see https://github.com/WordPress/gutenberg/tree/master/packages/editor/src/components/post-featured-image
174
+
175
+ if(typeof wp.element === 'undefined') { return };//this is not wordpress 5.x
176
+
177
+ var el = wp.element.createElement;
178
+ function wrapPostFeaturedImage( OriginalComponent ) {
179
+ return function( props ) {
180
+ setTimeout(function() {
181
+ var baseElem = $('.edit-post-sidebar');
182
+ var cropButton = $('<button class="button cropThumbnailsLink" style="margin-top:1em" data-cropthumbnail=\'{"image_id":'+ parseInt(props.featuredImageId) +',"viewmode":"single","posttype":"<?php echo get_post_type(); ?>"}\' title="<?php esc_attr_e('Crop Featured Image','crop-thumbnails') ?>"><span class="wp-media-buttons-icon"></span> <?php esc_html_e('Crop Featured Image','crop-thumbnails'); ?></button>');
183
+ if(typeof props.media !== 'undefined') {
184
+ var panel = baseElem.find('.editor-post-featured-image');
185
+ panel.find('.cropThumbnailsLink').remove();
186
+ if(panel.find('.components-responsive-wrapper').length>0) {
187
+ panel.append(cropButton);
188
+ }
189
+ }
190
+ }, 50);
191
+
192
+ return (
193
+ el(
194
+ wp.element.Fragment,
195
+ {},
196
+ el(
197
+ OriginalComponent,
198
+ props
199
+ ),
200
+ //TODO add in a better way here
201
+ )
202
+ );
203
+ }
204
+ }
205
+
206
+ wp.hooks.addFilter(
207
+ 'editor.PostFeaturedImage',
208
+ 'crop-thumbnails/wrap-post-featured-image',
209
+ wrapPostFeaturedImage
210
+ );
211
+ }
212
 
213
  <?php if(self::showCropButtonOnThisAdminPage()) : ?>
214
  /** add link on posts and pages **/
220
  CROP_THUMBNAILS_CURRENT_POST_ID = post_id_hidden;
221
 
222
  handleFeaturedImageBox();
223
+ handleFeaturedImagePanel();
224
 
225
  $('body').on('cropThumbnailModalClosed',function() {
226
  //lets cache-break the crop-thumbnail-preview-box
functions/editor.php CHANGED
@@ -8,39 +8,82 @@ class CPT_ForbiddenException extends Exception {}
8
 
9
  class CropPostThumbnailsEditor {
10
 
11
- private $debugOutput = '';
12
 
13
  function __construct() {
14
  add_action('wp_ajax_cpt_cropdata', array($this, 'provideCropData') );
15
  }
16
 
17
  public function provideCropData() {
 
18
  try {
19
- header('Content-Type: application/json; charset=UTF-8');
20
- $result = $this->getCropData();
21
- echo json_encode($result);
22
  } catch(InvalidArgumentException $e) {
23
- http_response_code(400);
24
- echo 'FAILURE while processing request: '.$e->getMessage();
25
  } catch(CPT_ForbiddenException $e) {
26
- http_response_code(403);
27
- echo 'ERROR not allowed.';
28
  } catch(Exception $e) {
29
- http_response_code(400);
30
- echo 'FAILURE while processing request.';
31
  }
32
  die();//to prevent to send back a "0"
33
  }
34
 
35
- private function fixJsLangStrings($msg) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  return str_replace('&quot;','"',esc_js($msg));
37
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  public function getCropData() {
40
- if(!self::isUserPermitted()) {
41
- throw new CPT_ForbiddenException();
42
- }
43
-
44
  global $content_width;//include nasty content_width
45
  $content_width = 9999;//override the idioty
46
 
@@ -56,27 +99,7 @@ class CropPostThumbnailsEditor {
56
  'sourceImageMeta' => null,
57
  'postTypeFilter' => null,
58
  'imageSizes' => array_values($GLOBALS['CROP_THUMBNAILS_HELPER']->getImageSizes()),
59
- 'lang' => array(
60
- 'warningOriginalToSmall' => self::fixJsLangStrings(__('Warning: the original image is too small to be cropped in good quality with this thumbnail size.','crop-thumbnails')),
61
- 'cropDisabled' => self::fixJsLangStrings(__('Cropping is disabled for this post-type.','crop-thumbnails')),
62
- 'waiting' => self::fixJsLangStrings(__('Please wait until the images are cropped.','crop-thumbnails')),
63
- 'rawImage' => self::fixJsLangStrings(__('Raw','crop-thumbnails')),
64
- 'pixel' => self::fixJsLangStrings(__('pixel','crop-thumbnails')),
65
- 'instructions_header' => self::fixJsLangStrings(__('Quick Instructions','crop-thumbnails')),
66
- 'instructions_step_1' => self::fixJsLangStrings(__('Step 1: Choose an image-size from the list.','crop-thumbnails')),
67
- 'instructions_step_2' => self::fixJsLangStrings(__('Step 2: Change the selection of the image above.','crop-thumbnails')),
68
- 'instructions_step_3' => self::fixJsLangStrings(__('Step 3: Click on "Save Crop".','crop-thumbnails')),
69
- 'label_crop' => self::fixJsLangStrings(__('Save Crop','crop-thumbnails')),
70
- 'label_same_ratio' => self::fixJsLangStrings(__('Crop all images with same ratio at once','crop-thumbnails')),
71
- 'label_deselect_all' => self::fixJsLangStrings(__('deselect all','crop-thumbnails')),
72
- 'dimensions' => self::fixJsLangStrings(__('Dimensions:','crop-thumbnails')),
73
- 'ratio' => self::fixJsLangStrings(__('Ratio:','crop-thumbnails')),
74
- 'cropped' => self::fixJsLangStrings(__('cropped','crop-thumbnails')),
75
- 'lowResWarning' => self::fixJsLangStrings(__('Original image size too small for good crop quality!','crop-thumbnails')),
76
- 'notYetCropped' => self::fixJsLangStrings(__('Not yet cropped by wordpress.','crop-thumbnails')),
77
- 'message_image_orientation' => self::fixJsLangStrings(__('This image has an image orientation value in its exif-metadata. Be aware that this may result in rotatated or mirrored images on safari ipad / iphone.','crop-thumbnails')),
78
- 'script_connection_error' => self::fixJsLangStrings(__('The plugin can not correctly connect to the server.','crop-thumbnails'))
79
- ),
80
  'nonce' => wp_create_nonce($GLOBALS['CROP_THUMBNAILS_HELPER']->getNonceBase())
81
  );
82
 
@@ -84,6 +107,7 @@ class CropPostThumbnailsEditor {
84
  if(empty($_REQUEST['imageId'])) {
85
  throw new InvalidArgumentException('Missing Parameter "imageId".');
86
  }
 
87
 
88
  $imagePostObj = get_post(intval($_REQUEST['imageId']));
89
  if(empty($imagePostObj) || $imagePostObj->post_type!=='attachment') {
@@ -91,6 +115,10 @@ class CropPostThumbnailsEditor {
91
  }
92
  $result['sourceImageId'] = $imagePostObj->ID;
93
 
 
 
 
 
94
  if(!empty($_REQUEST['posttype']) && post_type_exists($_REQUEST['posttype'])) {
95
  $result['postTypeFilter'] = $_REQUEST['posttype'];
96
  }
@@ -162,7 +190,7 @@ class CropPostThumbnailsEditor {
162
  return $result;
163
  }
164
 
165
- private function getUncroppedImageData($ID, $imageSize = 'full') {
166
  $orig_img = wp_get_attachment_image_src($ID, $imageSize);
167
  $orig_ima_gcd = $this->gcd($orig_img[1], $orig_img[2]);
168
  $result = array(
@@ -177,7 +205,7 @@ class CropPostThumbnailsEditor {
177
  return $result;
178
  }
179
 
180
- private function calculateRatioData($width,$height) {
181
  $gcd = $this->gcd($width,$height);
182
  $result = array(
183
  'gcd' => $gcd,
@@ -187,7 +215,7 @@ class CropPostThumbnailsEditor {
187
  return $result;
188
  }
189
 
190
- private static function shouldBeHiddenOnPostType($options,$post_type) {
191
  if(empty($post_type)) {
192
  return false;
193
  }
@@ -204,13 +232,13 @@ class CropPostThumbnailsEditor {
204
  * @param string name post-type (i.e. post, page, ...)
205
  * @return boolean true if Image-size should be hidden
206
  */
207
- private static function shouldSizeBeHidden($options, $img_size, $post_type='') {
208
  $_return = false;
209
  if(!empty($post_type)) {
210
  //we are NOT in the mediathek
211
 
212
  //-if hide_size
213
- if(!empty($options['hide_size'][$post_type][ $img_size['name'] ])) {
214
  $_return = true;
215
  }
216
 
@@ -230,20 +258,10 @@ class CropPostThumbnailsEditor {
230
  }
231
 
232
 
233
- private static function isUserPermitted() {
234
- $return = false;
235
- if(current_user_can('upload_files')) {
236
- $return = true;
237
- }
238
- //TODO maybe add noence (is it needed? there are no file- or db-operations)
239
- return $return;
240
- }
241
-
242
-
243
  /**
244
  * Greatest cummon divisor
245
  */
246
- private function gcd($a, $b) {
247
  if(function_exists('gmp_gcd')) {
248
  $gcd = gmp_strval(gmp_gcd($a,$b));
249
  return ($gcd);
@@ -253,7 +271,7 @@ class CropPostThumbnailsEditor {
253
  }
254
  }
255
 
256
- private static function my_gcd($a, $b) {
257
  $b = ( $a == 0 )? 0 : $b;
258
  return ( $a % $b )? self::my_gcd($b, abs($a - $b)) : $b;
259
  }
8
 
9
  class CropPostThumbnailsEditor {
10
 
11
+ protected $debugOutput = '';
12
 
13
  function __construct() {
14
  add_action('wp_ajax_cpt_cropdata', array($this, 'provideCropData') );
15
  }
16
 
17
  public function provideCropData() {
18
+ header('Content-Type: application/json; charset=UTF-8');
19
  try {
20
+ echo json_encode( $this->getCropData() );
 
 
21
  } catch(InvalidArgumentException $e) {
22
+ self::doErrorResponse(400, 'FAILURE while processing request: '.$e->getMessage());
 
23
  } catch(CPT_ForbiddenException $e) {
24
+ self::doErrorResponse(403, 'ERROR not allowed.');
 
25
  } catch(Exception $e) {
26
+ self::doErrorResponse(400, 'FAILURE while processing request.');
 
27
  }
28
  die();//to prevent to send back a "0"
29
  }
30
 
31
+
32
+ /**
33
+ * Will directly print an error-output
34
+ * @param int $statusCode the statuscode of the response
35
+ * @param string $errorMsg the provided errormessage
36
+ */
37
+ protected static function doErrorResponse($statusCode, $errorMsg) {
38
+ http_response_code($statusCode);
39
+ echo json_encode(array(
40
+ 'lang' => self::getLangArray(),
41
+ 'nonce' => wp_create_nonce($GLOBALS['CROP_THUMBNAILS_HELPER']->getNonceBase()),
42
+ 'error' => $errorMsg,
43
+ 'statusCode' => $statusCode
44
+ ));
45
+ }
46
+
47
+ /**
48
+ * Fix html-encoded language strings
49
+ * Note: abstraction to keep the code DRY
50
+ * @param string $msg the language string to fix
51
+ * @return string
52
+ */
53
+ protected function fixJsLangStrings($msg) {
54
  return str_replace('&quot;','"',esc_js($msg));
55
  }
56
+
57
+ /**
58
+ * Returns the lang-array needed for the js-app to work
59
+ * @return Array
60
+ */
61
+ protected static function getLangArray() {
62
+ return array(
63
+ 'warningOriginalToSmall' => self::fixJsLangStrings(__('Warning: the original image is too small to be cropped in good quality with this thumbnail size.','crop-thumbnails')),
64
+ 'cropDisabled' => self::fixJsLangStrings(__('Cropping is disabled for this post-type.','crop-thumbnails')),
65
+ 'waiting' => self::fixJsLangStrings(__('Please wait until the images are cropped.','crop-thumbnails')),
66
+ 'rawImage' => self::fixJsLangStrings(__('Raw','crop-thumbnails')),
67
+ 'pixel' => self::fixJsLangStrings(__('pixel','crop-thumbnails')),
68
+ 'instructions_header' => self::fixJsLangStrings(__('Quick Instructions','crop-thumbnails')),
69
+ 'instructions_step_1' => self::fixJsLangStrings(__('Step 1: Choose an image-size from the list.','crop-thumbnails')),
70
+ 'instructions_step_2' => self::fixJsLangStrings(__('Step 2: Change the selection of the image above.','crop-thumbnails')),
71
+ 'instructions_step_3' => self::fixJsLangStrings(__('Step 3: Click on "Save Crop".','crop-thumbnails')),
72
+ 'label_crop' => self::fixJsLangStrings(__('Save Crop','crop-thumbnails')),
73
+ 'label_same_ratio' => self::fixJsLangStrings(__('Crop all images with same ratio at once','crop-thumbnails')),
74
+ 'label_deselect_all' => self::fixJsLangStrings(__('deselect all','crop-thumbnails')),
75
+ 'dimensions' => self::fixJsLangStrings(__('Dimensions:','crop-thumbnails')),
76
+ 'ratio' => self::fixJsLangStrings(__('Ratio:','crop-thumbnails')),
77
+ 'cropped' => self::fixJsLangStrings(__('cropped','crop-thumbnails')),
78
+ 'lowResWarning' => self::fixJsLangStrings(__('Original image size too small for good crop quality!','crop-thumbnails')),
79
+ 'notYetCropped' => self::fixJsLangStrings(__('Not yet cropped by wordpress.','crop-thumbnails')),
80
+ 'message_image_orientation' => self::fixJsLangStrings(__('This image has an image orientation value in its exif-metadata. Be aware that this may result in rotatated or mirrored images on safari ipad / iphone.','crop-thumbnails')),
81
+ 'script_connection_error' => self::fixJsLangStrings(__('The plugin can not correctly connect to the server.','crop-thumbnails')),
82
+ 'noPermission' => self::fixJsLangStrings(__('You are not permitted to crop the thumbnails.','crop-thumbnails'))
83
+ );
84
+ }
85
 
86
  public function getCropData() {
 
 
 
 
87
  global $content_width;//include nasty content_width
88
  $content_width = 9999;//override the idioty
89
 
99
  'sourceImageMeta' => null,
100
  'postTypeFilter' => null,
101
  'imageSizes' => array_values($GLOBALS['CROP_THUMBNAILS_HELPER']->getImageSizes()),
102
+ 'lang' => self::getLangArray(),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  'nonce' => wp_create_nonce($GLOBALS['CROP_THUMBNAILS_HELPER']->getNonceBase())
104
  );
105
 
107
  if(empty($_REQUEST['imageId'])) {
108
  throw new InvalidArgumentException('Missing Parameter "imageId".');
109
  }
110
+
111
 
112
  $imagePostObj = get_post(intval($_REQUEST['imageId']));
113
  if(empty($imagePostObj) || $imagePostObj->post_type!=='attachment') {
115
  }
116
  $result['sourceImageId'] = $imagePostObj->ID;
117
 
118
+ if(!CptSaveThumbnail::isUserPermitted( $imagePostObj->ID )) {
119
+ throw new CPT_ForbiddenException();
120
+ }
121
+
122
  if(!empty($_REQUEST['posttype']) && post_type_exists($_REQUEST['posttype'])) {
123
  $result['postTypeFilter'] = $_REQUEST['posttype'];
124
  }
190
  return $result;
191
  }
192
 
193
+ protected function getUncroppedImageData($ID, $imageSize = 'full') {
194
  $orig_img = wp_get_attachment_image_src($ID, $imageSize);
195
  $orig_ima_gcd = $this->gcd($orig_img[1], $orig_img[2]);
196
  $result = array(
205
  return $result;
206
  }
207
 
208
+ protected function calculateRatioData($width,$height) {
209
  $gcd = $this->gcd($width,$height);
210
  $result = array(
211
  'gcd' => $gcd,
215
  return $result;
216
  }
217
 
218
+ protected static function shouldBeHiddenOnPostType($options,$post_type) {
219
  if(empty($post_type)) {
220
  return false;
221
  }
232
  * @param string name post-type (i.e. post, page, ...)
233
  * @return boolean true if Image-size should be hidden
234
  */
235
+ protected static function shouldSizeBeHidden($options, $img_size, $post_type='') {
236
  $_return = false;
237
  if(!empty($post_type)) {
238
  //we are NOT in the mediathek
239
 
240
  //-if hide_size
241
+ if(!empty($options['hide_size'][$post_type][ $img_size['id'] ])) {
242
  $_return = true;
243
  }
244
 
258
  }
259
 
260
 
 
 
 
 
 
 
 
 
 
 
261
  /**
262
  * Greatest cummon divisor
263
  */
264
+ protected function gcd($a, $b) {
265
  if(function_exists('gmp_gcd')) {
266
  $gcd = gmp_strval(gmp_gcd($a,$b));
267
  return ($gcd);
271
  }
272
  }
273
 
274
+ protected static function my_gcd($a, $b) {
275
  $b = ( $a == 0 )? 0 : $b;
276
  return ( $a % $b )? self::my_gcd($b, abs($a - $b)) : $b;
277
  }
functions/helper.php CHANGED
@@ -12,8 +12,8 @@
12
  * This class is for overriding settings in php-tests - therefore the functions cant be static :(
13
  */
14
  class CropThumbnailsHelper {
15
- private static $defaultSizes = array('thumbnail','medium','medium_large','large');
16
- private static $optionsKey = 'crop-post-thumbs';
17
 
18
  public function getUploadDir() {
19
  $upload_dir = wp_upload_dir();
12
  * This class is for overriding settings in php-tests - therefore the functions cant be static :(
13
  */
14
  class CropThumbnailsHelper {
15
+ protected static $defaultSizes = array('thumbnail','medium','medium_large','large');
16
+ protected static $optionsKey = 'crop-post-thumbs';
17
 
18
  public function getUploadDir() {
19
  $upload_dir = wp_upload_dir();
functions/save.php CHANGED
@@ -4,7 +4,7 @@ add_action( 'wp_ajax_cptSaveThumbnail', array($cptSave, 'saveThumbnailAjaxWrap')
4
 
5
  class CptSaveThumbnail {
6
 
7
- private static $debug = array();
8
 
9
  /**
10
  * Handle-function called via ajax request.
@@ -105,7 +105,7 @@ class CptSaveThumbnail {
105
 
106
  if(!$_error) {
107
  //update metadata --> otherwise new sizes will not be updated
108
- $imageMetadata = self::updateMetadata($imageMetadata, $activeImageSize->name, $currentFilePathInfo, $croppedSize['width'], $croppedSize['height']);
109
  } else {
110
  self::addDebug('error on '.$currentFilePathInfo['basename']);
111
  self::addDebug($_processing_error);
@@ -195,18 +195,29 @@ class CptSaveThumbnail {
195
  die();
196
  }
197
 
198
- private static function addDebug($text) {
199
  self::$debug[] = $text;
200
  }
201
 
202
- private static function getDebug() {
203
  if(!empty(self::$debug)) {
204
  return self::$debug;
205
  }
206
  return array();
207
  }
208
 
209
- private static function updateMetadata($imageMetadata, $imageSizeName, $currentFilePathInfo, $croppedWidth, $croppedHeight) {
 
 
 
 
 
 
 
 
 
 
 
210
  $fullFilePath = trailingslashit($currentFilePathInfo['dirname']) . $currentFilePathInfo['basename'];
211
 
212
  $fileTypeInformations = wp_check_filetype($fullFilePath);
@@ -216,6 +227,14 @@ class CptSaveThumbnail {
216
  $newValues['width'] = intval($croppedWidth);
217
  $newValues['height'] = intval($croppedHeight);
218
  $newValues['mime-type'] = $fileTypeInformations['type'];
 
 
 
 
 
 
 
 
219
 
220
  $oldValues = array();
221
  if(empty($imageMetadata['sizes'])) {
@@ -235,7 +254,7 @@ class CptSaveThumbnail {
235
  * @param array all available ImageSizes
236
  * @return boolean true if the newImageSize is in the list of ImageSizes and dimensions are correct
237
  */
238
- private static function isImageSizeValid(&$submitted,$dbData) {
239
  if(empty($submitted->name)) {
240
  return false;
241
  }
@@ -256,7 +275,8 @@ class CptSaveThumbnail {
256
  * @return object JSON-Object with submitted data
257
  * @throw Exception if the security validation fails
258
  */
259
- private function getValidatedInput() {
 
260
  if(!check_ajax_referer($GLOBALS['CROP_THUMBNAILS_HELPER']->getNonceBase(),'_ajax_nonce',false)) {
261
  throw new Exception(__("ERROR: Security Check failed (maybe a timeout - please try again).",'crop-thumbnails'), 1);
262
  }
@@ -272,6 +292,9 @@ class CptSaveThumbnail {
272
  throw new Exception(__('ERROR: Submitted data is incomplete.','crop-thumbnails'), 1);
273
  }
274
 
 
 
 
275
 
276
  if(!isset($input->selection->x) || !isset($input->selection->y) || !isset($input->selection->x2) || !isset($input->selection->y2)) {
277
  throw new Exception(__('ERROR: Submitted data is incomplete.','crop-thumbnails'), 1);
@@ -306,7 +329,7 @@ class CptSaveThumbnail {
306
  * @param int height of the new image
307
  * @return string path to the new image
308
  */
309
- private static function generateFilename( $file, $w, $h ){
310
  $info = pathinfo($file);
311
  $dir = $info['dirname'];
312
  $ext = $info['extension'];
@@ -315,4 +338,24 @@ class CptSaveThumbnail {
315
  $destfilename = $dir.'/'.$name.'-'.$suffix.'.'.$ext;
316
  return $destfilename;
317
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
318
  }
4
 
5
  class CptSaveThumbnail {
6
 
7
+ protected static $debug = array();
8
 
9
  /**
10
  * Handle-function called via ajax request.
105
 
106
  if(!$_error) {
107
  //update metadata --> otherwise new sizes will not be updated
108
+ $imageMetadata = self::updateMetadata($imageMetadata, $activeImageSize->name, $currentFilePathInfo, $croppedSize['width'], $croppedSize['height'], $input);
109
  } else {
110
  self::addDebug('error on '.$currentFilePathInfo['basename']);
111
  self::addDebug($_processing_error);
195
  die();
196
  }
197
 
198
+ protected static function addDebug($text) {
199
  self::$debug[] = $text;
200
  }
201
 
202
+ protected static function getDebug() {
203
  if(!empty(self::$debug)) {
204
  return self::$debug;
205
  }
206
  return array();
207
  }
208
 
209
+ /**
210
+ * Update the metadata for one image-size.
211
+ *
212
+ * @param array $imageMetadata the image-metadata base array to modify
213
+ * @param string $imageSizeName the name of the image-size
214
+ * @param array $currentFilePathInfo pathinfo of the new thumbnail/image-size
215
+ * @param int $croppedWidth the new width of the image
216
+ * @param int $croppedHeight the new height of the image
217
+ * @param array $croppingInput the input data for the cropping (to store the crop-informations)
218
+ * @return array the modified $imageMetadata
219
+ */
220
+ protected static function updateMetadata($imageMetadata, $imageSizeName, $currentFilePathInfo, $croppedWidth, $croppedHeight, $croppingInput) {
221
  $fullFilePath = trailingslashit($currentFilePathInfo['dirname']) . $currentFilePathInfo['basename'];
222
 
223
  $fileTypeInformations = wp_check_filetype($fullFilePath);
227
  $newValues['width'] = intval($croppedWidth);
228
  $newValues['height'] = intval($croppedHeight);
229
  $newValues['mime-type'] = $fileTypeInformations['type'];
230
+ $newValues['cpt_last_cropping_data'] = array(
231
+ 'x' => $croppingInput->selection->x,
232
+ 'y' => $croppingInput->selection->y,
233
+ 'x2' => $croppingInput->selection->x2,
234
+ 'y2' => $croppingInput->selection->y2,
235
+ 'original_width' => $imageMetadata['width'],
236
+ 'original_height' => $imageMetadata['height'],
237
+ );
238
 
239
  $oldValues = array();
240
  if(empty($imageMetadata['sizes'])) {
254
  * @param array all available ImageSizes
255
  * @return boolean true if the newImageSize is in the list of ImageSizes and dimensions are correct
256
  */
257
+ protected static function isImageSizeValid(&$submitted,$dbData) {
258
  if(empty($submitted->name)) {
259
  return false;
260
  }
275
  * @return object JSON-Object with submitted data
276
  * @throw Exception if the security validation fails
277
  */
278
+ protected function getValidatedInput() {
279
+
280
  if(!check_ajax_referer($GLOBALS['CROP_THUMBNAILS_HELPER']->getNonceBase(),'_ajax_nonce',false)) {
281
  throw new Exception(__("ERROR: Security Check failed (maybe a timeout - please try again).",'crop-thumbnails'), 1);
282
  }
292
  throw new Exception(__('ERROR: Submitted data is incomplete.','crop-thumbnails'), 1);
293
  }
294
 
295
+ if(!self::isUserPermitted($input->sourceImageId)) {
296
+ throw new Exception(__("You are not permitted to crop the thumbnails.",'crop-thumbnails'), 1);
297
+ }
298
 
299
  if(!isset($input->selection->x) || !isset($input->selection->y) || !isset($input->selection->x2) || !isset($input->selection->y2)) {
300
  throw new Exception(__('ERROR: Submitted data is incomplete.','crop-thumbnails'), 1);
329
  * @param int height of the new image
330
  * @return string path to the new image
331
  */
332
+ protected static function generateFilename( $file, $w, $h ){
333
  $info = pathinfo($file);
334
  $dir = $info['dirname'];
335
  $ext = $info['extension'];
338
  $destfilename = $dir.'/'.$name.'-'.$suffix.'.'.$ext;
339
  return $destfilename;
340
  }
341
+
342
+ /**
343
+ * Check if the user is permitted to crop the thumbnails.
344
+ *
345
+ * You may override the default result of this function by using the filter 'crop_thumbnails_user_permission_check'.
346
+ *
347
+ * @param int $imageId The ID of the image that should be cropped (not used in default test)
348
+ * @return boolean true if the user is permitted
349
+ */
350
+ public static function isUserPermitted($imageId) {
351
+ $return = false;
352
+ $cropThumbnailSettings = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
353
+ if(current_user_can('edit_files')) {
354
+ $return = true;
355
+ }
356
+ if(current_user_can('upload_files') && empty($cropThumbnailSettings['user_permission_only_on_edit_files'])) {
357
+ $return = true;
358
+ }
359
+ return apply_filters('crop_thumbnails_user_permission_check', $return, $imageId);
360
+ }
361
  }
functions/settingsscreen.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  class CropThumbnailsSettingsScreen {
3
- private static $uniqeSettingsId = 'cpt-settings';
4
- private static $cssPrefix = 'cpt_settings_';
5
 
6
  public function __construct() {
7
  add_action('admin_menu', array($this,'addOptionsPage'));
@@ -75,6 +75,11 @@ class CropThumbnailsSettingsScreen {
75
  $_sectionID = 'choose_sizes_section';
76
  add_settings_section($_sectionID, esc_html__('Sizes and Post Types','crop-thumbnails'), array($this,'sectionDescriptionChooseSizes'), 'page1');
77
 
 
 
 
 
 
78
  $_sectionID = 'quick_test';
79
  add_settings_section($_sectionID, esc_html__('Plugin Test','crop-thumbnails'), array($this,'sectionDescriptionTest'), 'page1');
80
 
@@ -86,7 +91,7 @@ class CropThumbnailsSettingsScreen {
86
  add_settings_field($_tmpID, esc_html__('Enable Data-Debug.','crop-thumbnails'), array($this,'callback_'.$_tmpID), 'page1', $_sectionID, array( 'label_for' => self::$cssPrefix.$_tmpID ));
87
  }
88
 
89
- private function vueSettingsScreen() {
90
  $settings = array(
91
  'options' => $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions(),
92
  'post_types' => $GLOBALS['CROP_THUMBNAILS_HELPER']->getPostTypes(),
@@ -123,6 +128,20 @@ class CropThumbnailsSettingsScreen {
123
 
124
  public function emptySectionDescription() {/*empty*/ }
125
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  public function callback_debug_js() {
127
  $options = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
128
  $_id = 'debug_js';
@@ -166,6 +185,11 @@ class CropThumbnailsSettingsScreen {
166
  }
167
  }
168
 
 
 
 
 
 
169
  /* Advanced Section */
170
  $_tmpID = 'debug_js';
171
  if(!empty($input[$_tmpID])) {
1
  <?php
2
  class CropThumbnailsSettingsScreen {
3
+ protected static $uniqeSettingsId = 'cpt-settings';
4
+ protected static $cssPrefix = 'cpt_settings_';
5
 
6
  public function __construct() {
7
  add_action('admin_menu', array($this,'addOptionsPage'));
75
  $_sectionID = 'choose_sizes_section';
76
  add_settings_section($_sectionID, esc_html__('Sizes and Post Types','crop-thumbnails'), array($this,'sectionDescriptionChooseSizes'), 'page1');
77
 
78
+ $_sectionID = 'userPermission';
79
+ add_settings_section($_sectionID, esc_html__('User Permission','crop-thumbnails'), array($this,'emptySectionDescription'), 'page1');
80
+ $_tmpID = 'user_permission_only_on_edit_files';
81
+ add_settings_field($_tmpID, esc_html__('When active, only users who are able to edit files can crop thumbnails. Otherwise (default), any user who can upload files can also crop thumbnails.','crop-thumbnails'), array($this,'callback_'.$_tmpID), 'page1', $_sectionID, array( 'label_for' => self::$cssPrefix.$_tmpID ));
82
+
83
  $_sectionID = 'quick_test';
84
  add_settings_section($_sectionID, esc_html__('Plugin Test','crop-thumbnails'), array($this,'sectionDescriptionTest'), 'page1');
85
 
91
  add_settings_field($_tmpID, esc_html__('Enable Data-Debug.','crop-thumbnails'), array($this,'callback_'.$_tmpID), 'page1', $_sectionID, array( 'label_for' => self::$cssPrefix.$_tmpID ));
92
  }
93
 
94
+ protected function vueSettingsScreen() {
95
  $settings = array(
96
  'options' => $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions(),
97
  'post_types' => $GLOBALS['CROP_THUMBNAILS_HELPER']->getPostTypes(),
128
 
129
  public function emptySectionDescription() {/*empty*/ }
130
 
131
+
132
+
133
+ public function callback_user_permission_only_on_edit_files() {
134
+ $options = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
135
+ $_id = 'user_permission_only_on_edit_files';
136
+ if(empty($options[$_id])) { $options[$_id] = ''; }
137
+ echo '<input name="'.$GLOBALS['CROP_THUMBNAILS_HELPER']->getOptionsKey().'['.$_id.']" id="'.self::$cssPrefix.$_id.'" type="checkbox" value="1" ' . checked( 1, $options[$_id], false) . ' />';
138
+ ?>
139
+ <div class="<?php echo self::$cssPrefix ?>submit">
140
+ <input name="Submit" type="submit" value="<?php esc_attr_e('Save Changes','crop-thumbnails'); ?>" class="button-primary" />
141
+ </div>
142
+ <?php
143
+ }
144
+
145
  public function callback_debug_js() {
146
  $options = $GLOBALS['CROP_THUMBNAILS_HELPER']->getOptions();
147
  $_id = 'debug_js';
185
  }
186
  }
187
 
188
+ $_tmpID = 'user_permission_only_on_edit_files';
189
+ if(!empty($input[$_tmpID])) {
190
+ $storeInDb[$_tmpID] = 1;
191
+ }
192
+
193
  /* Advanced Section */
194
  $_tmpID = 'debug_js';
195
  if(!empty($input[$_tmpID])) {
readme.txt CHANGED
@@ -4,8 +4,8 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=volkm
4
  Tags: post-thumbnails, images, media library
5
  Requires at least: 4.6
6
  Requires PHP: 5.3.0
7
- Tested up to: 4.9
8
- Stable tag: 1.1.3
9
  License: GPL v3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -123,6 +123,14 @@ If you fork and planning to publish the forked plugin, please contact me.
123
  5. Quicktest on settings-page, to check if your system is correct setup.
124
 
125
  == Changelog ==
 
 
 
 
 
 
 
 
126
  = 1.1.3 =
127
  * add a filter (crop_thumbnails_activat_on_adminpages), for adding the plugins js/css on futher admin-pages like the taxonomy edit-page.
128
  * update js and webpack dependencies
4
  Tags: post-thumbnails, images, media library
5
  Requires at least: 4.6
6
  Requires PHP: 5.3.0
7
+ Tested up to: 5.0
8
+ Stable tag: 1.2.0
9
  License: GPL v3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
123
  5. Quicktest on settings-page, to check if your system is correct setup.
124
 
125
  == Changelog ==
126
+ = 1.2.0 =
127
+ * the used cropping data are now stored in the image after the crop, making it possible to code a plugin for restoring the cropped region on new image-sizes
128
+ * fix for hiding crop sizes is not working when the image_size_names_choose-filter is used for that post-type
129
+ * change permission from 'upload_files' to 'edit_files' (Attention: authors will no longer able to crop the thumbnails)
130
+ * add a filter function to override the permission to crop thumbnails (crop_thumbnails_user_permission_check)
131
+ * add settings-section to set if users can crop thumbnails with capability "edit_files" or "upload_files"
132
+ * get featured image panel button working in wordpress v5
133
+
134
  = 1.1.3 =
135
  * add a filter (crop_thumbnails_activat_on_adminpages), for adding the plugins js/css on futher admin-pages like the taxonomy edit-page.
136
  * update js and webpack dependencies