Changeset 3411238
- Timestamp:
- 12/04/2025 05:16:54 PM (2 months ago)
- Location:
- lens-media-library-folders/trunk
- Files:
-
- 6 edited
-
assets/js/modules/features/Settings.js (modified) (6 diffs)
-
assets/js/modules/posts/PostMediaOrganizer.js (modified) (5 diffs)
-
readme.txt (modified) (1 diff)
-
src/Admin/AssetLoader.php (modified) (1 diff)
-
src/Admin/PostMediaManager.php (modified) (7 diffs)
-
src/Rest/Controllers/SettingsController.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
lens-media-library-folders/trunk/assets/js/modules/features/Settings.js
r3408664 r3411238 58 58 // Settings storage key 59 59 this.storageKey = 'lens_settings'; 60 this.localStorageMigrationFlag = 'lens_settings_migrated_v1'; 61 this.localStorageIgnoredKeys = ['telemetry_enabled']; 62 this.telemetryCacheKey = 'lens_telemetry_cache'; 60 63 61 64 // Current settings cache … … 75 78 this.$resetButton = null; 76 79 this.$resetConfirmInput = null; 77 this.$resetStatusMessage = null; 80 this.$resetStatusMessage = null; 81 82 // One-time cleanup: remove telemetry flag from old localStorage so fresh installs default to opt-out 83 this.purgeLegacyTelemetryCache(); 78 84 79 85 this.apiAccess = { … … 189 195 const saved = localStorage.getItem(this.storageKey); 190 196 const settings = saved ? JSON.parse(saved) : {}; 197 198 // Never hydrate telemetry from local cache; must come from server 199 this.localStorageIgnoredKeys.forEach((key) => { 200 if (settings[key] !== undefined) { 201 delete settings[key]; 202 } 203 }); 191 204 192 205 if (settings['sidebar-width'] === undefined) { … … 299 312 writeSettingsToLocal(settings) { 300 313 try { 301 localStorage.setItem(this.storageKey, JSON.stringify(settings)); 314 const filtered = { ...settings }; 315 this.localStorageIgnoredKeys.forEach((key) => { 316 if (filtered[key] !== undefined) { 317 delete filtered[key]; 318 } 319 }); 320 321 localStorage.setItem(this.storageKey, JSON.stringify(filtered)); 302 322 return true; 303 323 } catch (error) { 304 324 console.error('[Settings] Error saving settings to localStorage:', error); 305 325 return false; 326 } 327 } 328 329 /** 330 * Persist telemetry state with activation stamp so reinstalls (new activation) ignore stale cache. 331 */ 332 writeTelemetryCache(value) { 333 try { 334 const activation = this.getTelemetryActivation(); 335 const payload = { 336 value: Boolean(value), 337 activation 338 }; 339 localStorage.setItem(this.telemetryCacheKey, JSON.stringify(payload)); 340 } catch (error) { 341 console.warn('[Settings] Unable to write telemetry cache', error); 342 } 343 } 344 345 /** 346 * Read telemetry cache if activation matches current install. 347 */ 348 readTelemetryCache() { 349 try { 350 const raw = localStorage.getItem(this.telemetryCacheKey); 351 if (!raw) return null; 352 const parsed = JSON.parse(raw); 353 if (!parsed || typeof parsed.activation === 'undefined') { 354 return null; 355 } 356 if (parsed.activation !== this.getTelemetryActivation()) { 357 return null; 358 } 359 return typeof parsed.value === 'undefined' ? null : Boolean(parsed.value); 360 } catch (error) { 361 console.warn('[Settings] Unable to read telemetry cache', error); 362 return null; 363 } 364 } 365 366 /** 367 * Get current activation stamp passed from server (resets on uninstall/reinstall). 368 */ 369 getTelemetryActivation() { 370 const config = window.lens_config || {}; 371 const telemetry = config.telemetry || {}; 372 const activation = telemetry.first_activated || 0; 373 return Number(activation) || 0; 374 } 375 376 /** 377 * One-time cleanup to ensure telemetry opt-in isn't carried across uninstall/reinstall 378 */ 379 purgeLegacyTelemetryCache() { 380 try { 381 if (localStorage.getItem(this.localStorageMigrationFlag)) { 382 return; 383 } 384 385 const saved = localStorage.getItem(this.storageKey); 386 if (saved) { 387 const settings = JSON.parse(saved); 388 if (settings && settings.telemetry_enabled !== undefined) { 389 delete settings.telemetry_enabled; 390 localStorage.setItem(this.storageKey, JSON.stringify(settings)); 391 } 392 } 393 394 localStorage.setItem(this.localStorageMigrationFlag, '1'); 395 } catch (error) { 396 console.warn('[Settings] Unable to purge legacy telemetry cache', error); 306 397 } 307 398 } … … 1616 1707 }; 1617 1708 1709 // Telemetry: trust server, but fall back to cached state if activation matches and server didn't return true 1710 const cachedTelemetry = this.readTelemetryCache(); 1711 if (typeof cachedTelemetry === 'boolean' && this.currentSettings['telemetry_enabled'] !== true) { 1712 this.currentSettings['telemetry_enabled'] = cachedTelemetry; 1713 } 1714 1618 1715 this.currentSettings = this.normalizeSettings(this.currentSettings); 1619 1716 … … 1719 1816 ...collectedSettings 1720 1817 }; 1818 1819 // Persist telemetry state locally with activation stamp (used only as fallback, not for reinstalls) 1820 if (Object.prototype.hasOwnProperty.call(collectedSettings, 'telemetry_enabled')) { 1821 this.writeTelemetryCache(collectedSettings['telemetry_enabled']); 1822 } 1721 1823 1722 1824 if (!this.writeSettingsToLocal(this.currentSettings)) { -
lens-media-library-folders/trunk/assets/js/modules/posts/PostMediaOrganizer.js
r3407095 r3411238 851 851 */ 852 852 initBulkActions() { 853 this.bindBulkFormSubmission(); 854 853 855 // Check if bulk organize was triggered 854 856 const urlParams = new URLSearchParams(window.location.search); … … 866 868 // See initBulkActions() 867 869 } 870 871 /** 872 * Bind bulk form submission to prevent page refresh for Lens action. 873 */ 874 bindBulkFormSubmission() { 875 const $form = $('#posts-filter'); 876 if (!$form.length) { 877 return; 878 } 879 880 const getSelectedAction = () => { 881 const top = $('#bulk-action-selector-top').val(); 882 const bottom = $('#bulk-action-selector-bottom').val(); 883 return top !== '-1' ? top : bottom; 884 }; 885 886 $form.on('submit.lensBulkOrganize', this.handleBulkFormSubmit.bind(this, $form, getSelectedAction)); 887 } 888 889 /** 890 * Handle Lens bulk organize submit without reloading. 891 */ 892 handleBulkFormSubmit($form, getSelectedAction, e) { 893 const action = getSelectedAction(); 894 if (action !== 'lens_bulk_organize_media') { 895 return; 896 } 897 898 e.preventDefault(); 899 e.stopPropagation(); 900 901 const postIds = this.getSelectedPostIds($form); 902 903 if (!postIds.length) { 904 this.showNotification(__('Please select posts and try again','lens-media-library-folders'), 'info'); 905 return; 906 } 907 908 this.showBulkOrganizeModal(postIds.length, postIds); 909 } 910 911 /** 912 * Collect selected post IDs from the list table. 913 */ 914 getSelectedPostIds($form) { 915 return $form.find('input[name="post[]"]:checked').map((index, el) => parseInt(el.value, 10)).get().filter((id) => !isNaN(id)); 916 } 868 917 869 918 /** 870 919 * Show bulk organize modal 871 920 */ 872 async showBulkOrganizeModal(postCount) { 873 // Get the transient data from server 874 const transientKey = 'lens_bulk_organize_' + this.config.userId; 875 921 async showBulkOrganizeModal(postCount, selectedPostIds = null) { 922 const postIdsFromSelection = Array.isArray(selectedPostIds) ? selectedPostIds.filter((id) => !isNaN(parseInt(id, 10))) : null; 923 876 924 try { 877 // First get the post IDs from transient 925 if (postIdsFromSelection && postIdsFromSelection.length) { 926 const statsResponse = await this.apiClient.post('post-media/stats', { 927 post_ids: postIdsFromSelection 928 }); 929 930 if (!statsResponse.success) { 931 this.showNotification(__('Failed to get media statistics','lens-media-library-folders'), 'error'); 932 return; 933 } 934 935 this.showBulkModal(postIdsFromSelection, statsResponse.data); 936 return; 937 } 938 939 // Fallback to server-provided transient (compatibility with existing flow) 878 940 const response = await $.ajax({ 879 941 url: this.config.ajaxUrl, … … 893 955 894 956 // Show a helpful message instead of error 895 this.showNotification(__('Please select posts and try again','lens-media-library-folders'), 'info');957 this.showNotification(__('Please select posts and try again','lens-media-library-folders'), 'info'); 896 958 return; 897 959 } … … 1026 1088 }); 1027 1089 1090 // Preload media so organize works without expanding the list 1091 this.loadBulkMedia(postIds); 1092 1028 1093 // Show modal with fade-in effect after initialization 1029 1094 setTimeout(() => { … … 1057 1122 this.bulkModal.find('.lens-modal-organize').on('click', async () => { 1058 1123 const folderData = bulkFolderSelector.getValue(); 1059 1060 // Confirm action1061 if (!confirm(this.config.strings.confirmBulk)) {1062 return;1063 }1064 1124 1065 1125 await this.organizeBulkMedia(postIds, folderData.id, folderData.name); -
lens-media-library-folders/trunk/readme.txt
r3409943 r3411238 320 320 321 321 == Changelog == 322 323 = 1.0.1 - 2025-12-04 = 324 * Added: "Organize Media" to WooCommerce product bulk actions, allowing product media to be moved in a single pass. 325 * Minor bug fixes and improvements. 322 326 323 327 = 1.0.0 - 2025-12-01 = -
lens-media-library-folders/trunk/src/Admin/AssetLoader.php
r3407095 r3411238 1231 1231 'max_file_size_readable' => $import_max_file_size_label, 1232 1232 'supported_formats' => array( 'csv', 'json', 'xml', 'zip' ), 1233 ), 1234 'telemetry' => array( 1235 'first_activated' => (int) get_option( \Lens\Admin\Telemetry::OPTION_FIRST_ACTIVATED, 0 ), 1233 1236 ), 1234 1237 ); -
lens-media-library-folders/trunk/src/Admin/PostMediaManager.php
r3407095 r3411238 22 22 use Lens\Models\Folder; 23 23 use Lens\Models\Attachment; 24 use Lens\Models\Permission; 24 25 use WP_Post; 25 26 use WP_Error; … … 33 34 34 35 /** 36 * Post types that should expose Lens bulk actions. 37 * 38 * @var array 39 */ 40 private $bulk_action_post_types = array( 'post', 'page', 'product' ); 41 42 /** 35 43 * Constructor. 36 44 */ … … 54 62 55 63 // Add bulk actions 56 add_filter( 'bulk_actions-edit-post', array( $this, 'add_bulk_actions' ) );57 add_filter( 'bulk_actions-edit-page', array( $this, 'add_bulk_actions' ) );58 add_filter( 'handle_bulk_actions-edit-post', array( $this, 'handle_bulk_actions' ), 10, 3 );59 add_filter( 'handle_bulk_actions-edit-page', array( $this, 'handle_bulk_actions' ), 10, 3 );64 foreach ( $this->get_bulk_action_post_types() as $post_type ) { 65 add_filter( "bulk_actions-edit-{$post_type}", array( $this, 'add_bulk_actions' ) ); 66 add_filter( "handle_bulk_actions-edit-{$post_type}", array( $this, 'handle_bulk_actions' ), 10, 3 ); 67 } 60 68 61 69 // AJAX handlers … … 2085 2093 $trashed_media_ids = $trashed_media_handler->get_trashed_attachment_ids(); 2086 2094 2095 // Provide lightweight permission map for list/editor contexts 2096 $user_permissions = array( 2097 'lens_manage_media' => Permission::current_user_can_capability( 'lens_manage_media' ), 2098 'lens_bulk_operations' => Permission::current_user_can_capability( 'lens_bulk_operations' ), 2099 ); 2100 2101 $permissions_available = (bool) apply_filters( 'lens/permissions/is_available', false, get_current_user_id() ); 2102 2087 2103 // IMPORTANT: Localize ApiClient BEFORE enqueueing it 2088 2104 wp_localize_script( … … 2096 2112 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG, 2097 2113 'trashedMediaIds' => $trashed_media_ids, // Pre-loaded trashed IDs for Classic Editor 2114 'user_permissions' => $user_permissions, 2115 'permissions_available' => $permissions_available, 2098 2116 ) 2099 2117 ); … … 2840 2858 2841 2859 /** 2860 * Get post types that should expose Lens bulk actions. 2861 * 2862 * @return array Post types. 2863 */ 2864 private function get_bulk_action_post_types() { 2865 /** 2866 * Filters the post types that display Lens bulk actions. 2867 * 2868 * @since 1.0.0 2869 * 2870 * @param array $post_types Post types that should expose Lens bulk actions. 2871 */ 2872 return apply_filters( 'lens_bulk_action_post_types', $this->bulk_action_post_types ); 2873 } 2874 2875 /** 2842 2876 * Add bulk actions to post list. 2843 2877 * … … 3210 3244 } 3211 3245 3212 $valid_screens = array( 'post', 'page', ' edit-post', 'edit-page' );3246 $valid_screens = array( 'post', 'page', 'product', 'edit-post', 'edit-page', 'edit-product' ); 3213 3247 $screen_id = $screen->id; 3214 3248 $base = $screen->base; -
lens-media-library-folders/trunk/src/Rest/Controllers/SettingsController.php
r3408648 r3411238 350 350 351 351 // Handle special telemetry setting 352 if ( isset( $config['handler'] ) && 'telemetry' === $config['handler'] ) { 353 if ( ! current_user_can( 'manage_options' ) ) { 354 return $this->error( 355 Constants::REST_ERROR_FORBIDDEN, 356 __( 'You do not have permission to change telemetry settings.', 'lens-media-library-folders' ), 357 rest_authorization_required_code() 358 ); 359 } 360 361 Telemetry::update_consent( $value ); 362 $updated[ $key ] = $value; 363 continue; 364 } 352 if ( isset( $config['handler'] ) && 'telemetry' === $config['handler'] ) { 353 // Same capability already enforced by update_settings_permissions_check (upload_files). 354 Telemetry::update_consent( $value ); 355 $updated[ $key ] = $value; 356 continue; 357 } 365 358 366 359 $meta_key = isset( $config['user_meta_key'] ) ? $config['user_meta_key'] : Constants::user_meta( $key );
Note: See TracChangeset
for help on using the changeset viewer.