Changeset 3444453
- Timestamp:
- 01/21/2026 10:55:34 PM (2 months ago)
- Location:
- technodrome-ai-content-assistant/trunk
- Files:
-
- 8 edited
-
changelog.txt (modified) (1 diff)
-
features/content-rules-tab/profile-name-sync.js (modified) (7 diffs)
-
features/footer/profile-buttons.js (modified) (12 diffs)
-
features/generate-tab/default-tone.js (modified) (11 diffs)
-
includes/class-ajax-handler.php (modified) (1 diff)
-
includes/class-profile-manager.php (modified) (1 diff)
-
readme.txt (modified) (3 diffs)
-
technodrome-ai-content-assistant.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
technodrome-ai-content-assistant/trunk/changelog.txt
r3444346 r3444453 1 1 # Changelog - Technodrome AI Content Assistant 2 3 ## Version 4.0.3 - 2026-01-21 4 5 ### BUG FIXES 6 - **Structure Name Not Persisting:** CRITICAL FIX - Structure Name field now properly saves and loads 7 - structure_name was missing from collectCompleteProfileData() - FIXED (added to line 540, 751) 8 - Now included in content_rules when autosave collects profile data 9 - Structure Name properly persists across profile switches 10 - Fixed 3000ms debounce delay - reduced to standard 500ms for faster saves (line 54-59) 11 - Fixed save_profile() treating update_user_meta false return as error - was blocking save when data identical 12 13 - **Default Tone AutoSave:** Fixed Default Tone field not saving to profile 14 - Added missing AutoSave event listener for default tone changes 15 - Default Tone now persists correctly across profile switches 16 - AutoSave notification displays when default tone is changed 17 18 ### IMPROVEMENTS 19 - Enhanced field change detection in AutoSave system 20 - Better profile data persistence for all fields 21 - Faster AutoSave debounce timing for Structure Name field (500ms instead of 3000ms) 22 23 --- 2 24 3 25 ## Version 4.0.2 - 2026-01-21 -
technodrome-ai-content-assistant/trunk/features/content-rules-tab/profile-name-sync.js
r3444356 r3444453 8 8 9 9 init: function() { 10 console.log('TAICS Profile Name Sync initialized');11 10 this.bindEvents(); 12 11 this.loadCurrentProfileName(); … … 14 13 15 14 bindEvents: function() { 16 // Listen for profile changes to reload the Structure Name17 $(document).off('taics_profile_ changed.profile-name');18 $(document).on('taics_profile_ changed.profile-name', (e, profileNumber) => {15 // Listen for profile changes to load the Structure Name from profile data 16 $(document).off('taics_profile_loaded.profile-name'); 17 $(document).on('taics_profile_loaded.profile-name', (e, profileNumber, profileData) => { 19 18 this.activeProfile = profileNumber; 20 this.loadCurrentProfileName(); 19 // Load structure_name from profile data, not from button label 20 this.loadStructureNameFromProfile(profileData); 21 21 }); 22 22 … … 24 24 $('#taics-structure-name').off('input.profile-name blur.profile-name'); 25 25 $('#taics-structure-name').on('input.profile-name blur.profile-name', () => { 26 this.syncProfileName(); 26 // Only sync if NOT currently loading profile data 27 // Check flag from profile-buttons.js 28 const isLoading = window.TAICS_Profile_Buttons && window.TAICS_Profile_Buttons.isLoadingProfileData; 29 if (!isLoading) { 30 this.syncProfileName(); 31 } 27 32 }); 28 33 }, 29 34 35 loadStructureNameFromProfile: function(profileData) { 36 // Load structure_name from profile data 37 if (profileData && profileData.content_rules && profileData.content_rules.structure_name) { 38 // Profile has a custom structure name - use it 39 const structureName = profileData.content_rules.structure_name; 40 $('#taics-structure-name').val(structureName); 41 // Update button label to match loaded name 42 this.updateButtonLabel(structureName); 43 } else { 44 // Profile doesn't have structure name - clear the field 45 $('#taics-structure-name').val(''); 46 // Restore default button label 47 this.updateButtonLabel(''); 48 } 49 }, 50 51 updateButtonLabel: function(customName) { 52 // Apply 7-character limit and UPPERCASE conversion 53 let displayLabel = customName.substring(0, 7).toUpperCase(); 54 // If empty, use "PROFILE" as fallback 55 displayLabel = displayLabel || 'PROFILE'; 56 57 // Update the profile button label 58 const $profileBtn = $(`.taics-profile-btn-wide[data-profile="${this.activeProfile}"]`); 59 const $profileLabel = $profileBtn.find('.taics-profile-text'); 60 $profileLabel.text(displayLabel); 61 62 // Update the title attribute 63 const titleText = `Profile ${this.activeProfile} (${displayLabel})`; 64 $profileBtn.attr('title', titleText); 65 }, 66 30 67 loadCurrentProfileName: function() { 31 // Get the custom name from the current profile button if it exists 32 const $profileBtn = $(`.taics-profile-btn-wide[data-profile="${this.activeProfile}"]`); 33 if ($profileBtn.length === 0) { 34 return; 35 } 36 37 // Get the label text from the profile button 38 const $profileLabel = $profileBtn.find('.taics-profile-text'); 39 let currentLabel = $profileLabel.text() || 'PROFILE'; 40 41 // If label is just "PROFILE", clear the Structure Name 42 if (currentLabel === 'PROFILE') { 43 $('#taics-structure-name').val(''); 44 } else { 45 // Otherwise, put the label value back in Structure Name for editing 46 $('#taics-structure-name').val(currentLabel); 47 } 68 // This method is no longer used - kept for backward compatibility 48 69 }, 49 70 … … 101 122 success: (response) => { 102 123 if (response.success) { 103 console.log('Profile name saved:', customName, 'for profile', this.activeProfile);104 105 124 // Show notification 106 125 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.show === 'function') { … … 112 131 } 113 132 } else { 114 console.warn('Failed to save profile name:', response.data);115 133 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.show === 'function') { 116 134 window.TAICS_Notifications.show( … … 123 141 }, 124 142 error: (xhr, status, error) => { 125 console.error('AJAX error saving profile name:', error);126 143 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.show === 'function') { 127 144 window.TAICS_Notifications.show( … … 137 154 138 155 $(document).ready(function() { 156 // Wait 200ms to ensure profile-buttons.js initializes first (it uses 100ms) 139 157 setTimeout(() => { 140 158 TAICS_Profile_Name_Sync.init(); 141 159 window.TAICS_Profile_Name_Sync = TAICS_Profile_Name_Sync; 142 }, 300);160 }, 200); 143 161 }); 144 162 -
technodrome-ai-content-assistant/trunk/features/footer/profile-buttons.js
r3434643 r3444453 9 9 apiKeys: {}, 10 10 isInitializing: false, 11 isFirstProfileLoad: true, // Track if this is the first profile load (on page init) 12 13 // AutoSave Revolution v3.4.0 - Properties 11 isFirstProfileLoad: true, 12 isLoadingProfileData: false, 14 13 autoSaveTimeout: null, 15 14 autoSaveInProgress: false, 16 lastChangedField: null, // Za specifičnu notifikaciju 17 15 lastChangedField: null, 16 structureNameTimeout: null, 17 18 18 init: function() { 19 19 if (this.isInitialized) { … … 25 25 this.loadProfiles(); 26 26 this.setDefaultActiveProfile(); 27 console.log('TAICS Profile Buttons initialized successfully');28 27 }, 29 28 … … 32 31 $('.taics-profile-btn-wide').on('click.taics-profile', this.handleProfileClick.bind(this)); 33 32 34 // AUTOSAVE REVOLUCIJA: Save dugme je uklonjeno - AutoSave sistem je aktivan 35 // Komentarišem event listener jer više nije potreban 36 // Sve izmene se automatski čuvaju u profil kroz AutoSave sistem 37 /* 38 $('#taics-save-profile-btn').off('click.taics-save-profile'); 39 $('#taics-save-profile-btn').on('click.taics-save-profile', this.handleSaveProfileClick.bind(this)); 40 */ 41 42 // AutoSave Revolution v3.4.0 - Event listeners na svim poljima 33 // AutoSave event listeners na svim poljima 43 34 $('#taics-ai-provider').on('change.autosave', () => this.autoSaveField('ai_settings.ai_provider')); 44 35 $('#taics-ai-model').on('change.autosave', () => this.autoSaveField('ai_settings.ai_model')); … … 49 40 $('#taics-category').on('change.autosave', () => this.autoSaveField('category')); 50 41 $('#taics-generation-mode').on('change.autosave', () => this.autoSaveField('generation_mode')); 42 $('#taics-default-tone').on('change.autosave', () => this.autoSaveField('default_tone')); 51 43 $('input[name="layout_template"]').on('change.autosave', () => this.autoSaveField('layout_template.template_id')); 52 44 $('#taics-ai-image-toggle').on('change.autosave', () => this.autoSaveField('ai_image.enabled')); … … 58 50 $(document).on('taics_headings_changed.autosave', () => this.autoSaveField('content_rules.headings')); 59 51 60 // v3.4.0: Content Rules fields - Structure Name with 3s debounce 61 let structureNameTimeout; 52 // Content Rules fields - Structure Name with auto-sync to autosave 62 53 $('#taics-structure-name').on('input.autosave', () => { 63 clearTimeout(structureNameTimeout); 64 structureNameTimeout = setTimeout(() => { 54 // v4.0.3 FIX: Use consistent 500ms debounce with main autosave system 55 // This ensures structure_name is saved immediately when user stops typing 56 clearTimeout(this.structureNameTimeout); 57 this.structureNameTimeout = setTimeout(() => { 65 58 this.autoSaveField('content_rules.structure_name'); 66 }, 3000);59 }, 500); 67 60 }); 68 61 $(document).on('taics_guidelines_changed.autosave', () => this.autoSaveField('content_rules.guidelines')); … … 141 134 // This ensures video-context-mode.js and video-slot-transformation.js initialize properly 142 135 setTimeout(() => { 143 $(document).trigger('taics_profile_loaded', [this.activeProfile]); 136 const profileData = this.profiles[this.activeProfile] || {}; 137 $(document).trigger('taics_profile_loaded', [this.activeProfile, profileData]); 144 138 }, 100); 145 139 } … … 192 186 } 193 187 this.switchProfile(profileNumber); 194 // IMPORTANT: Only trigger taics_profile_loaded when actually SWITCHING to a different profile 195 // This prevents resetting the UI when user navigates away and back to Generate tab 196 $(document).trigger('taics_profile_loaded', [profileNumber]); 197 }, 198 188 }, 189 199 190 switchProfile: function(profileNumber) { 191 // 🔥 v4.0.4 FIX: Set flag to prevent autosave while loading profile data 192 this.isLoadingProfileData = true; 193 200 194 $('.taics-profile-btn-wide').removeClass('active'); 201 195 $(`.taics-profile-btn-wide[data-profile="${profileNumber}"]`).addClass('active'); … … 206 200 if (profileData && Object.keys(profileData).length > 0) { 207 201 this.loadCompleteProfileDataFromObject(profileData); 208 202 209 203 // Show detailed profile loaded notification 210 204 this.showProfileLoadedNotification(profileNumber, profileData); 211 205 } else { 212 206 this.loadCompleteProfileData(profileNumber); 213 207 214 208 // Show empty profile notification 215 209 this.showNotification( … … 221 215 this.updateProfileStatus(); 222 216 $(document).trigger('taics_profile_changed', [profileNumber]); 217 218 // 🔥 FIX v4.0.3: Trigger taics_profile_loaded BEFORE clearing flag 219 // This ensures profile-name-sync.js receives the flag as true during load 220 const switchProfileData = profileData || {}; 221 $(document).trigger('taics_profile_loaded', [profileNumber, switchProfileData]); 222 223 // 🔥 v4.0.4 FIX: Clear flag after profile data loading completes 224 // This allows autosave to work again for user changes 225 this.isLoadingProfileData = false; 223 226 }, 224 227 … … 324 327 loadContentRulesData: function(contentRules) { 325 328 // Load Structure Name 326 $('#taics-structure-name').val(contentRules.structure_name || ''); 329 const structureName = contentRules.structure_name || ''; 330 $('#taics-structure-name').val(structureName); 331 332 // 🔥 v4.0.4 FIX: Only trigger input if NOT currently loading profile data 333 // This allows profile-name-sync to detect the new value without triggering autosave 334 // when the field is being loaded from profile (not user-changed) 335 if (!this.isLoadingProfileData) { 336 $('#taics-structure-name').trigger('input.autosave'); 337 } 327 338 328 339 if (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.setValue === 'function') { … … 538 549 window.TAICS_Default_Tone.getValue() : 'article-specific', 539 550 content_rules: { 551 structure_name: $('#taics-structure-name').val() || '', 540 552 headings: (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.getValue === 'function') ? 541 553 window.TAICS_Headings_Editor.getValue() : $('#taics-headings-editor').val() || '', … … 749 761 window.TAICS_Default_Tone.getValue() : 'article-specific', 750 762 content_rules: { 763 structure_name: $('#taics-structure-name').val() || '', 751 764 headings: (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.getValue === 'function') ? 752 765 window.TAICS_Headings_Editor.getValue() : $('#taics-headings-editor').val() || '', -
technodrome-ai-content-assistant/trunk/features/generate-tab/default-tone.js
r3421276 r3444453 33 33 init: function() { 34 34 if (this.state.initialized) { 35 console.log('TAICS Default Tone already initialized');36 35 return; 37 36 } 38 39 console.log('Initializing TAICS Default Tone v3.4.0 (AutoSave REVOLUCIJA)');40 37 41 38 // Bind events … … 46 43 47 44 this.state.initialized = true; 48 console.log('TAICS Default Tone initialized successfully');49 45 }, 50 46 … … 55 51 const self = this; 56 52 57 // Handle tone selection change - trigger AutoSave58 $(this.config.fieldId).on('change. taics-default-tone', function() {53 // Handle tone selection change - uses .autosave namespace so profile-buttons.js detects it 54 $(this.config.fieldId).on('change.autosave', function() { 59 55 const selectedValue = $(this).val(); 60 56 self.handleToneChange(selectedValue); … … 67 63 68 64 // Listen for profile loaded events 69 $(document).on('taics_profile_loaded.default-tone', function(e, data) { 70 self.handleProfileLoaded(data); 65 // v4.0.3 FIX: Event now passes (profileNumber, profileData) as parameters 66 $(document).on('taics_profile_loaded.default-tone', function(e, profileNumber, profileData) { 67 self.handleProfileLoaded(profileData); 71 68 }); 72 69 … … 87 84 handleToneChange: function(selectedValue) { 88 85 if (this.state.isLocked) { 89 console.log('Default tone field is locked, change ignored');90 86 return; 91 87 } … … 93 89 this.state.currentValue = selectedValue; 94 90 95 // Trigger AutoSave event - profile-buttons.js will save to profile 96 $(document).trigger('taics_field_changed.autosave', ['default_tone', selectedValue]); 97 98 console.log('Default tone changed to:', selectedValue); 91 // Event is already triggered by the dropdown change - no need to trigger again! 92 // profile-buttons.js will catch the change.autosave event automatically 99 93 }, 100 94 … … 102 96 * Handle profile switch 103 97 */ 104 handleProfileSwitch: function(profileId) { 105 console.log('Profile switched to:', profileId); 98 handleProfileSwitch: function() { 106 99 // Profile data will be loaded via profile_loaded event 107 100 }, … … 114 107 if (data && data.default_tone) { 115 108 this.setValue(data.default_tone); 116 console.log('Default tone loaded from profile:', data.default_tone);117 109 } else { 118 110 this.setValue(this.config.defaultValue); … … 164 156 $field.prop('disabled', true).addClass('taics-field-locked'); 165 157 this.state.isLocked = true; 166 console.log('Default tone field locked');167 158 }, 168 159 … … 174 165 $field.prop('disabled', false).removeClass('taics-field-locked'); 175 166 this.state.isLocked = false; 176 console.log('Default tone field unlocked');177 167 }, 178 168 … … 184 174 $(document).off('.default-tone'); 185 175 this.state.initialized = false; 186 console.log('TAICS Default Tone cleaned up');187 176 } 188 177 }; -
technodrome-ai-content-assistant/trunk/includes/class-ajax-handler.php
r3444346 r3444453 1542 1542 } 1543 1543 1544 // Limit to 7 characters and uppercase1545 $profile_name = strtoupper(substr($profile_name, 0, 7));1546 1547 // Save to user_meta1548 1544 $user_id = get_current_user_id(); 1549 $meta_key = 'taics_profile_' . $profile_number . '_name'; 1550 1551 if ($profile_name === '' || $profile_name === 'PROFILE') { 1552 // Delete the meta if it's being reset to empty/default 1553 delete_user_meta($user_id, $meta_key); 1545 $profile_manager = new TAICS_Profile_Manager(); 1546 $profile_data = $profile_manager->get_profile($profile_number); 1547 1548 // Update structure_name in content_rules 1549 if (!isset($profile_data['content_rules'])) { 1550 $profile_data['content_rules'] = array(); 1551 } 1552 $profile_data['content_rules']['structure_name'] = $profile_name; 1553 1554 // Save the updated profile 1555 $result = $profile_manager->save_profile($profile_number, $profile_data); 1556 1557 if ($result) { 1558 wp_send_json_success([ 1559 'message' => esc_html__('Profile name saved successfully', 'technodrome-ai-content-assistant'), 1560 'profile_number' => $profile_number, 1561 'profile_name' => $profile_name 1562 ]); 1554 1563 } else { 1555 // Save the custom name 1556 update_user_meta($user_id, $meta_key, $profile_name); 1557 } 1558 1559 wp_send_json_success([ 1560 'message' => esc_html__('Profile name saved successfully', 'technodrome-ai-content-assistant'), 1561 'profile_number' => $profile_number, 1562 'profile_name' => $profile_name 1563 ]); 1564 wp_send_json_error(esc_html__('Failed to save profile name', 'technodrome-ai-content-assistant')); 1565 } 1564 1566 } catch (Exception $e) { 1565 1567 wp_send_json_error(esc_html__('Failed to save profile name', 'technodrome-ai-content-assistant')); -
technodrome-ai-content-assistant/trunk/includes/class-profile-manager.php
r3421276 r3444453 125 125 $result = update_user_meta($user_id, $meta_key, $json_data); 126 126 127 // v4.0.3 FIX: update_user_meta returns false if data is identical, not if save failed 128 // Only treat actual database errors as failure (return 0 from wpdb queries) 129 // Success = true (data changed), false (data identical - still saved), or meta_id (new metadata) 130 if (is_wp_error($result)) { 131 return false; 132 } 133 127 134 if ($profile_name) { 128 135 update_user_meta($user_id, $meta_key . '_name', sanitize_text_field($profile_name)); 129 136 } 130 131 if ($result === false) {132 return false;133 }134 137 135 138 $this->log_profile_action('save', $profile_number, $user_id); -
technodrome-ai-content-assistant/trunk/readme.txt
r3444356 r3444453 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.0 7 Stable tag: 4.0. 27 Stable tag: 4.0.3 8 8 License: GPL v2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 41 41 == Changelog == 42 42 43 = 4.0.5 (2026-01-21) = 44 * **FIX**: Structure Name no longer resets to "PROFILE" when switching profiles 45 * **FIX**: Custom profile names persist correctly without being overwritten 46 * **IMPROVED**: Better control flow for profile data synchronization 47 48 = 4.0.4 (2026-01-21) = 49 * **FIX**: Structure Name field now clears properly when switching profiles 50 * **FIX**: Profile button labels update correctly with saved custom names 51 * **IMPROVED**: Better event listener timing for profile data synchronization 52 53 = 4.0.3 (2026-01-21) = 54 * **FIX**: Default Tone field now saves correctly with AutoSave 55 * **IMPROVED**: Default tone is now properly persisted in profiles 56 * **IMPROVED**: AutoSave event listener added for default tone changes 57 43 58 = 4.0.2 (2026-01-21) = 44 59 * **NEW**: Custom Profile Names - Rename profiles for better organization … … 73 88 74 89 == Upgrade Notice == 90 91 = 4.0.3 = 92 Important bug fix - Default Tone field now saves correctly with AutoSave. Recommended for all users. 75 93 76 94 = 4.0.2 = -
technodrome-ai-content-assistant/trunk/technodrome-ai-content-assistant.php
r3444346 r3444453 3 3 * Plugin Name: Technodrome AI Content Assistant 4 4 * Plugin URI: https://technodrome.org/ai-content-assistant 5 * Description: Advanced AI content generation plugin with multiple AI providers, profile system, layout templates, and content rules for WordPress. v4.0.2 - Custom Profile Names.6 * Version: 4.0. 25 * Description: Advanced AI content generation plugin with multiple AI providers, profile system, layout templates, and content rules for WordPress. 6 * Version: 4.0.3 7 7 * Author: Technodrome Team 8 8 * Author URI: https://technodrome.org … … 30 30 31 31 // Plugin constants 32 define('TAICS_VERSION', '4.0. 2'); // Custom Profile Names32 define('TAICS_VERSION', '4.0.3'); 33 33 define('TAICS_PLUGIN_FILE', __FILE__); 34 34 define('TAICS_PLUGIN_DIR', plugin_dir_path(__FILE__));
Note: See TracChangeset
for help on using the changeset viewer.