Plugin Directory

Changeset 3363822


Ignore:
Timestamp:
09/18/2025 08:47:07 AM (6 months ago)
Author:
petredobrescu
Message:

Version 6.3.4

🤖 The Smarter AI Control Update

Release Date: September 18, 2025

✨ New Features & Improvements

  • Preserve Existing SEO Data: Added an option to keep your existing ALT, caption, and description fields untouched when using AI-generated image SEO — your manual work is safe!
  • Better Block Editor Integration: AI-generated SEO data and image optimization now work flawlessly when uploading images directly from the block editor.
  • AI Support Chatbot: Meet our new AI-powered support assistant — here to help you faster, 24/7.

🛠️ Fixes & Behavioral Improvements

  • Full AI Deactivation: Disabling AI Image SEO now also disables auto-generation and hides all related options from WP Admin, including the Bulk Processing section.
  • EXIF-Based AI Training Block: If AI training is blocked via EXIF data, the plugin now shows a clear message and prevents the image from being sent for processing.
  • Re-Optimize Logic Fixed: The "Re-optimize with/without SmartCrop" buttons now respect the selected option instead of always using the saved setting.
  • Inline Font Handling: Inline fonts will no longer be replaced with CDN links if the CSS option is disabled.
  • AI SEO for Excluded Items: Bulk AI SEO generation now skips excluded items as expected.

Update now for smarter AI behavior, more control over your data, and an improved support experience! 🚀

Location:
shortpixel-image-optimiser
Files:
70 edited
1 copied

Legend:

Unmodified
Added
Removed
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/AdminController.php

    r3346588 r3363822  
    127127         $queueController = new QueueController();
    128128
    129         if ($optimizeAiController->isAutoAiEnabled())
     129        /* if ($optimizeAiController->isAutoAiEnabled())
    130130         {
    131131            $args = ['action' => 'requestAlt'];
    132132            $queueController->addItemToQueue($mediaItem, $args);
    133          }
     133         } */
    134134                 
    135135         
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/AjaxController.php

    r3346588 r3363822  
    690690        $this->checkImageAccess($imageModel);
    691691
    692         $smartcrop = false;
     692        $args = ['action' => 'reoptimize', 'compressionType' => $compressionType];
     693
     694        // Smartcrop is not always passed, only add here when passed otherwise to defaults.
    693695        if ($actionType == ImageModel::ACTION_SMARTCROP || $actionType == ImageModel::ACTION_SMARTCROPLESS)
    694696        {
    695             $smartcrop = $actionType;
     697            $args['smartcrop'] = $actionType;
    696698        }
    697699
    698700        // @todo Ideally this should go to QueueController - addItemToQueue, but issue with arguments. Leaving it for now.
    699701        $queueController = new QueueController();
    700         $result  = $queueController->addItemToQueue($imageModel, ['action' => 'reoptimize', 'compressionType' => $compressionType,
    701             'smartcrop' => $smartcrop]);
     702        $result  = $queueController->addItemToQueue($imageModel, $args);
    702703
    703704   
     
    807808    protected function createBulk($json, $data)
    808809    {
     810        $filters = [];
     811        $has_filters = false;
     812       
     813        if (isset($_POST['filter_startdate']))
     814        {
     815             $filters['start_date'] = intval($_POST['filter_startdate']);
     816             $has_filters = true;   
     817        }
     818        if (isset($_POST['filter_enddate']))
     819        {
     820             $filters['end_date'] = intval($_POST['filter_enddate']);
     821             $has_filters = true;
     822
     823        }
     824
     825        $args = [];
     826        if (true === $has_filters)
     827        {
     828            $args['filters'] = $filters;
     829        }
     830       
     831
     832
    809833        $bulkControl = BulkController::getInstance();
    810         $stats = $bulkControl->createNewBulk('media');
     834        $stats = $bulkControl->createNewBulk('media', $args);
    811835        $json->media->stats = $stats;
    812836
    813         $stats = $bulkControl->createNewBulk('custom');
     837        $stats = $bulkControl->createNewBulk('custom', $args);
    814838        $json->custom->stats = $stats;
    815839
     
    827851       
    828852        $doAi = filter_var(sanitize_text_field($_POST['aiActive']), FILTER_VALIDATE_BOOLEAN);
     853
     854        $aiPreserve = isset($_POST['aiPreserve']) ? filter_var(sanitize_text_field($_POST['aiPreserve']), FILTER_VALIDATE_BOOLEAN) : null;
     855
    829856        $backgroundProcess = filter_var(sanitize_text_field($_POST['backgroundProcess']), FILTER_VALIDATE_BOOLEAN);
    830857
     
    841868        \wpSPIO()->settings()->autoAIBulk = $doAi;
    842869
     870        if (false === is_null($aiPreserve))
     871        {
     872            \wpSPIO()->settings()->aiPreserve = $aiPreserve;
     873        }
     874
    843875        $bulkControl = BulkController::getInstance();
    844876
     
    887919
    888920        if (in_array('media', $queues)) {
    889             $stats = $bulkControl->createNewBulk('media', 'bulk-restore');
     921            $stats = $bulkControl->createNewBulk('media', ['customOp' => 'bulk-restore']);
    890922            $json->media->stats = $stats;
    891923        }
    892924
    893925        if (in_array('custom', $queues)) {
    894             $stats = $bulkControl->createNewBulk('custom', 'bulk-restore');
     926            $stats = $bulkControl->createNewBulk('custom', ['customOp' => 'bulk-restore']);
    895927            $json->custom->stats = $stats;
    896928        }
     
    905937
    906938
    907         $stats = $bulkControl->createNewBulk('media', 'migrate');
     939        $stats = $bulkControl->createNewBulk('media', ['customOp' => 'migrate']);
    908940        $json->media->stats = $stats;
    909941
     
    917949
    918950
    919         $stats = $bulkControl->createNewBulk('media', 'removeLegacy');
     951        $stats = $bulkControl->createNewBulk('media', ['customOp' => 'removeLegacy']);
    920952        $json->media->stats = $stats;
    921953
     
    9841016
    9851017        $optimizer = $qItem->getApiController('requestAlt');
    986         //$optimize->useCustomSettings($settingsData);
    987         //$result = $optimizer->enqueueItem($qItem, array_merge(['preview_only' => true, 'action' => 'requestAlt'], $settingsData));
    9881018
    9891019        $qItem->requestAltAction(array_merge(['preview_only' => true], $settingsData));
     
    10211051                    if (property_exists($result, 'aiData'))
    10221052                    {
     1053                        $aiModel = AiDataModel::getModelByAttachment($qItem->item_id, 'media');
     1054
    10231055                         $aiData = $optimizer->formatResultData($result->aiData, $qItem);
     1056                         list($items, $aiData) = $optimizer->formatGenerated($aiData, $aiModel->getCurrentData(), $aiModel->getOriginalData());
    10241057                         $aiData['item_id'] = $qItem->item_id;
    10251058                         $aiData['time_generated'] = time();
     
    10391072                    }
    10401073                }
    1041 
    1042 
    1043                 //$is_done = true;
    1044                 //$this->send((object) $result_json);
    1045                 //break;
    10461074               
    10471075            }
     
    10591087            $i++;
    10601088        }
    1061 
    1062         //$this->send($result_json);
    1063 
    10641089    }
    10651090
     
    10761101        else
    10771102        {
    1078             $item = new AiDataModel($id);
     1103            $item = AiDataModel::getModelByAttachment($id);
    10791104            $attach_id = $id;
    10801105        }
    1081 
    1082     //  $attach_id = null;
    10831106       
    10841107        $imageModel = \wpSPIO()->fileSystem()->getMediaImage($attach_id);
     
    15401563    {
    15411564
     1565        // defaults
     1566        $message = __('This user is not allowed to edit this image', 'shortpixel-image-optimiser');
     1567
    15421568        $accessModel = AccessModel::getInstance();
    15431569        if (is_object($mediaItem)) {
    15441570            $bool = $accessModel->imageIsEditable($mediaItem);
    15451571            $id = $mediaItem->get('id');
     1572
    15461573        } else {
    15471574            $bool = false;
    15481575            $id = false;
     1576            if (! is_object($mediaItem))
     1577            {
     1578                $message = __('Image does not exist or could not be loaded', 'shortpixel-image-optimiser');
     1579            }
    15491580        }
    15501581
    15511582        if ($bool === false) {
    15521583            $json = new \stdClass;
    1553             $json->message = __('This user is not allowed to edit this image', 'shortpixel-image-optimiser');
     1584            $json->message = $message;
    15541585            $json->status = false;
    15551586            $json->id = $id;
     
    15731604
    15741605        wp_send_json($json);
    1575 
    15761606        exit();
    15771607    }
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Api/AiController.php

    r3347742 r3363822  
    235235    protected function handleSuccess($aiData, QueueItem $qItem)
    236236    {
    237        
     237      if (false === is_null($qItem->data()->returndatalist))
     238      {
     239         $returndatalist = $qItem->data()->returndatalist;
     240         if (is_object($returndatalist))
     241         {
     242           $returndatalist = (array) $returndatalist;
     243         }
     244
     245         foreach($returndatalist as $name => $data)
     246         {
     247            if (is_object($data)) // annoying conversion somehow by json decode from record
     248            {
     249               $data = (array) $data;
     250            }
     251            if (! isset($aiData[$name]) && isset($data['status']))
     252            {
     253                $aiData[$name]  = $data['status'];
     254            }
     255         }
     256      }
     257     
    238258      return $this->returnSuccess(['aiData' => $aiData], RequestManager::STATUS_SUCCESS, __('Retrieved AI image SEO data', 'shortpixel-image-optimiser')); ;
    239259    }
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Api/ApiController.php

    r3289850 r3363822  
    239239                    $imageName = $imageNames[$index];
    240240                    $fileName = $fileNames[$index];
     241                    $paramlist = $qItem->data()->paramlist;
     242
     243                    // Here add paramList items that are possible needed for success checks
     244                    $params = isset($paramlist[$index]) ? (array) $paramlist[$index] : [];
     245                   
     246
    241247                    $data = array(
    242248                        'fileName' => $fileName,
    243249                        'imageName' => $imageName,
    244250                    );
     251
     252                    $data = array_merge($params, $data);
    245253
    246254                    // Filesize might not be present, but also imageName ( only if smartcrop is done, might differ per image)
     
    399407
    400408        if (false === $this->checkFileSizeMargin($originalFileSize, $checkFileSize)) {
    401             $image['image']['status'] = self::STATUS_OPTIMIZED_BIGGER;
    402             $checkFileSize = $originalFileSize;
     409           
     410            // Prevent this check if smartcrop is active on this image.
     411            if (isset($data['resize']) && 4 == $data['resize'] )
     412            {
     413                $image['image']['status'] = self::STATUS_OPTIMIZED_BIGGER;
     414                $checkFileSize = $originalFileSize;
     415            }
    403416        }
    404417
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/BulkController.php

    r3289850 r3363822  
    3737   * 'bulk-restore', or 'migrate'.
    3838   */
    39    public function createNewBulk($type = 'media', $customOp = null)
    40    {
     39   public function createNewBulk($type = 'media', $args = [])
     40   {
     41      $defaults = [
     42          'customOp' => null,
     43          'filters' => [],
     44
     45      ];
     46
     47
     48      $args = wp_parse_args($args, $defaults);
     49
    4150      $queueController = new QueueController(['is_bulk' => true]);
    4251
     
    5362      $Q = $queueController->getQueue($type);
    5463
    55       $Q->createNewBulk();
    56 
    57       if (! is_null($customOp))
     64      if (! is_null($args['customOp']))
    5865      {
    59         $options = array();
     66        $customOp = $args['customOp'];
     67        //$args['customOp'] = $customOp;
    6068        if ($customOp == 'bulk-restore')
    6169        {
    62           $options['numitems'] = 5;
    63           $options['retry_limit'] = 5;
    64           $options['process_timeout'] = 3000;
     70          $args['numitems'] = 5;
     71          $args['retry_limit'] = 5;
     72          $args['process_timeout'] = 3000;
     73         
    6574        }
    6675        if ($customOp == 'migrate' || $customOp == 'removeLegacy')
    6776        {
    68            $options['numitems'] = 200;
     77           $args['numitems'] = 200;
    6978        }
    7079
    71                 $options = apply_filters('shortpixel/bulk/custom_options', $options, $customOp);
    72         $Q->setCustomBulk($customOp, $options);
     80                $args = apply_filters('shortpixel/bulk/custom_options', $args);
     81
    7382      }
     83
     84     
     85      $Q->createNewBulk($args);
     86
    7487
    7588      return $Q->getStats();
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Front/CDNController.php

    r3346588 r3363822  
    299299        $fonts = ['.ttf', '.woff', '.woff2', '.otf'];
    300300
    301         if (true == $settings->cdn_js) {
     301        if (true === $settings->cdn_js) {
    302302            $checkExtensions[] = '.js';
    303303           
    304304        }
    305         if (true == $settings->cdn_css)
     305        if (true === $settings->cdn_css)
    306306        {   
    307307            $checkExtensions[] = '.css';
     
    353353        $replaceBlocks = $this->filterRegexExclusions($replaceBlocks);
    354354        $replaceBlocks = $this->filterOtherDomains($replaceBlocks);
     355        $replaceBlocks = $this->filterFonts($replaceBlocks);
    355356
    356357        if (count($replaceBlocks) > 0) {
     
    466467        }
    467468
     469
     470    }
     471
     472    /** The image check on inline CSS might also catch inline fonts.  Check against settings if they should be processed or not.
     473     *
     474     * @param mixed $replaceBlocks
     475     * @return mixed
     476     */
     477    protected function filterFonts($replaceBlocks)
     478    {
     479        $settings = \wpSPIO()->settings();
     480
     481        if (true === $settings->cdn_css)
     482        {
     483            return $replaceBlocks;
     484        }
     485
     486        $replaceBlocks = array_filter($replaceBlocks, function ($replaceBlock)
     487        {
     488             $fonts = ['.ttf', '.woff', '.woff2', '.otf'];
     489             foreach($fonts as $extcheck)
     490             {
     491                  if (strpos($replaceBlock->url, $extcheck) !== false)
     492                  {
     493                        return false;
     494                  }
     495             }
     496             return true;
     497
     498        });
     499   
     500        return $replaceBlocks;
    468501
    469502    }
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Optimizer/OptimizeAiController.php

    r3348311 r3363822  
    4646        }
    4747
    48    //   $qItem->addResult(['qStatus' => Queue::RESULT_ERROR]);
    4948      return;
    5049  }
     
    5352  public function sendToProcessing(QueueItem $qItem) {
    5453
    55     if (false == $this->isSupported($qItem))
     54/*    if (false == $this->isSupported($qItem))
    5655    {
    5756        // For now only fail here is GIF support, so message is a backstop for now that later should be updated.
     
    6564    }
    6665    else
    67     {
     66    { */
    6867        $this->api->processMediaItem($qItem, $qItem->imageModel);
    69     }
     68    //}
    7069 
    7170  }
     
    7574  {     
    7675
    77       $aiDataModel = new AiDataModel($qItem->item_id);
     76      $aiDataModel = AiDataModel::getModelByAttachment($qItem->item_id);
    7877      $is_processable = $aiDataModel->isProcessable();
    7978
    8079      if (false === $is_processable) {
     80         $message = $aiDataModel->getProcessableReason();
    8181        $qItem->addResult([
    82           'message' => __('AI generation not possible or already generated', 'shortpixel-image-optimiser'),
     82          'message' => $message,
    8383          'is_error' => true,
    8484          'is_done' => true,
     
    107107        break;
    108108        case 'retrieveAlt':  // This might be deprecated, since retrieve will be called via next_action.
    109             $qItem->retrieveAltAction($args['remote_id']);
     109            $qItem->retrieveAltAction($args);
    110110            $directAction = false;
    111111        break;
     
    226226    // Always save the original filename
    227227    $aiData['original_filebase'] = $qItem->imageModel->getFileBase();
     228    $returnDataList = $qItem->data()->returndatalist;
    228229
    229230    if (! isset($aiData['filebase']))
     
    236237    foreach($textItems as $textItem)
    237238    {
    238          if (isset($aiData[$textItem]) && false !== $aiData[$textItem])
     239     
     240         if (isset($aiData[$textItem]) && false !== $aiData[$textItem] && false === is_numeric($aiData[$textItem]))
    239241         {
    240242             $aiData[$textItem] = $this->processTextResult($aiData[$textItem]);
    241243         }
    242     }           
     244         // If 1 is returned as data, this means for some reason the API didn't create a text for this field, while it is allowed to do so. Defer to empty string better than '1'
     245         if (true === is_numeric($aiData[$textItem]) && 1 == $aiData[$textItem])
     246         {
     247            $aiData[$textItem] = '';
     248         }
     249    }   
     250
     251    // Re-add Result after formatting so it passed back
     252    //$qItem->addResult(['aiData' => $aiData]);
     253
    243254
    244255    return $aiData;
     
    248259  {
    249260        $aiData = $qItem->result->aiData; 
    250         $settings = $this->getAISettings();
    251 
     261        $settings = \wpSPIO()->settings();
    252262
    253263        $checks = ['alt' => 'ai_gen_alt',
     
    257267        ];
    258268
    259         foreach($checks as $check_name => $check_setting)
    260         {
    261             if (false === $settings[$check_setting])
    262             {
    263                  unset($aiData[$check_name]);
    264             }
    265         }
    266 
    267 
    268269        $aiData = $this->formatResultData($aiData, $qItem);
    269270
     
    273274        $item_id = $qItem->item_id;
    274275       
    275         $aiModel = new AIDataModel($item_id, 'media');
     276        $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    276277        $aiModel->handleNewData($aiData);
    277278
     
    292293        }
    293294        */
     295
    294296        $imageModel = $qItem->imageModel;
    295         $qItem->addResult(['improvements' => $imageModel->getImprovements()]);
    296 
    297 
    298         $this->addPreview($qItem);
     297        $qItem->addResult(['improvements' => $imageModel->getImprovements()]); // Improvements for bulk UX.
     298
     299        $this->addPreview($qItem); // Preview ( image ) for bulk UX
     300
     301        AiDataModel::flushModelCache($item_id);
     302
     303        // Get generated data which is the final result for the action including exclusions etc.
     304        $data = $this->getAltData($qItem);
     305        $qItem->addResult(['aiData' => $data['generated']]); // But the generated data in the result.
    299306
    300307        $this->finishItemProcess($qItem);
     
    312319  protected function replaceImageAttributes(QueueItem $qItem, $aiData)
    313320  {
     321            if (is_int($aiData['alt']) && is_int($aiData['caption']))
     322            {
     323                Log::addInfo('Alt/Caption returned integer/status, not replace');
     324                return;
     325            }
     326
    314327             // Replacer Part
    315328             $url = $qItem->data()->url;
     
    392405        if ($targetFileObj->exists())
    393406        {
    394           //$qItem->result()->is_error = true;
    395           //$qItem->result()->message = __('Replace Files: File Already exists', 'shortpixel-image-optimiser');
    396407          Log::addWarn('Replace files found filename conflict and didnt run', $targetFileObj->getFullPath());
    397408          return false;
     
    414425      }
    415426
    416 
    417 
    418 
    419427      $replacer = new Replacer();
    420428      $replacer->setSource($source_url);
     
    424432     
    425433      $replacer->replace();
    426 
    427434
    428435      $this->replaceMetaData($item_id, $base_filename, $newFileName );
     
    511518             } */
    512519
    513              $sources[] = $match;
    514 
    515              if (isset($aiData['alt']))
     520             $do_replace = false;
     521
     522             if (isset($aiData['alt']) && false === is_int($aiData['alt']))
    516523             {
    517524                $frontImage->alt = $aiData['alt'];
     525                $do_replace = true;
    518526             }
    519              if (isset($aiData['caption']))
     527             if (isset($aiData['caption']) && false === is_int($aiData['caption']))
    520528             {
    521529                $frontImage->caption = $aiData['caption'];
     530                $do_replace = true;
    522531             }
    523532
    524              $replaces[] = $frontImage->buildImage();
    525 
     533             if (true === $do_replace)
     534             {
     535                $sources[] = $match;
     536                $replaces[] = $frontImage->buildImage();
     537             }
    526538
    527539            }
    528540
    529             $content = $replacer2->replaceContent($content, $sources, $replaces);
    530            
    531             $replacer2->Updater()->updatePost($post_id, $content);
     541            if (count($sources) > 0 && count($replaces) > 0)
     542            {
     543                Log::addInfo('Running Ai Replace : ', [$aiData, $sources, $replaces]);
     544                $content = $replacer2->replaceContent($content, $sources, $replaces);
     545                $replacer2->Updater()->updatePost($post_id, $content);
     546            }
    532547        }
    533548
     
    544559  }
    545560
     561  /*
     562  protected function fetchCaptionMatches($content, $qItem)
     563  {
     564       $pattern = '/'
     565  }
     566*/
    546567  /**
    547568   * Check if setting AI is enabled in settings.
     
    598619  }
    599620
    600   protected function getRequestJSON($url, $item_id, $params = [])
     621  /*
     622  protected function getRequestJSON($url, $params = [])
    601623  {
    602624     $settings = $this->getAISettings($params);
     625
     626     $ignore_fields = (isset($params['ignore_fields'])) ? $params['ignore_fields'] : [];
    603627
    604628     $json = [
     
    646670  }
    647671
     672  */
     673
     674  /*
    648675  public function parseJSONForQItem(QueueItem $qItem, $params = [])
    649676  {
    650677        $url = $qItem->data()->url;
    651678        $item_id = $qItem->item_id;
    652         $json = $this->getRequestJSON($url, $item_id, $params);
     679        $settings = \wpSPIO()->settings();
     680
     681        // Note this is also checked in AiDataModel for checking processable.  Might need to sync upon adding fields
     682        if (true === $settings->aiPreserve)
     683        {
     684            $returnDataList = $qItem->data()->returndatalist;
     685
     686            $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
     687            $current = $aiModel->getCurrentData();
     688            $filtered = array_filter($current); // filter out all empty variables
     689
     690          //  $altdata = $this->getAltData($qItem);
     691            $params['ignore_fields'] = array_keys($filtered);
     692           
     693            foreach($filtered as $key => $filter)
     694            {
     695                 $returnDataList[$key] = AiDataModel::F_STATUS_EXCLUDE;
     696            }
     697            $qItem->data()->returndatalist = $returnDataList;
     698        }
     699       
     700        $json = $this->getRequestJSON($url, $params);
     701
     702
    653703        $qItem->data()->paramlist = $json;
    654704  }
    655 
     705        */
     706
     707/*
    656708  protected function parseQuestionForQItem(QueueItem $qItem)
    657709  {
     
    661713        $qItem->data()->url = $question;
    662714  }
    663 
     715*/
     716  /*
    664717  private function getAISettings($params = [])
    665718  {
     
    684737    'ai_use_exif' => $settings->ai_use_exif,
    685738    'ai_language' => $settings->ai_language,
     739    'aiPreserve' => $settings->aiPreserve,
    686740    ];
    687741
     
    690744    return $params;
    691745  }
    692 
    693   public function parseQuestion($url, $item_id, $params = [])
    694   {
    695     $settings = \wpSPIO()->settings();
    696 
    697     $defaults = [
    698     'ai_general_context' => $settings->ai_general_context,
    699     'ai_use_post' => $settings->ai_use_post,
    700     'ai_gen_alt' => $settings->ai_gen_alt,
    701     'ai_gen_caption' => $settings->ai_gen_caption,
    702     'ai_gen_description' => $settings->ai_gen_description,
    703     'ai_filename_prefercurrent' => $settings->ai_filename_prefercurrent,
    704     'ai_limit_alt_chars' => $settings->ai_limit_alt_chars,
    705     'ai_alt_context' => $settings->ai_alt_context,
    706     'ai_limit_description_chars' => $settings->ai_limit_description_chars,
    707     'ai_description_context' => $settings->ai_description_context,
    708     'ai_limit_caption_chars' => $settings->ai_limit_caption_chars,
    709     'ai_caption_context' => $settings->ai_caption_context,
    710     'ai_gen_filename' => $settings->ai_gen_filename,
    711     'ai_limit_filename_chars' => $settings->ai_limit_filename_chars,
    712     'ai_filename_context' => $settings->ai_filename_context,
    713     'ai_use_exif' => $settings->ai_use_exif,
    714     'ai_language' => $settings->ai_language,
    715     ];
    716 
    717     $params = wp_parse_args($params, $defaults);
    718 
    719     $question = [
    720             'main' => $params['ai_general_context'],
    721             'language' => $params['ai_language'],
    722             'required_tags' => [],
    723      ];
    724 
    725      $question['page'] = $this->getPageQuestion($question, $item_id, $params);
     746 */
    726747   
    727      $question = $this->getPartQuestion($question, 'alt', $params);
    728      $question = $this->getPartQuestion($question, 'caption', $params);
    729      $question = $this->getPartQuestion($question, 'description', $params);
    730      $question = $this->getPartQuestion($question, 'filename', $params);
    731 
    732     if (true == $params['ai_use_exif'])
    733     {
    734          $question['exif'] = ' and take into account the image EXIF data when generating all the requested texts';
    735     }
    736 
    737    // $question['tags'] = implode($question['required_tags'], ',');
    738 
    739     $question = apply_filters('shortpixel/ai/parsed_questions', $question);
    740 
    741    /* $alt = isset($question['alt']) ? $question['alt'] : '';
    742     $caption = isset($question['caption']) ? $question['caption'] : '';
    743     $description =isset($question['description']) ? $question['description'] : '';
    744     $filename = isset($question['filename']) ? $question['filename'] : '';
    745 */
    746     $specs = [];
    747     foreach($question['required_tags'] as $tag)
    748     {
    749         $specs[] = $question[$tag];
    750     }
    751     $specs = implode(' ', $specs);
    752 
    753     $required_tags =  implode(',', $question['required_tags_ainame']);
    754    
    755     if (strlen(trim($params['ai_language'])) <= 0)
    756     {   
    757         $params['ai_language'] = get_locale();
    758     }
    759 
    760     $final_question = sprintf("For the URL %s , with this context \" %s \" , write for %s SEO friendly texts with the following specifications: %s in %s language %s . Provide the answer in JSON format, seperating the %s output in seperate fields",
    761         $url,
    762         $question['main'],
    763         $required_tags,
    764         $specs,
    765         $question['language'],
    766         $question['exif'],
    767         $required_tags
    768 
    769 
    770     );
    771 
    772     return $final_question;
    773 
    774   }
    775  
    776   protected function getPartQuestion($question, $name, $params)
    777   {
    778    
    779     $limit = 'ai_limit_' . $name . '_chars';
    780     $context = 'ai_' . $name . '_context';
    781     $to_use = 'ai_gen_' . $name; 
    782 
    783     switch($name)
    784     {
    785          case 'alt':
    786             $aiName = 'alt tag';
    787          break;
    788          case 'caption':
    789             $aiName = 'caption tag';
    790          break;
    791          case 'description':
    792             $aiName = 'description text';
    793          break;
    794          case 'filename':
    795             $aiName = 'the file name';
    796          break;
    797     }
    798 
    799     if (true === $params[$to_use])
    800     {
    801         $limit = $params[$limit];
    802         $context = $params[$context];
    803 
    804         $string = ' For the ' . $aiName . ' limit your response to the most relevant ' . $limit . ' characters for SEO ';
    805 
    806         if ('filename' == $name)
    807         {
    808             $string .= ' leaving the filename extension intact ';
    809             if (true === $params['ai_filename_prefercurrent'])
    810             {
    811                 $string .=  ' and change filename only when the current filename is not relevant. Otherwise return false for this field ';
    812             }
    813            
    814         }
    815 
    816         if (strlen(trim($context)) > 0)
    817         {
    818              $string .= ' and use this additional information when generating the ' . $aiName . ':' . $context . '. ';
    819         }
    820 
    821         $question[$name] = $string;
    822         $question['required_tags_ainame'][] = $aiName;
    823         $question['required_tags'][] = $name;
    824     }
    825 
    826    
    827     return $question;
    828   }
    829 
    830   protected function getPageQuestion($question, $item_id, $params)
    831   {
    832         if (false == $params['ai_use_post'])
    833         {
    834              return false;
    835         }
    836 
    837         $post = get_post($item_id);
    838         if (is_null($post) || false === $post)
    839         {
    840              return false;
    841         }
    842 
    843         $parent = $post->post_parent;
    844 
    845         if ($parent <= 0 || ! is_int($parent))
    846         {
    847              return false;
    848         }
    849 
    850         $page_post = get_post($parent);
    851         if (is_null($page_post) || false === $page_post)
    852         {
    853              return false;
    854         }
    855 
    856         $title = $page_post->post_title;
    857         $excerpt = get_the_excerpt(($page_post));
    858 
    859         $string = ' for the article with the title ' . $title;
    860 
    861         if (strlen(trim($excerpt)) > 0 )
    862         {
    863             $string .= ' and excerpt ' . $excerpt;
    864         }
    865 
    866        
    867         return $string;
    868   }
    869 
    870  
    871 
     748
     749  /*
    872750  public function isSupported(queueItem $qItem)
    873751  {
     
    881759       
    882760       return true;
    883   }
     761  } */
    884762
    885763  public function undoAltData(QueueItem $qItem)
    886764  {
    887765       $item_id = $qItem->item_id;
    888        $aiModel = new AiDataModel($item_id, 'media');
     766       $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    889767       $original = $aiModel->getOriginalData();
    890768       $generated = $aiModel->getGeneratedData();
     769
    891770
    892771       $aiData = [
     
    898777   
    899778       $aiModel->revert();
     779       AiDataModel::flushModelCache($item_id);
    900780
    901781       $this->replaceImageAttributes($qItem, $aiData);
    902782
     783       $aiData = $aiModel->getCurrentData();
     784   
    903785       return $this->getAltData($qItem);
    904786  }
     
    908790    $item_id = $qItem->item_id;
    909791
    910     $aiModel = new AiDataModel($item_id, 'media');
     792    $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    911793
    912794    $status = $aiModel->getStatus();
     
    920802                $aiModel->migrate($metacheck);
    921803                delete_post_meta($item_id, 'shortpixel_alt_requests');
    922                 $aiModel = new AiDataModel($item_id, 'media');
     804                $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    923805                $status = $aiModel->getStatus();
    924806         }
     
    927809    $generated = $aiModel->getGeneratedData();
    928810    $original = $aiModel->getOriginalData();
     811    $current = $aiModel->getCurrentData();
    929812
    930813    $image_url = $qItem->imageModel->getUrl();
    931814
    932     $fields = ['alt', 'caption', 'description'];
    933     $dataItems = [];
    934     foreach($fields as $name)
    935     {
    936          if (isset($generated[$name]) && false === is_null($generated[$name]) && strlen($generated[$name]) > 1)
    937          {
    938             $dataItems[] = ucfirst($name);
    939          }
    940     }
     815    list($dataItems, $generated) = $this->formatGenerated($generated, $current, $original);
     816
    941817
    942818    $view = new ViewController();
     
    946822            'result_alt' => $generated['alt'],
    947823            'has_data' => ($status == AiDataModel::AI_STATUS_GENERATED) ? true : false,
     824            'is_processable' => $aiModel->isProcessable(),
     825            'processable_reason' => $aiModel->getProcessableReason(),
     826            'processable_status' => $aiModel->getProcessableReason(true),
    948827            'image_url' => $image_url,
    949828           // 'current_alt' => $current_alt,
    950829            'status' => $status,
    951             'isSupported' => $this->isSupported($qItem),
    952             'dataItems' => $dataItems,
     830      //      'isSupported' => $this->isSupported($qItem),
     831            'dataItems' => $dataItems,  // This seems not used(?)
    953832            'isDifferent' =>  $aiModel->currentIsDifferent(),
    954833        ]);
    955834
     835
     836    // *****!!! Temporary don't pass these back since we don't support it yet ** //
     837
     838    if (isset($generated['filebase']))
     839    {
     840       unset($generated['filebase']);
     841    }
     842    if (isset($generated['filename']))
     843    {
     844       unset($generated['filename']);
     845    }
     846
    956847    $metadata['snippet'] = $view->returnView('snippets/part-aitext');
    957848
    958849    $metadata['generated'] = $generated;
    959850    $metadata['original'] = $original;
     851    $metadata['current'] = $current;
    960852    $metadata['action'] = $qItem->data()->action;
    961853    $metadata['item_id'] = $item_id;
     
    964856}
    965857
     858public function formatGenerated($generated, $current, $original)
     859{
     860   
     861  $fields = ['alt', 'caption', 'description'];
     862  $dataItems = [];
     863
     864  // Statii from AiDataModel which means generated is not available (replace for original/current?)
     865  $statii = [AiDataModel::F_STATUS_PREVENTOVERRIDE, AiDataModel::F_STATUS_EXCLUDESETTING];
     866
     867  foreach($fields as $name)
     868  {
     869       if (false === isset($generated[$name]))
     870       {
     871          continue;
     872       }
     873       $value = $generated[$name];
     874       
     875
     876       if (false === is_null($value) && false === is_int($value) && strlen($value) > 1)
     877       {
     878          $dataItems[] = ucfirst($name);
     879       }
     880       if (is_int($value) && in_array($value, $statii))
     881       {
     882          if (isset($current[$name]))
     883          {
     884               $value = $current[$name];
     885          }
     886          elseif(isset($original[$name]))
     887          {
     888               $value = $original[$name];
     889          }
     890          $generated[$name] = $value;
     891       }
     892  }
     893
     894  return [$dataItems, $generated];
     895}
     896
    966897
    967898
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Optimizer/OptimizerBase.php

    r3346588 r3363822  
    4848      //exit('This call is wron because in it messes with ActionController - Reoptimize ( calls ActionController again instead of OptimizeConrtoller');
    4949      $calledClass = get_called_class();
    50 //Log::addTemp('OptimizerBase Called Class - ' . $calledClass);
     50
    5151      if (! isset(static::$instances[$calledClass]))
    5252      {
     
    5454      }
    5555
    56  //  Log::addTemp('OptimizeBase, Instances', self::$instances);
    5756        return self::$instances[$calledClass];
    5857    }
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Queue/MediaLibraryQueue.php

    r3289850 r3363822  
    88use ShortPixel\ShortQ\ShortQ as ShortQ;
    99use ShortPixel\Controller\CacheController as CacheController;
     10use ShortPixel\Helper\UtilHelper;
    1011use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
    1112use ShortPixel\Model\Image\ImageModel as ImageModel;
     
    3233        'retry_limit' => 30, // amount of times it will retry without errors before giving up
    3334        'enqueue_limit' => 200, // amount of items added to the queue when preparing.
     35        'filters' => [],
    3436     );
    3537
     
    5658   }
    5759
     60   public function createNewBulk($args = [])
     61   {
     62     /* if (isset($args['filters']))
     63      {
     64         $this->addFilters($args['filters']);
     65      } */
     66       
     67     
     68      // Parent should save options as well.
     69       return parent::createNewBulk($args);
     70   }
     71
     72
     73   protected function addFilters($filters)
     74   {
     75
     76      //$start_id = $end_id = null;
     77
     78     
     79      global $wpdb;
     80     
     81
     82      $start_date = isset($filters['start_date'])  ? new \DateTime($filters['start_date']) : false;
     83      $end_date = isset($filters['end_date'])  ? new \DateTime($filters['end_date']) : false;
     84
     85      if (isset($filters['start_date']))
     86      {
     87         //$date = UtilHelper::timestampToDB($filters['start_time']);
     88         $date = $start_date->format("Y-m-d H:i:s");
     89         $startSQL = 'select max(ID) from wp_posts where post_date <= %s group by post_date order by post_date DESC limit 1';
     90         $sql = $wpdb->prepare($startSQL, $date);
     91         $start_id =  $wpdb->get_var($sql);
     92      }
     93      if (isset($filters['end_date']))
     94      {
     95        // $date = UtilHelper::timestampToDB($filters['end_time']);
     96        $date = $end_date->format("Y-m-d H:i:s");
     97         $endSQL = 'select MIN(ID) from wp_posts where post_date <= %s group by post_date order by post_date DESC limit 1';
     98         $sql = $wpdb->prepare($endSQL, $date);
     99         $end_id =  $wpdb->get_var($sql);
     100      }
     101     
     102
     103
     104       //echo "Start $start_id END $end_id";
     105       //exit();
     106      // IF POST DATE NEEDS 09-20 ( or 23:59:59? )
     107      // select post_date, max(ID) from wp_posts where post_date <= '2024-09-21 00:00:00' group by post_date order by post_date DESC limit 100
     108   }
     109   
    58110
    59111   private function queryPostMeta()
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/Queue/Queue.php

    r3346588 r3363822  
    99use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
    1010use ShortPixel\Controller\CacheController as CacheController;
     11use ShortPixel\Controller\Optimizer\OptimizeAiController;
    1112use ShortPixel\Controller\ResponseController as ResponseController;
    1213use ShortPixel\Model\Converter\Converter as Converter;
     
    4849
    4950   
    50     public function createNewBulk()
     51    public function createNewBulk($args = [])
    5152    {
    5253                $this->resetQueue();
     
    5859        $cache = new CacheController();
    5960        $cache->deleteItem($this->cacheName);
     61
     62        $this->setCustomBulk($args);
    6063    }
    6164
     
    98101       $args = wp_parse_args($args, $defaults);
    99102
    100 
    101103       $qItem = QueueItems::getImageItem($imageModel);
    102 
    103104
    104105             $result = new \stdClass;
     
    107108       $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
    108109
     110       $this->checkQueueCache($imageModel->get('id'));
     111
    109112       $result->qstatus = $this->getQStatus($numitems);
    110113       $result->numitems = $numitems;
     
    117120    {
    118121      $this->q->addItems([$qItem->returnEnqueue()], false);
     122      $item_id = $qItem->item_id;
    119123      $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
    120124
     
    123127      $result->numitems = $numitems;
    124128
    125       do_action('shortpixel_start_image_optimisation', $qItem->item_id, $qItem->imageModel);
     129      $this->checkQueueCache($item_id);
     130     
     131
     132      do_action('shortpixel_start_image_optimisation', $item_id, $qItem->imageModel);
    126133      return $result;
    127134    }
     
    270277              }
    271278             
     279                $optimizeAiController = OptimizeAiController::getInstance();
     280
    272281                // If autoAi is on the bulk, add operation to the item
    273                 if ('media' === $mediaItem->get('type') && true === $settings->autoAIBulk)
     282                $enqueueAi = false;
     283                if ('media' === $mediaItem->get('type') && true === $optimizeAiController->isAiEnabled() && true === $settings->autoAIBulk)
    274284                {
    275 
    276                   $aiDataModel = new AiDataModel($mediaItem->get('id'));
    277                   $enqueueAi = false;
    278                   if ($aiDataModel->isProcessable() && in_array($mediaItem->getExtension(), $aiDataModel->supportedExtensions()))
    279                   {
    280                     $enqueueAi = true;
    281                   }
     285                  $aiDataModel = AiDataModel::getModelByAttachment($mediaItem->get('id')); 
     286                  $enqueueAi = $aiDataModel->isProcessable();
    282287                }
    283                 else
    284                 {
    285                    $enqueueAi = false;
    286                 }
    287 
    288288
    289289                if ($mediaItem->isProcessable() && $mediaItem->isOptimizePrevented() === false && ! $operation) // Checking will be done when processing queue.
     
    317317                                        $baseCount += $counts->baseCount; // base images (all minus webp/avif)
    318318
     319                   
     320                    $this->checkQueueCache($item_id);
    319321                    do_action('shortpixel_start_image_optimisation', $mediaItem);
    320322
     
    511513    }
    512514
    513     public function setCustomBulk($type = null, $options = array() )
    514     {
    515         if (is_null($type))
     515    public function setCustomBulk($options = [] )
     516    {
     517        if (0 === count($options))
    516518          return false;
    517519
    518520        $customData = $this->getStatus('custom_data');
    519         $customData->customOperation = $type;
     521
     522
     523        if (isset($options['customOp']))
     524        {
     525           $customOp = $options['customOp'];   
     526           $customData->customOperation = $customOp;
     527           unset($options['customOp']);
     528        }
     529
    520530        if (is_array($options) && count($options) > 0)
    521531          $customData->queueOptions = $options;
     
    613623                return false;
    614624        }
     625
     626    protected function checkQueueCache($item_id)
     627    {
     628
     629     
     630      if (isset(self::$isInQueue[$item_id]) && false === self::$isInQueue[$item_id])
     631      {
     632         unset(self::$isInQueue[$item_id]);
     633      }
     634
     635
     636
     637
     638    }
    615639
    616640    public function itemFailed(QueueItem $qItem, $fatal = false)
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/QueueController.php

    r3346588 r3363822  
    6363        'smartcrop' => null,
    6464        'next_actions' => [],
     65        'returndatalist' => [],
    6566      );
    6667      $args = wp_parse_args($args, $defaults);
     
    7273      {
    7374         $qItem->data()->next_actions = $args['next_actions'];
     75      }
     76
     77      if (is_object($args['returndatalist']))
     78      {
     79         $args['returndatalist'] = (array) $args['returndatalist'];
     80      }
     81      if (is_array($args['returndatalist']) && count($args['returndatalist']) > 0)
     82      {
     83         $qItem->data()->returndatalist = $args['returndatalist'];
    7484      }
    7585
     
    198208        // @todo This queueItem should maybe not to stuffed with 'addresult'm since it's a different object.
    199209          $queueItem = $q->getItem($mediaItem->get('id'));
     210         
    200211          if (is_object($queueItem))
    201212          {
     
    327338  {
    328339    $result = $Q->run();
    329     $results = [];
    330340    $fs = \wpSPIO()->filesystem();
    331341
     
    337347    $qtype = strtolower($qtype);
    338348
    339     //Log::addTemp('RunTick Items - ', $items);
    340349    /* Only runs if result is array, dequeued items.
    341350       Item is a MediaItem subset of QueueItem
     
    346355          $action = $qItem->data()->action;
    347356          $apiController = $qItem->getAPIController($action);
     357          $send_to_processing = true;
    348358
    349359
     
    374384          }
    375385         
    376           if (! is_object($imageModel))
     386          if (! is_object($imageModel)) // Error in loading imageModel, can't process this.
    377387          {
    378388            Log::addWarn('ImageObject was empty when send to processing - ' . $item_id);
    379389            $qItem->addResult([
    380390                'apiStatus' => RequestManager::STATUS_NOT_API,
    381                 'message' => __("File Error. File could not be loaded with this ID ", 'shortpixel-image-optimiser'),
     391                'message' => __("File Error. Media Item could not be loaded with this ID ", 'shortpixel-image-optimiser'),
    382392                'fileStatus' => ImageModel::FILE_STATUS_ERROR,
    383393                'is_done' => true,
    384394                'is_error' => true,
    385395            ]);
     396            $Q->itemFailed($qItem, true);
     397            $send_to_processing = false;
    386398          }
    387399          elseif(true === $qItem->block())
     
    392404            ]);
    393405            Log::addWarn('Encountered blocked item, processing success? ', $item_id);
     406            ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
     407            $send_to_processing = false;
    394408          }
    395409          else
     
    403417            ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
    404418          }
    405 
    406           ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
    407 
    408419       
    409420          $this->setLastID($item_id);
    410421
    411           if (! is_null($apiController))
     422          if (! is_null($apiController) && true === $send_to_processing)
    412423          {
    413424            $apiController->sendToProcessing($qItem);
     
    821832    $item_id = $qItem->item_id;
    822833    $responseItem = ResponseController::getResponseItem($item_id);
    823     $type = $qItem->imageModel->get('type');
     834
     835    $type = (is_object($qItem->imageModel)) ? $qItem->imageModel->get('type') : false;
     836
     837    if (false === $type)
     838    {
     839      return;
     840    }
    824841
    825842    $fs = \wpSPIO()->filesystem();
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/View/EditMediaViewController.php

    r3348311 r3363822  
    1515
    1616use ShortPixel\Controller\Queue\QueueItems as QueueItems;
    17 
     17use ShortPixel\Model\AiDataModel;
    1818use ShortPixel\Model\File\FileModel as FileModel;
    1919
     
    141141      protected function getStatistics()
    142142      {
    143         //$data = $this->data;
    144         $stats = array();
     143        $stats = [];
    145144        $imageObj = $this->imageModel;
    146145        $did_keepExif = $imageObj->getMeta('did_keepExif');
     
    213212                    }
    214213
     214          $optimizeAiController = OptimizeAiController::getInstance();
     215
    215216
    216217                    $thumbnails = $imageObj->get('thumbnails');
     
    267268                         $debugInfo[] = array(__('To Optimize URLS'),  $urls);
    268269                    }
    269                     if (isset($optimizeData))
    270                     {
    271                          $debugInfo[] = array(__('Optimize Data'), $optimizeData);
    272 
    273                          $queueControl = new QueueController();
    274 
    275                     //   $q = $queueControl->getQueue($imageObj->get('type'));
    276 
    277              $item = QueueItems::getImageItem($imageObj);
     270
     271
     272          $item = QueueItems::getImageItem($imageObj);
     273
     274          if ($imageObj->isProcessable())
     275                    {
     276                        // $queueControl = new QueueController();
     277
     278
    278279             $item->setDebug();
    279280             $item->newOptimizeAction();
     
    281282                         $returnEnqueue = $item->returnEnqueue();
    282283
    283              // TEST @todo REMOVE
    284              /*$replacer2 = new \ShortPixel\Replacer\Replacer();
    285              $setup = $replacer2->Setup();
    286              $setup->forSearch()->URL()->addData($item->imageModel->getURL());
    287              
    288              $base_url = $setup->forSearch()->URL()->getBaseURL();
    289            
    290              $text = 'AI TEST';
    291 
    292              $finder = $replacer2->Finder(['base_url' => $base_url, 'callback' => [OptimizeAiController::getInstance(), 'handleReplace'], 'return_data' => [
    293                  'retrievedText' => $text,
    294                  'qItem' => $item,
    295              ]]);
    296              
    297              $posts = $finder->posts();  */
    298 
    299                          $debugInfo[] = array(__('Image to Queue V2'), $returnEnqueue );
    300 
    301                     }
     284                         $debugInfo[] = array(__('Image to Queue'), $returnEnqueue );
     285
     286                    }
     287
     288          if ( $optimizeAiController->isAIEnabled())
     289          {
     290            $aiDataModel = AiDataModel::getModelByAttachment($this->post_id);
     291
     292            $aiProcessable = ($aiDataModel->isProcessable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> ';
     293
     294            $debugInfo[] = ['AI - is Processable', $aiProcessable];
     295
     296            if (true === $aiDataModel->isProcessable())
     297            {
     298              //$item->requestAltAction();
     299             // $optimizeAiController->parseJsonForQItem($item);
     300              $debugInfo[] = ['Ai - Paramlist ', $aiDataModel->getOptimizeData() ];
     301   //           $debugInfo[] = ['Ai - returnDataList' , $item->data()->returndatalist];
     302             
     303            }
     304            else
     305            {
     306               $debugInfo[] = ['Ai - Reason', $aiDataModel->getProcessableReason()];
     307            }
     308            if (true === $aiDataModel->isSomeThingGenerated())
     309            {
     310              $debugInfo[] = ['Ai -Generated ', $aiDataModel->getGeneratedData()];
     311            }
     312
     313          }
     314
     315
    302316
    303317          $debugInfo['imagemetadata'] = array(__('ImageModel Metadata (ShortPixel)'), $imageObj);
     
    366380
    367381              $display_size = ucfirst(str_replace("_", " ", $size));
    368               //$thumbObj = $imageObj->getThumbnail($size);
    369382
    370383              if ($thumbObj === false)
     
    374387              }
    375388
    376               $url = $thumbObj->getURL(); //$fs->pathToURL($thumbObj); //wp_get_attachment_image_src($this->post_id, $size);
     389              $url = $thumbObj->getURL();
    377390              $filename = $thumbObj->getFullPath();
    378391              $fileDir = $thumbObj->getFileDir();
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/View/ListMediaViewController.php

    r3347742 r3363822  
    1313
    1414use ShortPixel\Controller\ApiKeyController as ApiKeyController;
     15use ShortPixel\Controller\Optimizer\OptimizeAiController;
    1516use ShortPixel\Controller\QuotaController as QuotaController;
    1617use ShortPixel\Controller\QueueController as QueueController;
     
    6061  {
    6162    $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
    62     /*if (true === \wpSPIO()->settings()->enable_ai)
    63     {
    64       $defaults['wp-spio-ai'] = __('AI By Shortpixel', 'shortpixel-image-optimiser');
    65     } */
     63
    6664
    6765    return $defaults;
     
    9997     $list_actions = array();
    10098
    101      if (true === \wpSPIO()->settings()->enable_ai)
     99     $optimizeAiController = OptimizeAiController::getInstance();
     100
     101
     102     if (true === $optimizeAiController->isAiEnabled())
    102103     {
    103104        $aiDataModel = $this->loadAiItem($id);
     
    174175  protected function loadAiItem($item_id)
    175176  {
    176      $AiDataModel = new AiDataModel($item_id);
     177     $AiDataModel = AiDataModel::getModelByAttachment($item_id);
    177178     $this->view->item_id = $item_id;
    178179
  • shortpixel-image-optimiser/tags/6.3.4/class/Controller/View/SettingsViewController.php

    r3348311 r3363822  
    430430          }
    431431
     432         
     433
    432434                    // If the compression type setting changes, remove all queued items to prevent further optimizing with a wrong type.
    433435                    if (intval($this->postData['compressionType']) !== intval($this->model->compressionType))
     
    448450                    {
    449451                            $type = $this->model->getType($name);
    450                             if ('boolean' === $type && ! isset($this->postData[$name]))
     452                            if ('boolean' === $type )
    451453                            {
    452                                  $this->model->{$name} = false;
     454                if( ! isset($this->postData[$name]))
     455                {
     456                                  $this->model->{$name} = false;
     457                }
     458                else
     459                {
     460                   $this->model->{$name} = true;
     461                }
    453462                            }
    454463                    }
     
    864873              }
    865874          }
     875
     876        if (false === isset($post['enable_ai']))
     877        {
     878             if (isset($post['autoAI']))
     879             {
     880                unset($post['autoAI']);
     881             }
     882             if (isset($post['autoAIBulk']))
     883             {
     884                unset($post['autoAIBulk']);
     885             }
     886        }
    866887
    867888       
  • shortpixel-image-optimiser/tags/6.3.4/class/Helper/UiHelper.php

    r3346930 r3363822  
    331331      }
    332332
    333       //$aiDataModel = new AiDataModel($id);
    334 
    335333      if ($mediaItem->isSomethingOptimized() )
    336334      {
     
    439437
    440438
    441       if (false === is_null($aiDataModel) && $aiDataModel->isProcessable() && 'media' === $mediaItem->get('type') && in_array($mediaItem->getExtension(), $aiDataModel->supportedExtensions()))
     439      if (false === is_null($aiDataModel) && $aiDataModel->isProcessable() && 'media' === $mediaItem->get('type') )
    442440      {
    443441         $list_actions['shortpixel-generateai'] = self::getAction('shortpixel-generateai', $id);
  • shortpixel-image-optimiser/tags/6.3.4/class/Helper/UtilHelper.php

    r3346588 r3363822  
    3030  }
    3131
    32   static public function timestampToDB($timestamp)
     32  public static function timestampToDB($timestamp)
    3333  {
    3434    return date("Y-m-d H:i:s", $timestamp);
    3535  }
    3636
    37   static public function DBtoTimestamp($date)
     37  public static function DBtoTimestamp($date)
    3838  {
    3939    return strtotime($date);
     
    170170    }
    171171    return $bool;
     172  }
     173
     174  public static function getAiSettings($params = [])
     175  {
     176    $settings = \wpSPIO()->settings();
     177
     178    $defaults = [
     179    'ai_general_context' => $settings->ai_general_context,
     180    'ai_use_post' => $settings->ai_use_post,
     181    'ai_gen_alt' => $settings->ai_gen_alt,
     182    'ai_gen_caption' => $settings->ai_gen_caption,
     183    'ai_gen_description' => $settings->ai_gen_description,
     184    'ai_filename_prefercurrent' => $settings->ai_filename_prefercurrent,
     185    'ai_limit_alt_chars' => $settings->ai_limit_alt_chars,
     186    'ai_alt_context' => $settings->ai_alt_context,
     187    'ai_limit_description_chars' => $settings->ai_limit_description_chars,
     188    'ai_description_context' => $settings->ai_description_context,
     189    'ai_limit_caption_chars' => $settings->ai_limit_caption_chars,
     190    'ai_caption_context' => $settings->ai_caption_context,
     191    'ai_gen_filename' => $settings->ai_gen_filename,
     192    'ai_limit_filename_chars' => $settings->ai_limit_filename_chars,
     193    'ai_filename_context' => $settings->ai_filename_context,
     194    'ai_use_exif' => $settings->ai_use_exif,
     195    'ai_language' => $settings->ai_language,
     196    'aiPreserve' => $settings->aiPreserve,
     197    ];
     198
     199    $params = wp_parse_args($params, $defaults);
     200
     201    return $params;
    172202  }
    173203
  • shortpixel-image-optimiser/tags/6.3.4/class/Model/AiDataModel.php

    r3346588 r3363822  
    1414class AiDataModel
    1515{
    16 
    1716    protected $id;
    1817    protected $attach_id;
    1918    protected $type;
     19
     20    protected static $models = [];
    2021
    2122    protected $original = [
     
    4546    private $current_is_set = false;
    4647
     48    private $processable_status = 0;
    4749
    4850    const TYPE_MEDIA = 1;
    4951    const TYPE_CUSTOM = 2;
    5052
     53    // Status for the whole image, in the main table.
    5154    const AI_STATUS_NOTHING = 0;
    5255    const AI_STATUS_GENERATED = 1;
     56
     57    // IsProcessable statii
     58    const P_PROCESSABLE = 0;
     59    const P_ALREADYDONE = 1;  // Data already there
     60    const P_EXIFAI = 2;  // When Exif Flag forbids AI doing
     61    const P_EXTENSION = 3;
     62    const P_NOJOB = 4;
     63    const P_NOFIELDS = 5;
     64
     65    // Descriptive status if certain field is not generated / left alone.
     66    const F_STATUS_OK = 1;
     67    const F_STATUS_EXCLUDESETTING = -3;
     68    const F_STATUS_PREVENTOVERRIDE = -4;
     69   
    5370
    5471    public function __construct($attach_id, $type = 'media')
     
    94111        $this->generated = array_merge($this->generated, $generatedData);
    95112
    96 
    97113    }
    98114
     
    108124
    109125        return (array) $data;
     126
     127    }
     128
     129    /** Get all data needed to send API for generating AI texts, depending on settings. This includes all settings minus URL
     130     *
     131     * @return array{paramlist: array<string, array{context: mixed, chars: mixed}>, returndatalist: array<string, array<string, int>>}
     132     */
     133    public function getOptimizeData($params = [])
     134    {
     135        $settings = (object) UtilHelper::getAiSettings($params);
     136
     137        $ignore_fields = [];
     138        if (true === $settings->aiPreserve)
     139        {
     140            $currentData = $this->getCurrentData();
     141            $ignore_fields = array_keys(array_filter($currentData));
     142        }
     143
     144
     145       // $fields = ['ai_gen_alt', 'ai_gen_caption', 'ai_gen_description', 'ai_gen_filename'];
     146        $fields = ['alt', 'caption', 'description', 'filename'];
     147
     148        $paramlist = [
     149            'languages' => $settings->ai_language,
     150            'context' => $settings->ai_general_context,
     151        ];
     152        $returnDataList = [];
     153        $field_status = false; // check if there are any fields to process / not all excluded.
     154
     155        foreach($fields as $field_name)
     156        {
     157            $api_name = $field_name;
     158            //$paramlist[$api_name] = [];
     159
     160            switch($api_name)
     161            {
     162                case 'description':
     163                    $api_name = 'image_description';
     164                break;
     165                case 'filename':
     166                    $api_name = 'file';
     167                break;
     168            }
     169
     170
     171            if (false === $settings->{'ai_gen_' . $field_name})
     172            {
     173                $returnDataList[$field_name]['status'] = self::F_STATUS_EXCLUDESETTING;
     174                continue;
     175            }
     176            elseif (true === in_array($field_name, $ignore_fields))
     177            {
     178                $returnDataList[$field_name]['status'] = self::F_STATUS_PREVENTOVERRIDE;
     179            }
     180            else
     181            {
     182                $paramlist[$api_name] = [
     183                        'context' => $settings->{'ai_' . $field_name . '_context'},
     184                        'chars' => $settings->{'ai_limit_' . $field_name . '_chars'},
     185                ];
     186                $returnDataList[$field_name]['status']  = self::F_STATUS_OK;
     187                $field_status = true;
     188            }
     189
     190           
     191        }
     192
     193        if (false === $field_status)
     194        {
     195            $this->processable_status = self::P_NOJOB;
     196        }
     197
     198        return ['paramlist' => $paramlist, 'returndatalist' => $returnDataList];
    110199
    111200    }
     
    143232        }
    144233
    145 
    146234        // Save to WordPress
    147         /*
    148         if (isset($this->generated['alt']) && false !== $this->generated['alt'])
    149         {
    150             $bool = update_post_meta($this->attach_id, '_wp_attachment_image_alt', $this->generated['alt']);
    151         } */
    152 
    153235        $this->updateWPPost($this->generated);
    154236        $this->updateWpMeta($this->generated);
    155237
    156 /*        $post = get_post($this->attach_id);
    157         $post_updated = false;
    158 
    159         if (isset($this->generated['caption']) && false !== $this->generated['caption'])
    160         {
    161             $post->post_excerpt = $this->generated['caption'];
    162             $post_updated = true;
    163         }
    164 
    165         if (isset($this->generated['description']) && false !== $this->generated['description'])
    166         {
    167             $post->post_content = $this->generated['description'];
    168             $post_updated = true;
    169         }
    170 
    171         if (true === $post_updated)
    172         {
    173             wp_update_post($post);
    174         } */
    175238    }
    176239
    177240    protected function updateWPPost($data)
    178241    {
    179      
    180       //  Log::addTemp('Update WpPost', $data);
    181242        $post = get_post($this->attach_id);
    182243        $post_updated = false;
    183244
    184         if (isset($data['caption']) && false !== $data['caption'])
     245        if (isset($data['caption']) && false !== $data['caption'] && false === is_int($data['caption']))
    185246        {
    186247            $post->post_excerpt = $data['caption'];
     
    188249        }
    189250
    190         if (isset($data['description']) && false !== $data['description'])
     251        if (isset($data['description']) && false !== $data['description'] && false === is_int($data['description']))
    191252        {
    192253            $post->post_content = $data['description'];
     
    203264    {
    204265        Log::addTemp('Update WpMeta', $data);
    205         if (isset($data['alt']) && false !== $data['alt'])
     266        if (isset($data['alt']) && false !== $data['alt'] && false === is_int($data['alt']))
    206267        {
    207268            $bool = update_post_meta($this->attach_id, '_wp_attachment_image_alt', $data['alt']);
     
    314375        if (true === $this->has_record)
    315376        {
     377             $this->processable_status = SELF::P_ALREADYDONE;
    316378             return false;
    317379        }
     380
     381        // Stash here other conditions on top with && to build a big processable function
     382        $processable = ( $this->isExifProcesssable() && $this->isExtensionIncluded() && $this->hasSomethingGeneratable() ) ? true : false;
     383        return $processable;
     384    }
     385
     386
     387    private function isExifProcesssable()
     388    {
     389        $fs = \wpSPIO()->filesystem();
     390        $imageModel = $fs->getMediaImage($this->attach_id);
     391
     392        if (false === $imageModel->isSomethingOptimized())
     393        {
     394            return true;
     395        }
     396
     397        $imageObj = $imageModel->getSomethingOptimized();
     398       
     399
     400        $keepExif = $imageObj->getMeta('did_keepExif');
     401
     402        // 2-3 are exif_ai combined settings with keep-exif. 0-1 are when default settings are used and unset / unused
     403        if (in_array($keepExif, [0,1,2,3]))
     404        {
     405            return true;
     406        }
     407
     408        $this->processable_status = self::P_EXIFAI;
     409        return false;
     410
     411    }
     412
     413    public function getProcessableReason($returnStatus = false )
     414    {
     415        $message = false;
     416       
     417        if (true === $returnStatus)
     418        {
     419            return $this->processable_status;
     420        }
     421
     422        switch($this->processable_status)
     423        {
     424            case self::P_PROCESSABLE:
     425                $message = __('AI is processable', 'shortpixel-image-optimiser');
     426            break;
     427            case self::P_ALREADYDONE:
     428                $message = __('This image already has generated data', 'shortpixel-image-optimiser');
     429            break;
     430            case self::P_EXIFAI:
     431                $message = __('Image Exif settings restrict AI usage', 'shortpixel-image-optimiser');
     432            break;
     433            case self::P_EXTENSION:
     434                 $message = __('File Extension not supported', 'shortpixel-image-optimiser');
     435            break;
     436            case self::P_NOJOB:
     437                $message = __('No fields to generate', 'shortpixel-image-optimiser');
     438            break;
     439            default:
     440                 $message = sprintf(__('Status %s unknown', 'shortpixel-image-optimiser'), $this->processable_status);
     441            break;
     442        }
     443
     444        return $message;
     445    }
     446
     447    protected function isExtensionIncluded()
     448    {
     449        $fs = \wpSPIO()->filesystem();
     450        $imageModel = $fs->getMediaImage($this->attach_id);
     451       
     452        // Gif removed here, since we (temporarily don't support it)
     453        $extensions = ['png', 'jpeg', 'webp', 'jpg'];
     454
     455        if (in_array($imageModel->getExtension(), $extensions))
     456        {
     457            return true;
     458        }
     459
     460        $this->processable_status = self::P_EXTENSION;
     461        return false;
     462    }
     463
     464    protected function hasSomethingGeneratable()
     465    {
     466        $optimizeData = $this->getOptimizeData();
     467
     468        if (self::P_NOJOB === $this->processable_status)
     469        {
     470             return false;
     471
     472        }
    318473        return true;
    319     }
    320 
    321     public function supportedExtensions()
    322     {
    323          return ['png', 'jpeg', 'gif', 'webp', 'jpg'];
    324474    }
    325475
     
    429579
    430580   
    431     /*public static function getAiDataByAttachment($attach_id)
    432     {
    433 
    434     } */
     581    public static function getModelByAttachment($attach_id, $type = 'media')
     582    {
     583        if (false === isset(self::$models[$attach_id]))
     584        {
     585             self::$models[$attach_id]  = new AiDataModel($attach_id, $type);
     586        }
     587
     588        return self::$models[$attach_id];
     589
     590    }
     591
     592    public static function flushModelCache($attach_id, $type = 'media')
     593    {
     594        if (isset(self::$models[$attach_id]))
     595        {
     596             unset(self::$models[$attach_id]);
     597        }
     598        else
     599        {
     600             Log::addTemp('Ai MODEL not found in cache!', $attach_id);
     601        }
     602
     603    }
    435604
    436605
  • shortpixel-image-optimiser/tags/6.3.4/class/Model/Image/ImageModel.php

    r3346588 r3363822  
    478478                if (! isset($optimizeData['params']) || ! isset($optimizeData['urls']))
    479479                {
    480                     array(array(), 0);
     480                    array([], 0);
    481481                }
    482482
    483483                $count = 0;
    484                 $urls = array();
     484                $urls = [];
    485485                $i = 0;
    486486
  • shortpixel-image-optimiser/tags/6.3.4/class/Model/Queue/QueueItem.php

    r3346588 r3363822  
    1717use ShortPixel\Controller\Optimizer\OptimizeAiController as OptimizeAiController;
    1818use ShortPixel\Controller\Optimizer\ActionController as ActionController;
     19use ShortPixel\Model\AiDataModel;
    1920
    2021class QueueItem
     
    176177      }
    177178
    178 
    179179      $enqueue = ['id' => $item_id, 'value' => $value, 'item_count' => $this->item_count];
    180180     
     
    185185
    186186      return $enqueue;
    187 
    188187     
    189188   }
     
    472471      $this->item_count = 1;
    473472
     473      $item_id = $this->imageModel->get('id');
     474
     475      $paramlist = [];
    474476
    475477      $preview_only = false;
    476478      if (isset($args['preview_only']) && true == $args['preview_only'])
    477479      {
    478          $this->data->paramlist = ['preview_only' => true];
     480         $paramlist['preview_only'] = true;
    479481         $preview_only = true;
    480482      }
    481483
     484      $aiDataModel = new AiDataModel($item_id);
     485     
     486      $data = $aiDataModel->getOptimizeData($args);
     487
     488      if (isset($data['paramlist']))
     489      {
     490         $this->data()->paramlist = $data['paramlist'];
     491      }
     492      if (isset($data['returndatalist']))
     493      {
     494         $this->data()->returndatalist = $data['returndatalist'];
     495         $this->data()->addKeepDataArgs('returndatalist');
     496      }
     497
     498
    482499      $this->data->action = 'requestAlt'; // For Queue
    483500
    484       $optimizer = $this->getAPIController($this->data->action);
    485       $optimizer->parseJSONForQItem($this, $args);
     501    //  $optimizer = $this->getAPIController($this->data->action);
     502   //   $optimizer->parseJSONForQItem($this, $args);
    486503
    487504      if ($this->data()->hasNextAction())
     
    498515         $this->data->next_actions = $next_actions;
    499516      }
     517
     518
    500519     
    501520   }
    502521
    503    public function retrieveAltAction($remote_id)
     522   public function retrieveAltAction($args)
    504523   {
    505524      $this->newAction();
     525
     526      $remote_id = $args['remote_id'];
     527     
     528      if (isset($args['returndatalist']))
     529      {
     530         $this->data()->returndatalist = $args['returndatalist'];
     531      }
     532
     533
    506534      $this->data->remote_id = $remote_id;
    507535      $this->data->tries = 0;
  • shortpixel-image-optimiser/tags/6.3.4/class/Model/SettingsModel.php

    r3348311 r3363822  
    6868        'autoAI' => ['s' => 'boolean', 'default' => false],
    6969        'autoAIBulk' => ['s' => 'boolean', 'default' => false],
     70        'aiPreserve' => ['s' => 'boolean', 'default' => false ],
    7071        'ai_general_context' => ['s' => 'string', 'default' => 'callback', 'maxlength' => 500],
    7172        'ai_use_post' => ['s' => 'boolean', 'default' => true],
  • shortpixel-image-optimiser/tags/6.3.4/class/external/offload/wp-offload-media.php

    r3289850 r3363822  
    355355                    }
    356356                }
    357             }
    358         }
    359 
     357
     358            }
     359
     360        }
    360361
    361362        return $source_id;
  • shortpixel-image-optimiser/tags/6.3.4/class/external/wp-cli/wp-cli-bulk.php

    r3289850 r3363822  
    167167     * [--special=<migrate>]
    168168     * : Run the migration
    169      * ---
    170      *
     169     *
     170     * [--start-date=<start_date>]
     171     * : Filter, start from this date
     172     *
     173     * [--end-date=<end_date>]
     174     * : Filter, don't enqueue items old than this date.
     175     *
    171176     * ## EXAMPLES
    172177     *
     
    186191
    187192        $operation = null;
     193        $args = $filters = [];
    188194        if (isset($assoc['special'])) {
    189195            switch ($assoc['special']) {
    190196                case 'migrate':
    191197                    $operation = 'migrate';
     198                    $args['customOp'] = $operation;
    192199                    $queues = array('media'); // can only have one bulk, this.
    193200                    break;
     
    195202        }
    196203
     204        if (isset($assoc['start-date']))
     205        {
     206             $filters['start_date'] = sanitize_text_field($assoc['start-date']);
     207        }
     208        if (isset($assoc['end-date']))
     209        {
     210             $filters['end_date'] = sanitize_text_field($assoc['end-date']);
     211        }
     212
     213        if (count($filters) > 0)
     214        {
     215             $args['filters'] = $filters;
     216        }
     217
    197218        foreach ($queues as $qname) {
    198             $stats = $bulkControl->createNewBulk($qname, $operation);
     219            $stats = $bulkControl->createNewBulk($qname, $args);
    199220            $json->$qname->stats = $stats;
    200221
  • shortpixel-image-optimiser/tags/6.3.4/class/view/bulk/part-selection.php

    r3346588 r3363822  
    11<?php
    22namespace ShortPixel;
     3
     4use ShortPixel\Controller\Optimizer\OptimizeAiController;
    35
    46if ( ! defined( 'ABSPATH' ) ) {
     
    4547
    4648
    47        </div>
     49      </div>
    4850
    4951       <div class="interface wrapper">
     
    103105                <?php endif; ?>
    104106
    105                 <?php if (true === \wpSPIO()->settings()->enable_ai):  ?>
     107                <?php
     108                $optimizeAiController = OptimizeAiController::getInstance();
     109                if (true === $optimizeAiController->isAiEnabled()):  ?>
    106110             <div class='ai-images optiongroup'>
    107111                <div class='switch_button'>
     
    118122                </div> 
    119123
     124                <div class='switch_button'>
     125                <label>
     126                       <input type="checkbox" class="switch" id="aipreserve_checkbox" name="aipreserve_checkbox"
     127                        <?php checked(\wpSPIO()->settings()->aiPreserve); ?>  />
     128                       <div class="the_switch">&nbsp; </div>
     129                 </label>
     130                 <h4><label for="aipreserve_checkbox">
     131                    <?php printf(esc_html__('Prevent overriding any of the existing data with the one generated by AI', 'shortpixel-image-optimiser'), '<a href="options-general.php?page=wp-shortpixel-settings&part=ai">', '</a>' ); ?>
     132                            <span class='new'><?php _e('New!', 'shortpixel-image-optimiser'); ?></span>
     133                 </label></h4>
     134
     135                </div> 
     136
    120137             </div>
     138
    121139            <?php endif ?>
    122140           
  • shortpixel-image-optimiser/tags/6.3.4/class/view/settings/part-ai.php

    r3348311 r3363822  
    1212  <settinglist>
    1313
    14     <h2><?php esc_html_e('AI Image SEO', 'shortpixel-image-optimiser'); ?></h2>
     14    <h2><?php esc_html_e('AI Image SEO & Accessibility', 'shortpixel-image-optimiser'); ?></h2>
    1515
    1616    <gridbox class="width_half">
     
    2424            'checked' => $view->data->enable_ai,
    2525            'label' => esc_html__('Enable AI Image SEO', 'shortpixel-image-optimiser'),
    26           ]
     26            'data' => ['data-toggle="autoAiOptions"'],
     27
     28            ]
    2729        );
    2830        ?>
     
    3133        <name>
    3234
    33           <?php esc_html_e('Show AI image SEO options throughout ShortPixel Image Optimizer.', 'shortpixel-image-optimiser'); ?>
     35          <?php esc_html_e('Show AI image SEO options throughout ShortPixel Image Optimizer. The generated ALT tag is also very useful for accessibility.', 'shortpixel-image-optimiser'); ?>
    3436
    3537        </name>
     
    3739    </setting>
    3840
    39     <setting class='switch'>
     41    <setting class='switch toggleTarget autoAiOptions'>
    4042      <content>
    4143
     
    5860    </setting>
    5961
    60     <setting class='switch'>
     62    <setting class='switch toggleTarget autoAiOptions'>
    6163      <content>
    6264
     
    6567            'name' => 'autoAIBulk',
    6668            'checked' => $view->data->autoAIBulk,
    67             'label' => esc_html__('Generate image SEO data when running Bulk Processing', 'shortpixel-image-optimiser'),
     69            'label' => esc_html__('Generate image SEO data during Bulk Processing', 'shortpixel-image-optimiser'),
    6870          ]
    6971        );
     
    7880      </content>
    7981    </setting>
     82
     83
     84    <setting class='switch toggleTarget autoAiOptions'>
     85      <content>
     86
     87        <?php $this->printSwitchButton(
     88          [
     89            'name' => 'aiPreserve',
     90            'checked' => $view->data->aiPreserve,
     91            'label' => esc_html__('Preserve existing Image SEO data', 'shortpixel-image-optimiser'),
     92          ]
     93        );
     94        ?>
     95        <i class='documentation dashicons dashicons-editor-help' data-link="https://shortpixel.com/knowledge-base/article/ai-image-seo-settings-explained/#2-toc-title?target=iframe"></i>
     96
     97        <name>
     98
     99          <?php esc_html_e('When enabled, all existing ALT tags, captions and descriptions are retained. Disabling the switch means that the SEO data for images created with AI will overwrite the existing data.', 'shortpixel-image-optimiser'); ?>
     100
     101        </name>
     102      </content>
     103    </setting>
     104
    80105
    81106    </gridbox>
  • shortpixel-image-optimiser/tags/6.3.4/class/view/snippets/part-aitext.php

    r3346588 r3363822  
    33use ShortPixel\Controller\ApiKeyController;
    44use ShortPixel\Controller\QuotaController;
     5use ShortPixel\Model\AiDataModel;
    56
    67$icon_url = plugins_url( '/res/images/icon/', SHORTPIXEL_PLUGIN_FILE );
     
    1011$item_id = $this->data['item_id'];
    1112$has_data = $this->data['has_data'];
    12 $isSupported = $this->data['isSupported'];
     13//$isSupported = $this->data['isSupported'];
    1314$isDifferent = $this->data['isDifferent'];
    1415$dataItems = implode(',',$this->data['dataItems']);
     16
     17$is_processable = $this->data['is_processable'];
     18$processable_reason = $this->data['processable_reason'];
     19$processable_status = $this->data['processable_status'];
    1520
    1621$quotaControl = QuotaController::getInstance();
     
    2328    <p class='hidden' id='shortpixel-noai'></p>
    2429<?php
     30endif;
    2531
    26 elseif (false === $isSupported):
     32if (false === $is_processable && $processable_status !== AiDataModel::P_ALREADYDONE):
    2733
    28     ?>
    29         <p><?php _e('Currently, ShortPixel AI cannot generate SEO data for GIF files.', 'shortpixel-image-optimiser'); ?></p>
    30     <?php
     34    if (true === in_array($processable_status, [AiDataModel::P_NOJOB])) // Silent fail if all done
     35    {
     36
     37    }
     38    else
     39    {
     40        printf('<p>%s</p>', $processable_reason);
     41    }
     42   
    3143
    3244elseif (false === $has_data):
  • shortpixel-image-optimiser/tags/6.3.4/readme.txt

    r3348311 r3363822  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 6.3.3
     7Stable tag: 6.3.4
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    474474
    475475== Changelog ==
     476
     477= 6.3.4 =
     478
     479🤖 The Smarter AI Control Update
     480
     481Release Date: September 18, 2025
     482
     483✨ New Features & Improvements
     484
     485* Preserve Existing SEO Data: Added an option to keep your existing ALT, caption, and description fields untouched when using AI-generated image SEO — your manual work is safe!
     486* Better Block Editor Integration: AI-generated SEO data and image optimization now work flawlessly when uploading images directly from the block editor.
     487* AI Support Chatbot: Meet our new AI-powered support assistant — here to help you faster, 24/7.
     488
     489🛠️ Fixes & Behavioral Improvements
     490
     491* Full AI Deactivation: Disabling AI Image SEO now also disables auto-generation and hides all related options from WP Admin, including the Bulk Processing section.
     492* EXIF-Based AI Training Block: If AI training is blocked via EXIF data, the plugin now shows a clear message and prevents the image from being sent for processing.
     493* Re-Optimize Logic Fixed: The "Re-optimize with/without SmartCrop" buttons now respect the selected option instead of always using the saved setting.
     494* Inline Font Handling: Inline fonts will no longer be replaced with CDN links if the CSS option is disabled.
     495* AI SEO for Excluded Items: Bulk AI SEO generation now skips excluded items as expected.
     496
     497Update now for smarter AI behavior, more control over your data, and an improved support experience! 🚀
    476498
    477499= 6.3.3 =
  • shortpixel-image-optimiser/tags/6.3.4/res/css/shortpixel-bulk.css

    r3348311 r3363822  
    6363}
    6464.wrap.is-shortpixel-bulk-page header .top-buttons a:hover, .wrap.is-shortpixel-bulk-page header .top-buttons button:hover {
    65   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     65  background: #32d7e5;
    6666}
    6767.wrap.is-shortpixel-bulk-page header .top-buttons a i.switch, .wrap.is-shortpixel-bulk-page header .top-buttons button i.switch {
     
    265265}
    266266.wrap.is-shortpixel-bulk-page section.panel.dashboard .bulk-wrapper button:hover {
    267   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     267  background: #32d7e5;
    268268}
    269269.wrap.is-shortpixel-bulk-page section.panel.dashboard .bulk-wrapper button:disabled {
     
    398398}
    399399.wrap.is-shortpixel-bulk-page .button-primary:hover {
    400   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     400  background: #32d7e5;
    401401}
    402402.wrap.is-shortpixel-bulk-page nav {
     
    605605}
    606606.wrap.is-shortpixel-bulk-page .processor-paused:hover {
    607   background: rgb(27.5926724138, 169.1163793103, 178.9073275862);
     607  background: #1ca9b3;
    608608}
    609609.wrap.is-shortpixel-bulk-page .processor-paused .dashicons {
     
    719719  opacity: 1;
    720720  height: auto;
     721  z-index: 10;
     722  position: relative;
    721723}
    722724.wrap.is-shortpixel-bulk-page section.panel.selection .load.wrapper .loading {
     
    12451247}
    12461248.wrap.is-shortpixel-bulk-page section.panel.finished nav button.finish:hover {
    1247   background: rgb(36.2392241379, 209.2887931034, 221.2607758621);
     1249  background: #24d1dd;
    12481250}
    12491251.wrap.is-shortpixel-bulk-page .part-debug {
  • shortpixel-image-optimiser/tags/6.3.4/res/css/shortpixel-bulk.css.map

    r3348311 r3363822  
    1 {"version":3,"sourceRoot":"","sources":["../scss/elements/_fonts.scss","../scss/shortpixel-bulk.scss","../scss/bulk/_dashboard.scss","../scss/elements/_header.scss","../scss/elements/_mixins.scss","../scss/elements/_colors.scss","../scss/elements/_icons.scss","../scss/elements/_breakpoints.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAEA;EAEI;EACA;EACA;EACA;;AAGJ;EAGI;EACA;EACA;EACA;;ACXJ;ACLA;ADgBE;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmcE;AAiIF;;AEplBF;EAEI;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACH;;AAEM;EC5BL;EACA,YCKa;EDJb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACC;;AAKD;EACE;EACA;;AAEF;EAEE;EACA;EACA;;ADOM;EACE,QEXG;EFYH;EACA;EACP;EACA;;AG5BL;EAEE;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;;AACA;EACG;;AAOH;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AAIJ;EAGE;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;EACA;;AAGJ;EACE;EACA;EACA;;AJtDA;EAEG;EACA;EACA;;AAEA;EAAa;;AAIb;EAGK;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EACA,kBGtBA;EHwBA;EACA;EACA;EACP;EAEA;EACA;EACM;;AAEA;EACE;;AAID;EACE;EACA;;AAEF;EAEN;;AAML;EAGC,kBG1DS;EH2DT;;AACK;EACE;EACA;EACA;EACA;EACA;;AAKR;EACE;EACD;;AK7DD;EL2DA;IAIE;;;AAED;EACC;;AAIC;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;ADzFb;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAIF;EAEG;EACA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YItDiB;EJuDjB,OI1DU;EJ2DV;EACA;EACA;EACS;EACA;EACA;EACT;;AAEA;EACC,YIpES;EJqET;;AAGD;EAEC,YI1ES;;AJ4EV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YIvFS;EJwFT;EACA;EACA;EACA;;AACA;EAEE;;AAMN;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AACA;EACE;;AAIJ;EAAI;EAAuB;;AAK5B;EAGI;EACA;EACA;EACP;;AAGO;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACP;EACA;EACA;EACA;;AAEO;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AAQH;EAEI;;AACA;EAEE;EACA;EACA;EACA;EACE;;AAEF;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAEF;EAEE;EACA;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;;AAKG;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EOpgBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;APifnC;EAEE;EACD;;AAGD;EAEE;EACD;EACA;EACA;;AAIH;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAET;EACC;EACA;EACA;;AAGH;EACE;EACA;EACA;;AAIH;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAhJH;;AAiJG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE,YIttBS;EJutBT;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EArSH;;AAsSG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QI5xBD;EJ6xBC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AACL;EAEE;;AAIA;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEE;EACA;;AAKD;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;;AAIN;EAEC;EACA;EACA;;AAGK;EACI;EACA;EACA;EACP;EACA;;AAEO;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AACN;EACE;EACD;;AAMG;EACG;;AAKR;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EApmBH;;AAqmBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
     1{"version":3,"sourceRoot":"","sources":["../scss/elements/_fonts.scss","../scss/shortpixel-bulk.scss","../scss/bulk/_dashboard.scss","../scss/elements/_header.scss","../scss/elements/_mixins.scss","../scss/elements/_colors.scss","../scss/elements/_icons.scss","../scss/elements/_breakpoints.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAEA;EAEI;EACA;EACA;EACA;;AAGJ;EAGI;EACA;EACA;EACA;;ACXJ;ACLA;ADgBE;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmcE;AAmIF;;AEtlBF;EAEI;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACH;;AAEM;EC5BL;EACA,YCKa;EDJb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACC;;AAKD;EACE;EACA;;AAEF;EAEE;EACA;EACA;;ADOM;EACE,QEXG;EFYH;EACA;EACP;EACA;;AG5BL;EAEE;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;;AACA;EACG;;AAOH;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AAIJ;EAGE;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;EACA;;AAGJ;EACE;EACA;EACA;;AJtDA;EAEG;EACA;EACA;;AAEA;EAAa;;AAIb;EAGK;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EACA,kBGtBA;EHwBA;EACA;EACA;EACP;EAEA;EACA;EACM;;AAEA;EACE;;AAID;EACE;EACA;;AAEF;EAEN;;AAML;EAGC,kBG1DS;EH2DT;;AACK;EACE;EACA;EACA;EACA;EACA;;AAKR;EACE;EACD;;AK7DD;EL2DA;IAIE;;;AAED;EACC;;AAIC;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;ADzFb;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAIF;EAEG;EACA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YItDiB;EJuDjB,OI1DU;EJ2DV;EACA;EACA;EACS;EACA;EACA;EACT;;AAEA;EACC,YIpES;EJqET;;AAGD;EAEC,YI1ES;;AJ4EV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YIvFS;EJwFT;EACA;EACA;EACA;;AACA;EAEE;;AAMN;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AACA;EACE;;AAIJ;EAAI;EAAuB;;AAK5B;EAGI;EACA;EACA;EACP;;AAGO;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACP;EACA;EACA;EACA;;AAEO;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AAQH;EAEI;;AACA;EAEE;EACA;EACA;EACA;EACE;;AAEF;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAEF;EAEE;EACA;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;EACK;EACA;;AAKF;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EOtgBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;APmfnC;EAEE;EACD;;AAGD;EAEE;EACD;EACA;EACA;;AAIH;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAET;EACC;EACA;EACA;;AAGH;EACE;EACA;EACA;;AAIH;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAlJH;;AAmJG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE,YIxtBS;EJytBT;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAvSH;;AAwSG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QI9xBD;EJ+xBC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AACL;EAEE;;AAIA;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEE;EACA;;AAKD;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;;AAIN;EAEC;EACA;EACA;;AAGK;EACI;EACA;EACA;EACP;EACA;;AAEO;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AACN;EACE;EACD;;AAMG;EACG;;AAKR;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAtmBH;;AAumBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
  • shortpixel-image-optimiser/tags/6.3.4/res/css/shortpixel-settings.css

    r3347742 r3363822  
    134134}
    135135.wrap.is-shortpixel-settings-page .shortpixel-settings button:hover, .wrap.is-shortpixel-settings-page .shortpixel-settings .button-setting:hover {
    136   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     136  background: #32d7e5;
    137137}
    138138.wrap.is-shortpixel-settings-page .shortpixel-settings button i.switch, .wrap.is-shortpixel-settings-page .shortpixel-settings .button-setting i.switch {
     
    240240}
    241241.wrap.is-shortpixel-settings-page .shortpixel-settings section.wrapper .setting-tab .save-buttons.saving button.save {
    242   background-color: rgb(14.3684210526, 104.4473684211, 111.6315789474);
     242  background-color: #0e6870;
    243243}
    244244.wrap.is-shortpixel-settings-page .shortpixel-settings section.wrapper .setting-tab .save-buttons.saving button.save i {
     
    461461}
    462462.wrap.is-shortpixel-settings-page header .top-buttons a:hover, .wrap.is-shortpixel-settings-page header .top-buttons button:hover {
    463   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     463  background: #32d7e5;
    464464}
    465465.wrap.is-shortpixel-settings-page header .top-buttons a i.switch, .wrap.is-shortpixel-settings-page header .top-buttons button i.switch {
     
    11921192}
    11931193.wrap.is-shortpixel-settings-page #tab-overview .wrapper .dashboard-button:hover {
    1194   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     1194  background: #32d7e5;
    11951195}
    11961196.wrap.is-shortpixel-settings-page #tab-overview .wrapper .dashboard-button i.switch {
     
    21432143  min-height: 44px;
    21442144  color: #1ABDCA;
    2145   background-color: rgb(232.25, 232.25, 232.25);
     2145  background-color: #e8e8e8;
    21462146  border-radius: 6px;
    21472147  font-weight: 700;
     
    21512151}
    21522152.wrap.is-shortpixel-settings-page #tab-tools .button:hover {
    2153   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     2153  background: #32d7e5;
    21542154  color: #fff;
    21552155}
  • shortpixel-image-optimiser/tags/6.3.4/res/js/screens/screen-bulk.js

    r3346588 r3363822  
    261261     data.webpActive = (document.getElementById('webp_checkbox').checked) ? true : false;
    262262     data.avifActive = (document.getElementById('avif_checkbox').checked) ? true : false;
     263     
    263264     if (null !== document.getElementById('autoai_checkbox'))
    264265     {
    265266        data.aiActive = (document.getElementById('autoai_checkbox').checked) ? true : false;
     267        data.aiPreserve = (document.getElementById('aipreserve_checkbox').checked) ? true : false;
    266268     }
    267269     else
    268270     {
    269271       data.aiActive = false;
     272     //  data.aiPreserve = false;
    270273     }
    271274     data.backgroundProcess = (document.getElementById('background_checkbox').checked) ? true : false;
     
    510513            }); // circles;
    511514    }
    512   DoSelection() // action to update response.
    513   {
    514       // @todo Check the future of this function, since checking this is now createBulk.
    515       var data = {screen_action: 'applyBulkSelection'}; //
    516       data.callback = 'shortpixel.applySelectionDone';
    517 
    518       data.mediaActive = (document.getElementById('media_checkbox').checked) ? true : false;
    519       data.customActive = (document.getElementById('custom_checkbox').checked) ? true : false;
    520       data.webpActive = (document.getElementById('webp_checkbox').checked) ? true : false;
    521       data.avifActive = (document.getElementById('avif_checkbox').checked) ? true : false;
    522       data.aiActive = (document.getElementById('autoai_checkbox').checked) ? true : false;
    523       data.backgroundProcess = (document.getElementById('background_checkbox').checked) ? true : false;
    524 
    525       window.addEventListener('shortpixel.applySelectionDone', function (e) { this.SwitchPanel('summary'); }.bind(this) , {'once': true} );
    526       this.processor.AjaxRequest(data);
    527 
    528   }
     515
    529516
    530517  UpdateStats(stats, type)
  • shortpixel-image-optimiser/tags/6.3.4/res/js/screens/screen-item-base.js

    r3346588 r3363822  
    2727        }
    2828
    29 
    3029        // This is final, not more messing with this. In results (multiple) defined one level higher than result object, if single, it's in result.
    3130        var item_id = resultItem.item_id;
    3231        var message = resultItem.message;
    33 
    3432       
    3533        // This is the reporting element ( all the data, via getItemView? )
     
    6664        if ('ai' === apiName && typeof resultItem.aiData !== 'undefined')
    6765        {
    68            
    6966            if (null !== element)
    7067            {
     
    336333        window.addEventListener('shortpixel.HandleUndoAlt', function (event) {
    337334            var data = event.detail.media;
    338             var original = data.original;
     335            var original = data.current;
    339336   
    340337            if ('redo' == action_type)
  • shortpixel-image-optimiser/tags/6.3.4/res/js/screens/screen-media.js

    r3347742 r3363822  
    7676        }
    7777
    78         if (typeof newAltText !== 'undefined')
     78        if (typeof newAltText !== 'undefined' || newAltText < 0)
    7979        {
    8080            var inputs = this.altInputNames;
     
    107107         let captionFields = ['attachment_caption', 'attachment-details-caption'];
    108108         let descriptionFields = ['attachment_content', 'attachment-details-description'];
    109 
    110 
    111109         
    112          if (typeof newCaption !== 'undefined')
     110         if (typeof newCaption !== 'undefined' || newCaption < 0)
    113111         {
    114112            for (var i = 0; i < captionFields.length; i++)
     
    120118                }               
    121119            }
    122 
    123                
    124120         }
    125121
    126          if (typeof newDescription !== 'undefined')
     122         if (typeof newDescription !== 'undefined' || newDescription < 0)
    127123         {
    128124            for (var i = 0; i < descriptionFields.length; i++)
     
    134130                }
    135131            }
    136 
    137 
    138132         }
    139 
    140133
    141134        if (null !== attachmentAlt)
     
    157150            window.addEventListener('shortpixel.AttachAiInterface', this.AttachAiInterface.bind(this), {once: true});
    158151        }
    159     /*  if (typeof aiData !== 'undefined')
    160         {
    161             this.processor.LoadItemView({ id: item_id, type: 'media' });
    162         } */
    163 
    164 
    165152    }
    166153
     
    291278        var res = super.HandleImage(resultItem, type);
    292279        var fileStatus = this.processor.fStatus[resultItem.fileStatus];
     280        var apiName = (typeof resultItem.apiName !== 'undefined') ? resultItem.apiName : 'optimize';
     281
    293282
    294283        // If image editor is active and file is being restored because of this reason ( or otherwise ), remove the warning if this one exists.
     
    299288            }
    300289        }
     290
     291        if (fileStatus == 'FILE_DONE' && apiName == 'ai')
     292        {
     293            this.UpdateGutenBerg(resultItem);
     294        }
    301295    }
    302296
     
    323317    ListenGallery() {
    324318        var self = this;
     319        var next_item_run_process = false;
    325320
    326321        if (this.settings.hide_spio_in_popups)
     
    349344
    350345                if (typeof this.fetchSPIOData === 'function') {
    351                     this.fetchSPIOData(this.model.get('id'));
    352                     this.spioBusy = true; // Note if this system turns out not to work, the perhaps render empties all if first was painted, second cancelled?
    353 
     346                    let attach_id = this.model.get('id');
     347
     348                    if (typeof attach_id !== 'undefined')
     349                    {
     350                        if (true === next_item_run_process )
     351                        {
     352                            window.ShortPixelProcessor.SetInterval(-1);
     353                            window.ShortPixelProcessor.RunProcess();
     354                            next_item_run_process = false;
     355                        }
     356                        else
     357                        {
     358                        this.fetchSPIOData(attach_id);
     359                        this.spioBusy = true; // Note if this system turns out not to work, the perhaps render empties all if first was painted, second cancelled?
     360                        }
     361                    }
     362                    else if (true == this.model.get('uploading'))
     363                    {
     364                        next_item_run_process = true;
     365                        console.log('Upload Start Detected');
     366                    }
     367                    else
     368                    {
     369                        console.log('Id not found on render');
     370                    }
    354371                }
    355372
     
    447464        wrapper.classList.add('shortpixel-ai-interface',element.getAttribute('id'));
    448465       
    449         wrapper.innerHTML = data.snippet;   
     466        wrapper.innerHTML = data.snippet;   
     467
     468
    450469        element.after(wrapper);
    451470
     
    579598    }
    580599
     600    UpdateGutenBerg(resultItem)
     601    {
     602       
     603        var attach_id = resultItem.item_id;
     604        var aiData = resultItem.aiData;
     605       
     606        if (! wp.data || ! wp.data.select('core'))
     607        {
     608            return false;
     609        }
     610
     611        console.log(wp.data.select( 'core/block-editor' ));
     612
     613        let blocks = wp.data.select( 'core/block-editor' ).getBlocks();
     614        console.log(blocks);
     615        for (let i = 0; i < blocks.length; i++)
     616        {
     617            let block = blocks[i];
     618
     619             if (block.attributes.id == attach_id)
     620             {
     621                //block.attributes.alt = "I CAME TO ALT";
     622                //block.attributes.caption = "CAPTION THIS";
     623                let clientId = block.clientId;
     624
     625                console.log('DATA DISPATCH ', clientId, aiData);               
     626                wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes( clientId,
     627                    aiData );
     628
     629             }
     630        }
     631    }
     632
    581633} // class
    582634
  • shortpixel-image-optimiser/tags/6.3.4/res/scss/shortpixel-bulk.scss

    r3348311 r3363822  
    498498           opacity: 1;
    499499                     height: auto;
     500           z-index: 10;
     501           position: relative;
    500502        }
    501503      }
  • shortpixel-image-optimiser/tags/6.3.4/shortpixel-plugin.php

    r3346588 r3363822  
    365365        wp_register_script('shortpixel-media', plugins_url('res/js/shortpixel-media.js',  SHORTPIXEL_PLUGIN_FILE), array('jquery'), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
    366366
    367         wp_register_script('shortpixel-inline-help', plugins_url('res/js/shortpixel-inline-help.js',  SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
     367        wp_register_script('shortpixel-inline-help', plugins_url('res/js/shortpixel-inline-help.js',  SHORTPIXEL_PLUGIN_FILE), [], SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
     368        wp_register_script('shortpixel-chatbot',
     369            apply_filters('shortpixel/plugin/nohelp', 'https://spcdn.shortpixel.ai/assets/js/ext/ai-chat-agent.js'), [], SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
    368370
    369371        // This filter is from ListMediaViewController for the media library grid display, executive script in shortpixel-media.js.
     
    589591            $this->load_script( 'shortpixel-screen-nolist' ); // screen
    590592            $this->load_script( 'shortpixel-settings' );
     593            $this->load_script('shortpixel-chatbot');
    591594
    592595            // @todo Load onboarding only when no api key / onboarding required
     
    599602        } elseif ( $plugin_page == 'wp-short-pixel-bulk' ) {
    600603            $this->load_script( 'shortpixel-screen-bulk' );
     604            $this->load_script('shortpixel-chatbot');
    601605
    602606            $this->load_style( 'shortpixel-admin' );
     
    619623
    620624            $this->load_script( 'shortpixel-folderbrowser' );
     625            $this->load_script('shortpixel-chatbot');
    621626
    622627            $this->load_style( 'shortpixel-admin' );
  • shortpixel-image-optimiser/tags/6.3.4/wp-shortpixel.php

    r3348311 r3363822  
    44 * Plugin URI: https://shortpixel.com/
    55 * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="https://plugins.trac.wordpress.org/wp-admin/options-general.php?page=wp-shortpixel-settings" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
    6  * Version: 6.3.3
     6 * Version: 6.3.4
    77 * Author: ShortPixel - Convert WebP/AVIF & Optimize Images
    88 * Author URI: https://shortpixel.com
     
    3737define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
    3838
    39 define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "6.3.3");
     39define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "6.3.4");
    4040
    4141define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
  • shortpixel-image-optimiser/trunk/class/Controller/AdminController.php

    r3346588 r3363822  
    127127         $queueController = new QueueController();
    128128
    129         if ($optimizeAiController->isAutoAiEnabled())
     129        /* if ($optimizeAiController->isAutoAiEnabled())
    130130         {
    131131            $args = ['action' => 'requestAlt'];
    132132            $queueController->addItemToQueue($mediaItem, $args);
    133          }
     133         } */
    134134                 
    135135         
  • shortpixel-image-optimiser/trunk/class/Controller/AjaxController.php

    r3346588 r3363822  
    690690        $this->checkImageAccess($imageModel);
    691691
    692         $smartcrop = false;
     692        $args = ['action' => 'reoptimize', 'compressionType' => $compressionType];
     693
     694        // Smartcrop is not always passed, only add here when passed otherwise to defaults.
    693695        if ($actionType == ImageModel::ACTION_SMARTCROP || $actionType == ImageModel::ACTION_SMARTCROPLESS)
    694696        {
    695             $smartcrop = $actionType;
     697            $args['smartcrop'] = $actionType;
    696698        }
    697699
    698700        // @todo Ideally this should go to QueueController - addItemToQueue, but issue with arguments. Leaving it for now.
    699701        $queueController = new QueueController();
    700         $result  = $queueController->addItemToQueue($imageModel, ['action' => 'reoptimize', 'compressionType' => $compressionType,
    701             'smartcrop' => $smartcrop]);
     702        $result  = $queueController->addItemToQueue($imageModel, $args);
    702703
    703704   
     
    807808    protected function createBulk($json, $data)
    808809    {
     810        $filters = [];
     811        $has_filters = false;
     812       
     813        if (isset($_POST['filter_startdate']))
     814        {
     815             $filters['start_date'] = intval($_POST['filter_startdate']);
     816             $has_filters = true;   
     817        }
     818        if (isset($_POST['filter_enddate']))
     819        {
     820             $filters['end_date'] = intval($_POST['filter_enddate']);
     821             $has_filters = true;
     822
     823        }
     824
     825        $args = [];
     826        if (true === $has_filters)
     827        {
     828            $args['filters'] = $filters;
     829        }
     830       
     831
     832
    809833        $bulkControl = BulkController::getInstance();
    810         $stats = $bulkControl->createNewBulk('media');
     834        $stats = $bulkControl->createNewBulk('media', $args);
    811835        $json->media->stats = $stats;
    812836
    813         $stats = $bulkControl->createNewBulk('custom');
     837        $stats = $bulkControl->createNewBulk('custom', $args);
    814838        $json->custom->stats = $stats;
    815839
     
    827851       
    828852        $doAi = filter_var(sanitize_text_field($_POST['aiActive']), FILTER_VALIDATE_BOOLEAN);
     853
     854        $aiPreserve = isset($_POST['aiPreserve']) ? filter_var(sanitize_text_field($_POST['aiPreserve']), FILTER_VALIDATE_BOOLEAN) : null;
     855
    829856        $backgroundProcess = filter_var(sanitize_text_field($_POST['backgroundProcess']), FILTER_VALIDATE_BOOLEAN);
    830857
     
    841868        \wpSPIO()->settings()->autoAIBulk = $doAi;
    842869
     870        if (false === is_null($aiPreserve))
     871        {
     872            \wpSPIO()->settings()->aiPreserve = $aiPreserve;
     873        }
     874
    843875        $bulkControl = BulkController::getInstance();
    844876
     
    887919
    888920        if (in_array('media', $queues)) {
    889             $stats = $bulkControl->createNewBulk('media', 'bulk-restore');
     921            $stats = $bulkControl->createNewBulk('media', ['customOp' => 'bulk-restore']);
    890922            $json->media->stats = $stats;
    891923        }
    892924
    893925        if (in_array('custom', $queues)) {
    894             $stats = $bulkControl->createNewBulk('custom', 'bulk-restore');
     926            $stats = $bulkControl->createNewBulk('custom', ['customOp' => 'bulk-restore']);
    895927            $json->custom->stats = $stats;
    896928        }
     
    905937
    906938
    907         $stats = $bulkControl->createNewBulk('media', 'migrate');
     939        $stats = $bulkControl->createNewBulk('media', ['customOp' => 'migrate']);
    908940        $json->media->stats = $stats;
    909941
     
    917949
    918950
    919         $stats = $bulkControl->createNewBulk('media', 'removeLegacy');
     951        $stats = $bulkControl->createNewBulk('media', ['customOp' => 'removeLegacy']);
    920952        $json->media->stats = $stats;
    921953
     
    9841016
    9851017        $optimizer = $qItem->getApiController('requestAlt');
    986         //$optimize->useCustomSettings($settingsData);
    987         //$result = $optimizer->enqueueItem($qItem, array_merge(['preview_only' => true, 'action' => 'requestAlt'], $settingsData));
    9881018
    9891019        $qItem->requestAltAction(array_merge(['preview_only' => true], $settingsData));
     
    10211051                    if (property_exists($result, 'aiData'))
    10221052                    {
     1053                        $aiModel = AiDataModel::getModelByAttachment($qItem->item_id, 'media');
     1054
    10231055                         $aiData = $optimizer->formatResultData($result->aiData, $qItem);
     1056                         list($items, $aiData) = $optimizer->formatGenerated($aiData, $aiModel->getCurrentData(), $aiModel->getOriginalData());
    10241057                         $aiData['item_id'] = $qItem->item_id;
    10251058                         $aiData['time_generated'] = time();
     
    10391072                    }
    10401073                }
    1041 
    1042 
    1043                 //$is_done = true;
    1044                 //$this->send((object) $result_json);
    1045                 //break;
    10461074               
    10471075            }
     
    10591087            $i++;
    10601088        }
    1061 
    1062         //$this->send($result_json);
    1063 
    10641089    }
    10651090
     
    10761101        else
    10771102        {
    1078             $item = new AiDataModel($id);
     1103            $item = AiDataModel::getModelByAttachment($id);
    10791104            $attach_id = $id;
    10801105        }
    1081 
    1082     //  $attach_id = null;
    10831106       
    10841107        $imageModel = \wpSPIO()->fileSystem()->getMediaImage($attach_id);
     
    15401563    {
    15411564
     1565        // defaults
     1566        $message = __('This user is not allowed to edit this image', 'shortpixel-image-optimiser');
     1567
    15421568        $accessModel = AccessModel::getInstance();
    15431569        if (is_object($mediaItem)) {
    15441570            $bool = $accessModel->imageIsEditable($mediaItem);
    15451571            $id = $mediaItem->get('id');
     1572
    15461573        } else {
    15471574            $bool = false;
    15481575            $id = false;
     1576            if (! is_object($mediaItem))
     1577            {
     1578                $message = __('Image does not exist or could not be loaded', 'shortpixel-image-optimiser');
     1579            }
    15491580        }
    15501581
    15511582        if ($bool === false) {
    15521583            $json = new \stdClass;
    1553             $json->message = __('This user is not allowed to edit this image', 'shortpixel-image-optimiser');
     1584            $json->message = $message;
    15541585            $json->status = false;
    15551586            $json->id = $id;
     
    15731604
    15741605        wp_send_json($json);
    1575 
    15761606        exit();
    15771607    }
  • shortpixel-image-optimiser/trunk/class/Controller/Api/AiController.php

    r3347742 r3363822  
    235235    protected function handleSuccess($aiData, QueueItem $qItem)
    236236    {
    237        
     237      if (false === is_null($qItem->data()->returndatalist))
     238      {
     239         $returndatalist = $qItem->data()->returndatalist;
     240         if (is_object($returndatalist))
     241         {
     242           $returndatalist = (array) $returndatalist;
     243         }
     244
     245         foreach($returndatalist as $name => $data)
     246         {
     247            if (is_object($data)) // annoying conversion somehow by json decode from record
     248            {
     249               $data = (array) $data;
     250            }
     251            if (! isset($aiData[$name]) && isset($data['status']))
     252            {
     253                $aiData[$name]  = $data['status'];
     254            }
     255         }
     256      }
     257     
    238258      return $this->returnSuccess(['aiData' => $aiData], RequestManager::STATUS_SUCCESS, __('Retrieved AI image SEO data', 'shortpixel-image-optimiser')); ;
    239259    }
  • shortpixel-image-optimiser/trunk/class/Controller/Api/ApiController.php

    r3289850 r3363822  
    239239                    $imageName = $imageNames[$index];
    240240                    $fileName = $fileNames[$index];
     241                    $paramlist = $qItem->data()->paramlist;
     242
     243                    // Here add paramList items that are possible needed for success checks
     244                    $params = isset($paramlist[$index]) ? (array) $paramlist[$index] : [];
     245                   
     246
    241247                    $data = array(
    242248                        'fileName' => $fileName,
    243249                        'imageName' => $imageName,
    244250                    );
     251
     252                    $data = array_merge($params, $data);
    245253
    246254                    // Filesize might not be present, but also imageName ( only if smartcrop is done, might differ per image)
     
    399407
    400408        if (false === $this->checkFileSizeMargin($originalFileSize, $checkFileSize)) {
    401             $image['image']['status'] = self::STATUS_OPTIMIZED_BIGGER;
    402             $checkFileSize = $originalFileSize;
     409           
     410            // Prevent this check if smartcrop is active on this image.
     411            if (isset($data['resize']) && 4 == $data['resize'] )
     412            {
     413                $image['image']['status'] = self::STATUS_OPTIMIZED_BIGGER;
     414                $checkFileSize = $originalFileSize;
     415            }
    403416        }
    404417
  • shortpixel-image-optimiser/trunk/class/Controller/BulkController.php

    r3289850 r3363822  
    3737   * 'bulk-restore', or 'migrate'.
    3838   */
    39    public function createNewBulk($type = 'media', $customOp = null)
    40    {
     39   public function createNewBulk($type = 'media', $args = [])
     40   {
     41      $defaults = [
     42          'customOp' => null,
     43          'filters' => [],
     44
     45      ];
     46
     47
     48      $args = wp_parse_args($args, $defaults);
     49
    4150      $queueController = new QueueController(['is_bulk' => true]);
    4251
     
    5362      $Q = $queueController->getQueue($type);
    5463
    55       $Q->createNewBulk();
    56 
    57       if (! is_null($customOp))
     64      if (! is_null($args['customOp']))
    5865      {
    59         $options = array();
     66        $customOp = $args['customOp'];
     67        //$args['customOp'] = $customOp;
    6068        if ($customOp == 'bulk-restore')
    6169        {
    62           $options['numitems'] = 5;
    63           $options['retry_limit'] = 5;
    64           $options['process_timeout'] = 3000;
     70          $args['numitems'] = 5;
     71          $args['retry_limit'] = 5;
     72          $args['process_timeout'] = 3000;
     73         
    6574        }
    6675        if ($customOp == 'migrate' || $customOp == 'removeLegacy')
    6776        {
    68            $options['numitems'] = 200;
     77           $args['numitems'] = 200;
    6978        }
    7079
    71                 $options = apply_filters('shortpixel/bulk/custom_options', $options, $customOp);
    72         $Q->setCustomBulk($customOp, $options);
     80                $args = apply_filters('shortpixel/bulk/custom_options', $args);
     81
    7382      }
     83
     84     
     85      $Q->createNewBulk($args);
     86
    7487
    7588      return $Q->getStats();
  • shortpixel-image-optimiser/trunk/class/Controller/Front/CDNController.php

    r3346588 r3363822  
    299299        $fonts = ['.ttf', '.woff', '.woff2', '.otf'];
    300300
    301         if (true == $settings->cdn_js) {
     301        if (true === $settings->cdn_js) {
    302302            $checkExtensions[] = '.js';
    303303           
    304304        }
    305         if (true == $settings->cdn_css)
     305        if (true === $settings->cdn_css)
    306306        {   
    307307            $checkExtensions[] = '.css';
     
    353353        $replaceBlocks = $this->filterRegexExclusions($replaceBlocks);
    354354        $replaceBlocks = $this->filterOtherDomains($replaceBlocks);
     355        $replaceBlocks = $this->filterFonts($replaceBlocks);
    355356
    356357        if (count($replaceBlocks) > 0) {
     
    466467        }
    467468
     469
     470    }
     471
     472    /** The image check on inline CSS might also catch inline fonts.  Check against settings if they should be processed or not.
     473     *
     474     * @param mixed $replaceBlocks
     475     * @return mixed
     476     */
     477    protected function filterFonts($replaceBlocks)
     478    {
     479        $settings = \wpSPIO()->settings();
     480
     481        if (true === $settings->cdn_css)
     482        {
     483            return $replaceBlocks;
     484        }
     485
     486        $replaceBlocks = array_filter($replaceBlocks, function ($replaceBlock)
     487        {
     488             $fonts = ['.ttf', '.woff', '.woff2', '.otf'];
     489             foreach($fonts as $extcheck)
     490             {
     491                  if (strpos($replaceBlock->url, $extcheck) !== false)
     492                  {
     493                        return false;
     494                  }
     495             }
     496             return true;
     497
     498        });
     499   
     500        return $replaceBlocks;
    468501
    469502    }
  • shortpixel-image-optimiser/trunk/class/Controller/Optimizer/OptimizeAiController.php

    r3348311 r3363822  
    4646        }
    4747
    48    //   $qItem->addResult(['qStatus' => Queue::RESULT_ERROR]);
    4948      return;
    5049  }
     
    5352  public function sendToProcessing(QueueItem $qItem) {
    5453
    55     if (false == $this->isSupported($qItem))
     54/*    if (false == $this->isSupported($qItem))
    5655    {
    5756        // For now only fail here is GIF support, so message is a backstop for now that later should be updated.
     
    6564    }
    6665    else
    67     {
     66    { */
    6867        $this->api->processMediaItem($qItem, $qItem->imageModel);
    69     }
     68    //}
    7069 
    7170  }
     
    7574  {     
    7675
    77       $aiDataModel = new AiDataModel($qItem->item_id);
     76      $aiDataModel = AiDataModel::getModelByAttachment($qItem->item_id);
    7877      $is_processable = $aiDataModel->isProcessable();
    7978
    8079      if (false === $is_processable) {
     80         $message = $aiDataModel->getProcessableReason();
    8181        $qItem->addResult([
    82           'message' => __('AI generation not possible or already generated', 'shortpixel-image-optimiser'),
     82          'message' => $message,
    8383          'is_error' => true,
    8484          'is_done' => true,
     
    107107        break;
    108108        case 'retrieveAlt':  // This might be deprecated, since retrieve will be called via next_action.
    109             $qItem->retrieveAltAction($args['remote_id']);
     109            $qItem->retrieveAltAction($args);
    110110            $directAction = false;
    111111        break;
     
    226226    // Always save the original filename
    227227    $aiData['original_filebase'] = $qItem->imageModel->getFileBase();
     228    $returnDataList = $qItem->data()->returndatalist;
    228229
    229230    if (! isset($aiData['filebase']))
     
    236237    foreach($textItems as $textItem)
    237238    {
    238          if (isset($aiData[$textItem]) && false !== $aiData[$textItem])
     239     
     240         if (isset($aiData[$textItem]) && false !== $aiData[$textItem] && false === is_numeric($aiData[$textItem]))
    239241         {
    240242             $aiData[$textItem] = $this->processTextResult($aiData[$textItem]);
    241243         }
    242     }           
     244         // If 1 is returned as data, this means for some reason the API didn't create a text for this field, while it is allowed to do so. Defer to empty string better than '1'
     245         if (true === is_numeric($aiData[$textItem]) && 1 == $aiData[$textItem])
     246         {
     247            $aiData[$textItem] = '';
     248         }
     249    }   
     250
     251    // Re-add Result after formatting so it passed back
     252    //$qItem->addResult(['aiData' => $aiData]);
     253
    243254
    244255    return $aiData;
     
    248259  {
    249260        $aiData = $qItem->result->aiData; 
    250         $settings = $this->getAISettings();
    251 
     261        $settings = \wpSPIO()->settings();
    252262
    253263        $checks = ['alt' => 'ai_gen_alt',
     
    257267        ];
    258268
    259         foreach($checks as $check_name => $check_setting)
    260         {
    261             if (false === $settings[$check_setting])
    262             {
    263                  unset($aiData[$check_name]);
    264             }
    265         }
    266 
    267 
    268269        $aiData = $this->formatResultData($aiData, $qItem);
    269270
     
    273274        $item_id = $qItem->item_id;
    274275       
    275         $aiModel = new AIDataModel($item_id, 'media');
     276        $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    276277        $aiModel->handleNewData($aiData);
    277278
     
    292293        }
    293294        */
     295
    294296        $imageModel = $qItem->imageModel;
    295         $qItem->addResult(['improvements' => $imageModel->getImprovements()]);
    296 
    297 
    298         $this->addPreview($qItem);
     297        $qItem->addResult(['improvements' => $imageModel->getImprovements()]); // Improvements for bulk UX.
     298
     299        $this->addPreview($qItem); // Preview ( image ) for bulk UX
     300
     301        AiDataModel::flushModelCache($item_id);
     302
     303        // Get generated data which is the final result for the action including exclusions etc.
     304        $data = $this->getAltData($qItem);
     305        $qItem->addResult(['aiData' => $data['generated']]); // But the generated data in the result.
    299306
    300307        $this->finishItemProcess($qItem);
     
    312319  protected function replaceImageAttributes(QueueItem $qItem, $aiData)
    313320  {
     321            if (is_int($aiData['alt']) && is_int($aiData['caption']))
     322            {
     323                Log::addInfo('Alt/Caption returned integer/status, not replace');
     324                return;
     325            }
     326
    314327             // Replacer Part
    315328             $url = $qItem->data()->url;
     
    392405        if ($targetFileObj->exists())
    393406        {
    394           //$qItem->result()->is_error = true;
    395           //$qItem->result()->message = __('Replace Files: File Already exists', 'shortpixel-image-optimiser');
    396407          Log::addWarn('Replace files found filename conflict and didnt run', $targetFileObj->getFullPath());
    397408          return false;
     
    414425      }
    415426
    416 
    417 
    418 
    419427      $replacer = new Replacer();
    420428      $replacer->setSource($source_url);
     
    424432     
    425433      $replacer->replace();
    426 
    427434
    428435      $this->replaceMetaData($item_id, $base_filename, $newFileName );
     
    511518             } */
    512519
    513              $sources[] = $match;
    514 
    515              if (isset($aiData['alt']))
     520             $do_replace = false;
     521
     522             if (isset($aiData['alt']) && false === is_int($aiData['alt']))
    516523             {
    517524                $frontImage->alt = $aiData['alt'];
     525                $do_replace = true;
    518526             }
    519              if (isset($aiData['caption']))
     527             if (isset($aiData['caption']) && false === is_int($aiData['caption']))
    520528             {
    521529                $frontImage->caption = $aiData['caption'];
     530                $do_replace = true;
    522531             }
    523532
    524              $replaces[] = $frontImage->buildImage();
    525 
     533             if (true === $do_replace)
     534             {
     535                $sources[] = $match;
     536                $replaces[] = $frontImage->buildImage();
     537             }
    526538
    527539            }
    528540
    529             $content = $replacer2->replaceContent($content, $sources, $replaces);
    530            
    531             $replacer2->Updater()->updatePost($post_id, $content);
     541            if (count($sources) > 0 && count($replaces) > 0)
     542            {
     543                Log::addInfo('Running Ai Replace : ', [$aiData, $sources, $replaces]);
     544                $content = $replacer2->replaceContent($content, $sources, $replaces);
     545                $replacer2->Updater()->updatePost($post_id, $content);
     546            }
    532547        }
    533548
     
    544559  }
    545560
     561  /*
     562  protected function fetchCaptionMatches($content, $qItem)
     563  {
     564       $pattern = '/'
     565  }
     566*/
    546567  /**
    547568   * Check if setting AI is enabled in settings.
     
    598619  }
    599620
    600   protected function getRequestJSON($url, $item_id, $params = [])
     621  /*
     622  protected function getRequestJSON($url, $params = [])
    601623  {
    602624     $settings = $this->getAISettings($params);
     625
     626     $ignore_fields = (isset($params['ignore_fields'])) ? $params['ignore_fields'] : [];
    603627
    604628     $json = [
     
    646670  }
    647671
     672  */
     673
     674  /*
    648675  public function parseJSONForQItem(QueueItem $qItem, $params = [])
    649676  {
    650677        $url = $qItem->data()->url;
    651678        $item_id = $qItem->item_id;
    652         $json = $this->getRequestJSON($url, $item_id, $params);
     679        $settings = \wpSPIO()->settings();
     680
     681        // Note this is also checked in AiDataModel for checking processable.  Might need to sync upon adding fields
     682        if (true === $settings->aiPreserve)
     683        {
     684            $returnDataList = $qItem->data()->returndatalist;
     685
     686            $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
     687            $current = $aiModel->getCurrentData();
     688            $filtered = array_filter($current); // filter out all empty variables
     689
     690          //  $altdata = $this->getAltData($qItem);
     691            $params['ignore_fields'] = array_keys($filtered);
     692           
     693            foreach($filtered as $key => $filter)
     694            {
     695                 $returnDataList[$key] = AiDataModel::F_STATUS_EXCLUDE;
     696            }
     697            $qItem->data()->returndatalist = $returnDataList;
     698        }
     699       
     700        $json = $this->getRequestJSON($url, $params);
     701
     702
    653703        $qItem->data()->paramlist = $json;
    654704  }
    655 
     705        */
     706
     707/*
    656708  protected function parseQuestionForQItem(QueueItem $qItem)
    657709  {
     
    661713        $qItem->data()->url = $question;
    662714  }
    663 
     715*/
     716  /*
    664717  private function getAISettings($params = [])
    665718  {
     
    684737    'ai_use_exif' => $settings->ai_use_exif,
    685738    'ai_language' => $settings->ai_language,
     739    'aiPreserve' => $settings->aiPreserve,
    686740    ];
    687741
     
    690744    return $params;
    691745  }
    692 
    693   public function parseQuestion($url, $item_id, $params = [])
    694   {
    695     $settings = \wpSPIO()->settings();
    696 
    697     $defaults = [
    698     'ai_general_context' => $settings->ai_general_context,
    699     'ai_use_post' => $settings->ai_use_post,
    700     'ai_gen_alt' => $settings->ai_gen_alt,
    701     'ai_gen_caption' => $settings->ai_gen_caption,
    702     'ai_gen_description' => $settings->ai_gen_description,
    703     'ai_filename_prefercurrent' => $settings->ai_filename_prefercurrent,
    704     'ai_limit_alt_chars' => $settings->ai_limit_alt_chars,
    705     'ai_alt_context' => $settings->ai_alt_context,
    706     'ai_limit_description_chars' => $settings->ai_limit_description_chars,
    707     'ai_description_context' => $settings->ai_description_context,
    708     'ai_limit_caption_chars' => $settings->ai_limit_caption_chars,
    709     'ai_caption_context' => $settings->ai_caption_context,
    710     'ai_gen_filename' => $settings->ai_gen_filename,
    711     'ai_limit_filename_chars' => $settings->ai_limit_filename_chars,
    712     'ai_filename_context' => $settings->ai_filename_context,
    713     'ai_use_exif' => $settings->ai_use_exif,
    714     'ai_language' => $settings->ai_language,
    715     ];
    716 
    717     $params = wp_parse_args($params, $defaults);
    718 
    719     $question = [
    720             'main' => $params['ai_general_context'],
    721             'language' => $params['ai_language'],
    722             'required_tags' => [],
    723      ];
    724 
    725      $question['page'] = $this->getPageQuestion($question, $item_id, $params);
     746 */
    726747   
    727      $question = $this->getPartQuestion($question, 'alt', $params);
    728      $question = $this->getPartQuestion($question, 'caption', $params);
    729      $question = $this->getPartQuestion($question, 'description', $params);
    730      $question = $this->getPartQuestion($question, 'filename', $params);
    731 
    732     if (true == $params['ai_use_exif'])
    733     {
    734          $question['exif'] = ' and take into account the image EXIF data when generating all the requested texts';
    735     }
    736 
    737    // $question['tags'] = implode($question['required_tags'], ',');
    738 
    739     $question = apply_filters('shortpixel/ai/parsed_questions', $question);
    740 
    741    /* $alt = isset($question['alt']) ? $question['alt'] : '';
    742     $caption = isset($question['caption']) ? $question['caption'] : '';
    743     $description =isset($question['description']) ? $question['description'] : '';
    744     $filename = isset($question['filename']) ? $question['filename'] : '';
    745 */
    746     $specs = [];
    747     foreach($question['required_tags'] as $tag)
    748     {
    749         $specs[] = $question[$tag];
    750     }
    751     $specs = implode(' ', $specs);
    752 
    753     $required_tags =  implode(',', $question['required_tags_ainame']);
    754    
    755     if (strlen(trim($params['ai_language'])) <= 0)
    756     {   
    757         $params['ai_language'] = get_locale();
    758     }
    759 
    760     $final_question = sprintf("For the URL %s , with this context \" %s \" , write for %s SEO friendly texts with the following specifications: %s in %s language %s . Provide the answer in JSON format, seperating the %s output in seperate fields",
    761         $url,
    762         $question['main'],
    763         $required_tags,
    764         $specs,
    765         $question['language'],
    766         $question['exif'],
    767         $required_tags
    768 
    769 
    770     );
    771 
    772     return $final_question;
    773 
    774   }
    775  
    776   protected function getPartQuestion($question, $name, $params)
    777   {
    778    
    779     $limit = 'ai_limit_' . $name . '_chars';
    780     $context = 'ai_' . $name . '_context';
    781     $to_use = 'ai_gen_' . $name; 
    782 
    783     switch($name)
    784     {
    785          case 'alt':
    786             $aiName = 'alt tag';
    787          break;
    788          case 'caption':
    789             $aiName = 'caption tag';
    790          break;
    791          case 'description':
    792             $aiName = 'description text';
    793          break;
    794          case 'filename':
    795             $aiName = 'the file name';
    796          break;
    797     }
    798 
    799     if (true === $params[$to_use])
    800     {
    801         $limit = $params[$limit];
    802         $context = $params[$context];
    803 
    804         $string = ' For the ' . $aiName . ' limit your response to the most relevant ' . $limit . ' characters for SEO ';
    805 
    806         if ('filename' == $name)
    807         {
    808             $string .= ' leaving the filename extension intact ';
    809             if (true === $params['ai_filename_prefercurrent'])
    810             {
    811                 $string .=  ' and change filename only when the current filename is not relevant. Otherwise return false for this field ';
    812             }
    813            
    814         }
    815 
    816         if (strlen(trim($context)) > 0)
    817         {
    818              $string .= ' and use this additional information when generating the ' . $aiName . ':' . $context . '. ';
    819         }
    820 
    821         $question[$name] = $string;
    822         $question['required_tags_ainame'][] = $aiName;
    823         $question['required_tags'][] = $name;
    824     }
    825 
    826    
    827     return $question;
    828   }
    829 
    830   protected function getPageQuestion($question, $item_id, $params)
    831   {
    832         if (false == $params['ai_use_post'])
    833         {
    834              return false;
    835         }
    836 
    837         $post = get_post($item_id);
    838         if (is_null($post) || false === $post)
    839         {
    840              return false;
    841         }
    842 
    843         $parent = $post->post_parent;
    844 
    845         if ($parent <= 0 || ! is_int($parent))
    846         {
    847              return false;
    848         }
    849 
    850         $page_post = get_post($parent);
    851         if (is_null($page_post) || false === $page_post)
    852         {
    853              return false;
    854         }
    855 
    856         $title = $page_post->post_title;
    857         $excerpt = get_the_excerpt(($page_post));
    858 
    859         $string = ' for the article with the title ' . $title;
    860 
    861         if (strlen(trim($excerpt)) > 0 )
    862         {
    863             $string .= ' and excerpt ' . $excerpt;
    864         }
    865 
    866        
    867         return $string;
    868   }
    869 
    870  
    871 
     748
     749  /*
    872750  public function isSupported(queueItem $qItem)
    873751  {
     
    881759       
    882760       return true;
    883   }
     761  } */
    884762
    885763  public function undoAltData(QueueItem $qItem)
    886764  {
    887765       $item_id = $qItem->item_id;
    888        $aiModel = new AiDataModel($item_id, 'media');
     766       $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    889767       $original = $aiModel->getOriginalData();
    890768       $generated = $aiModel->getGeneratedData();
     769
    891770
    892771       $aiData = [
     
    898777   
    899778       $aiModel->revert();
     779       AiDataModel::flushModelCache($item_id);
    900780
    901781       $this->replaceImageAttributes($qItem, $aiData);
    902782
     783       $aiData = $aiModel->getCurrentData();
     784   
    903785       return $this->getAltData($qItem);
    904786  }
     
    908790    $item_id = $qItem->item_id;
    909791
    910     $aiModel = new AiDataModel($item_id, 'media');
     792    $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    911793
    912794    $status = $aiModel->getStatus();
     
    920802                $aiModel->migrate($metacheck);
    921803                delete_post_meta($item_id, 'shortpixel_alt_requests');
    922                 $aiModel = new AiDataModel($item_id, 'media');
     804                $aiModel = AiDataModel::getModelByAttachment($item_id, 'media');
    923805                $status = $aiModel->getStatus();
    924806         }
     
    927809    $generated = $aiModel->getGeneratedData();
    928810    $original = $aiModel->getOriginalData();
     811    $current = $aiModel->getCurrentData();
    929812
    930813    $image_url = $qItem->imageModel->getUrl();
    931814
    932     $fields = ['alt', 'caption', 'description'];
    933     $dataItems = [];
    934     foreach($fields as $name)
    935     {
    936          if (isset($generated[$name]) && false === is_null($generated[$name]) && strlen($generated[$name]) > 1)
    937          {
    938             $dataItems[] = ucfirst($name);
    939          }
    940     }
     815    list($dataItems, $generated) = $this->formatGenerated($generated, $current, $original);
     816
    941817
    942818    $view = new ViewController();
     
    946822            'result_alt' => $generated['alt'],
    947823            'has_data' => ($status == AiDataModel::AI_STATUS_GENERATED) ? true : false,
     824            'is_processable' => $aiModel->isProcessable(),
     825            'processable_reason' => $aiModel->getProcessableReason(),
     826            'processable_status' => $aiModel->getProcessableReason(true),
    948827            'image_url' => $image_url,
    949828           // 'current_alt' => $current_alt,
    950829            'status' => $status,
    951             'isSupported' => $this->isSupported($qItem),
    952             'dataItems' => $dataItems,
     830      //      'isSupported' => $this->isSupported($qItem),
     831            'dataItems' => $dataItems,  // This seems not used(?)
    953832            'isDifferent' =>  $aiModel->currentIsDifferent(),
    954833        ]);
    955834
     835
     836    // *****!!! Temporary don't pass these back since we don't support it yet ** //
     837
     838    if (isset($generated['filebase']))
     839    {
     840       unset($generated['filebase']);
     841    }
     842    if (isset($generated['filename']))
     843    {
     844       unset($generated['filename']);
     845    }
     846
    956847    $metadata['snippet'] = $view->returnView('snippets/part-aitext');
    957848
    958849    $metadata['generated'] = $generated;
    959850    $metadata['original'] = $original;
     851    $metadata['current'] = $current;
    960852    $metadata['action'] = $qItem->data()->action;
    961853    $metadata['item_id'] = $item_id;
     
    964856}
    965857
     858public function formatGenerated($generated, $current, $original)
     859{
     860   
     861  $fields = ['alt', 'caption', 'description'];
     862  $dataItems = [];
     863
     864  // Statii from AiDataModel which means generated is not available (replace for original/current?)
     865  $statii = [AiDataModel::F_STATUS_PREVENTOVERRIDE, AiDataModel::F_STATUS_EXCLUDESETTING];
     866
     867  foreach($fields as $name)
     868  {
     869       if (false === isset($generated[$name]))
     870       {
     871          continue;
     872       }
     873       $value = $generated[$name];
     874       
     875
     876       if (false === is_null($value) && false === is_int($value) && strlen($value) > 1)
     877       {
     878          $dataItems[] = ucfirst($name);
     879       }
     880       if (is_int($value) && in_array($value, $statii))
     881       {
     882          if (isset($current[$name]))
     883          {
     884               $value = $current[$name];
     885          }
     886          elseif(isset($original[$name]))
     887          {
     888               $value = $original[$name];
     889          }
     890          $generated[$name] = $value;
     891       }
     892  }
     893
     894  return [$dataItems, $generated];
     895}
     896
    966897
    967898
  • shortpixel-image-optimiser/trunk/class/Controller/Optimizer/OptimizerBase.php

    r3346588 r3363822  
    4848      //exit('This call is wron because in it messes with ActionController - Reoptimize ( calls ActionController again instead of OptimizeConrtoller');
    4949      $calledClass = get_called_class();
    50 //Log::addTemp('OptimizerBase Called Class - ' . $calledClass);
     50
    5151      if (! isset(static::$instances[$calledClass]))
    5252      {
     
    5454      }
    5555
    56  //  Log::addTemp('OptimizeBase, Instances', self::$instances);
    5756        return self::$instances[$calledClass];
    5857    }
  • shortpixel-image-optimiser/trunk/class/Controller/Queue/MediaLibraryQueue.php

    r3289850 r3363822  
    88use ShortPixel\ShortQ\ShortQ as ShortQ;
    99use ShortPixel\Controller\CacheController as CacheController;
     10use ShortPixel\Helper\UtilHelper;
    1011use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
    1112use ShortPixel\Model\Image\ImageModel as ImageModel;
     
    3233        'retry_limit' => 30, // amount of times it will retry without errors before giving up
    3334        'enqueue_limit' => 200, // amount of items added to the queue when preparing.
     35        'filters' => [],
    3436     );
    3537
     
    5658   }
    5759
     60   public function createNewBulk($args = [])
     61   {
     62     /* if (isset($args['filters']))
     63      {
     64         $this->addFilters($args['filters']);
     65      } */
     66       
     67     
     68      // Parent should save options as well.
     69       return parent::createNewBulk($args);
     70   }
     71
     72
     73   protected function addFilters($filters)
     74   {
     75
     76      //$start_id = $end_id = null;
     77
     78     
     79      global $wpdb;
     80     
     81
     82      $start_date = isset($filters['start_date'])  ? new \DateTime($filters['start_date']) : false;
     83      $end_date = isset($filters['end_date'])  ? new \DateTime($filters['end_date']) : false;
     84
     85      if (isset($filters['start_date']))
     86      {
     87         //$date = UtilHelper::timestampToDB($filters['start_time']);
     88         $date = $start_date->format("Y-m-d H:i:s");
     89         $startSQL = 'select max(ID) from wp_posts where post_date <= %s group by post_date order by post_date DESC limit 1';
     90         $sql = $wpdb->prepare($startSQL, $date);
     91         $start_id =  $wpdb->get_var($sql);
     92      }
     93      if (isset($filters['end_date']))
     94      {
     95        // $date = UtilHelper::timestampToDB($filters['end_time']);
     96        $date = $end_date->format("Y-m-d H:i:s");
     97         $endSQL = 'select MIN(ID) from wp_posts where post_date <= %s group by post_date order by post_date DESC limit 1';
     98         $sql = $wpdb->prepare($endSQL, $date);
     99         $end_id =  $wpdb->get_var($sql);
     100      }
     101     
     102
     103
     104       //echo "Start $start_id END $end_id";
     105       //exit();
     106      // IF POST DATE NEEDS 09-20 ( or 23:59:59? )
     107      // select post_date, max(ID) from wp_posts where post_date <= '2024-09-21 00:00:00' group by post_date order by post_date DESC limit 100
     108   }
     109   
    58110
    59111   private function queryPostMeta()
  • shortpixel-image-optimiser/trunk/class/Controller/Queue/Queue.php

    r3346588 r3363822  
    99use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
    1010use ShortPixel\Controller\CacheController as CacheController;
     11use ShortPixel\Controller\Optimizer\OptimizeAiController;
    1112use ShortPixel\Controller\ResponseController as ResponseController;
    1213use ShortPixel\Model\Converter\Converter as Converter;
     
    4849
    4950   
    50     public function createNewBulk()
     51    public function createNewBulk($args = [])
    5152    {
    5253                $this->resetQueue();
     
    5859        $cache = new CacheController();
    5960        $cache->deleteItem($this->cacheName);
     61
     62        $this->setCustomBulk($args);
    6063    }
    6164
     
    98101       $args = wp_parse_args($args, $defaults);
    99102
    100 
    101103       $qItem = QueueItems::getImageItem($imageModel);
    102 
    103104
    104105             $result = new \stdClass;
     
    107108       $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
    108109
     110       $this->checkQueueCache($imageModel->get('id'));
     111
    109112       $result->qstatus = $this->getQStatus($numitems);
    110113       $result->numitems = $numitems;
     
    117120    {
    118121      $this->q->addItems([$qItem->returnEnqueue()], false);
     122      $item_id = $qItem->item_id;
    119123      $numitems = $this->q->withRemoveDuplicates()->enqueue(); // enqueue returns numitems
    120124
     
    123127      $result->numitems = $numitems;
    124128
    125       do_action('shortpixel_start_image_optimisation', $qItem->item_id, $qItem->imageModel);
     129      $this->checkQueueCache($item_id);
     130     
     131
     132      do_action('shortpixel_start_image_optimisation', $item_id, $qItem->imageModel);
    126133      return $result;
    127134    }
     
    270277              }
    271278             
     279                $optimizeAiController = OptimizeAiController::getInstance();
     280
    272281                // If autoAi is on the bulk, add operation to the item
    273                 if ('media' === $mediaItem->get('type') && true === $settings->autoAIBulk)
     282                $enqueueAi = false;
     283                if ('media' === $mediaItem->get('type') && true === $optimizeAiController->isAiEnabled() && true === $settings->autoAIBulk)
    274284                {
    275 
    276                   $aiDataModel = new AiDataModel($mediaItem->get('id'));
    277                   $enqueueAi = false;
    278                   if ($aiDataModel->isProcessable() && in_array($mediaItem->getExtension(), $aiDataModel->supportedExtensions()))
    279                   {
    280                     $enqueueAi = true;
    281                   }
     285                  $aiDataModel = AiDataModel::getModelByAttachment($mediaItem->get('id')); 
     286                  $enqueueAi = $aiDataModel->isProcessable();
    282287                }
    283                 else
    284                 {
    285                    $enqueueAi = false;
    286                 }
    287 
    288288
    289289                if ($mediaItem->isProcessable() && $mediaItem->isOptimizePrevented() === false && ! $operation) // Checking will be done when processing queue.
     
    317317                                        $baseCount += $counts->baseCount; // base images (all minus webp/avif)
    318318
     319                   
     320                    $this->checkQueueCache($item_id);
    319321                    do_action('shortpixel_start_image_optimisation', $mediaItem);
    320322
     
    511513    }
    512514
    513     public function setCustomBulk($type = null, $options = array() )
    514     {
    515         if (is_null($type))
     515    public function setCustomBulk($options = [] )
     516    {
     517        if (0 === count($options))
    516518          return false;
    517519
    518520        $customData = $this->getStatus('custom_data');
    519         $customData->customOperation = $type;
     521
     522
     523        if (isset($options['customOp']))
     524        {
     525           $customOp = $options['customOp'];   
     526           $customData->customOperation = $customOp;
     527           unset($options['customOp']);
     528        }
     529
    520530        if (is_array($options) && count($options) > 0)
    521531          $customData->queueOptions = $options;
     
    613623                return false;
    614624        }
     625
     626    protected function checkQueueCache($item_id)
     627    {
     628
     629     
     630      if (isset(self::$isInQueue[$item_id]) && false === self::$isInQueue[$item_id])
     631      {
     632         unset(self::$isInQueue[$item_id]);
     633      }
     634
     635
     636
     637
     638    }
    615639
    616640    public function itemFailed(QueueItem $qItem, $fatal = false)
  • shortpixel-image-optimiser/trunk/class/Controller/QueueController.php

    r3346588 r3363822  
    6363        'smartcrop' => null,
    6464        'next_actions' => [],
     65        'returndatalist' => [],
    6566      );
    6667      $args = wp_parse_args($args, $defaults);
     
    7273      {
    7374         $qItem->data()->next_actions = $args['next_actions'];
     75      }
     76
     77      if (is_object($args['returndatalist']))
     78      {
     79         $args['returndatalist'] = (array) $args['returndatalist'];
     80      }
     81      if (is_array($args['returndatalist']) && count($args['returndatalist']) > 0)
     82      {
     83         $qItem->data()->returndatalist = $args['returndatalist'];
    7484      }
    7585
     
    198208        // @todo This queueItem should maybe not to stuffed with 'addresult'm since it's a different object.
    199209          $queueItem = $q->getItem($mediaItem->get('id'));
     210         
    200211          if (is_object($queueItem))
    201212          {
     
    327338  {
    328339    $result = $Q->run();
    329     $results = [];
    330340    $fs = \wpSPIO()->filesystem();
    331341
     
    337347    $qtype = strtolower($qtype);
    338348
    339     //Log::addTemp('RunTick Items - ', $items);
    340349    /* Only runs if result is array, dequeued items.
    341350       Item is a MediaItem subset of QueueItem
     
    346355          $action = $qItem->data()->action;
    347356          $apiController = $qItem->getAPIController($action);
     357          $send_to_processing = true;
    348358
    349359
     
    374384          }
    375385         
    376           if (! is_object($imageModel))
     386          if (! is_object($imageModel)) // Error in loading imageModel, can't process this.
    377387          {
    378388            Log::addWarn('ImageObject was empty when send to processing - ' . $item_id);
    379389            $qItem->addResult([
    380390                'apiStatus' => RequestManager::STATUS_NOT_API,
    381                 'message' => __("File Error. File could not be loaded with this ID ", 'shortpixel-image-optimiser'),
     391                'message' => __("File Error. Media Item could not be loaded with this ID ", 'shortpixel-image-optimiser'),
    382392                'fileStatus' => ImageModel::FILE_STATUS_ERROR,
    383393                'is_done' => true,
    384394                'is_error' => true,
    385395            ]);
     396            $Q->itemFailed($qItem, true);
     397            $send_to_processing = false;
    386398          }
    387399          elseif(true === $qItem->block())
     
    392404            ]);
    393405            Log::addWarn('Encountered blocked item, processing success? ', $item_id);
     406            ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
     407            $send_to_processing = false;
    394408          }
    395409          else
     
    403417            ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
    404418          }
    405 
    406           ResponseController::addData($item_id, 'fileName', $imageModel->getFileName());
    407 
    408419       
    409420          $this->setLastID($item_id);
    410421
    411           if (! is_null($apiController))
     422          if (! is_null($apiController) && true === $send_to_processing)
    412423          {
    413424            $apiController->sendToProcessing($qItem);
     
    821832    $item_id = $qItem->item_id;
    822833    $responseItem = ResponseController::getResponseItem($item_id);
    823     $type = $qItem->imageModel->get('type');
     834
     835    $type = (is_object($qItem->imageModel)) ? $qItem->imageModel->get('type') : false;
     836
     837    if (false === $type)
     838    {
     839      return;
     840    }
    824841
    825842    $fs = \wpSPIO()->filesystem();
  • shortpixel-image-optimiser/trunk/class/Controller/View/EditMediaViewController.php

    r3348311 r3363822  
    1515
    1616use ShortPixel\Controller\Queue\QueueItems as QueueItems;
    17 
     17use ShortPixel\Model\AiDataModel;
    1818use ShortPixel\Model\File\FileModel as FileModel;
    1919
     
    141141      protected function getStatistics()
    142142      {
    143         //$data = $this->data;
    144         $stats = array();
     143        $stats = [];
    145144        $imageObj = $this->imageModel;
    146145        $did_keepExif = $imageObj->getMeta('did_keepExif');
     
    213212                    }
    214213
     214          $optimizeAiController = OptimizeAiController::getInstance();
     215
    215216
    216217                    $thumbnails = $imageObj->get('thumbnails');
     
    267268                         $debugInfo[] = array(__('To Optimize URLS'),  $urls);
    268269                    }
    269                     if (isset($optimizeData))
    270                     {
    271                          $debugInfo[] = array(__('Optimize Data'), $optimizeData);
    272 
    273                          $queueControl = new QueueController();
    274 
    275                     //   $q = $queueControl->getQueue($imageObj->get('type'));
    276 
    277              $item = QueueItems::getImageItem($imageObj);
     270
     271
     272          $item = QueueItems::getImageItem($imageObj);
     273
     274          if ($imageObj->isProcessable())
     275                    {
     276                        // $queueControl = new QueueController();
     277
     278
    278279             $item->setDebug();
    279280             $item->newOptimizeAction();
     
    281282                         $returnEnqueue = $item->returnEnqueue();
    282283
    283              // TEST @todo REMOVE
    284              /*$replacer2 = new \ShortPixel\Replacer\Replacer();
    285              $setup = $replacer2->Setup();
    286              $setup->forSearch()->URL()->addData($item->imageModel->getURL());
    287              
    288              $base_url = $setup->forSearch()->URL()->getBaseURL();
    289            
    290              $text = 'AI TEST';
    291 
    292              $finder = $replacer2->Finder(['base_url' => $base_url, 'callback' => [OptimizeAiController::getInstance(), 'handleReplace'], 'return_data' => [
    293                  'retrievedText' => $text,
    294                  'qItem' => $item,
    295              ]]);
    296              
    297              $posts = $finder->posts();  */
    298 
    299                          $debugInfo[] = array(__('Image to Queue V2'), $returnEnqueue );
    300 
    301                     }
     284                         $debugInfo[] = array(__('Image to Queue'), $returnEnqueue );
     285
     286                    }
     287
     288          if ( $optimizeAiController->isAIEnabled())
     289          {
     290            $aiDataModel = AiDataModel::getModelByAttachment($this->post_id);
     291
     292            $aiProcessable = ($aiDataModel->isProcessable()) ? '<span class="green">Yes</span>' : '<span class="red">No</span> ';
     293
     294            $debugInfo[] = ['AI - is Processable', $aiProcessable];
     295
     296            if (true === $aiDataModel->isProcessable())
     297            {
     298              //$item->requestAltAction();
     299             // $optimizeAiController->parseJsonForQItem($item);
     300              $debugInfo[] = ['Ai - Paramlist ', $aiDataModel->getOptimizeData() ];
     301   //           $debugInfo[] = ['Ai - returnDataList' , $item->data()->returndatalist];
     302             
     303            }
     304            else
     305            {
     306               $debugInfo[] = ['Ai - Reason', $aiDataModel->getProcessableReason()];
     307            }
     308            if (true === $aiDataModel->isSomeThingGenerated())
     309            {
     310              $debugInfo[] = ['Ai -Generated ', $aiDataModel->getGeneratedData()];
     311            }
     312
     313          }
     314
     315
    302316
    303317          $debugInfo['imagemetadata'] = array(__('ImageModel Metadata (ShortPixel)'), $imageObj);
     
    366380
    367381              $display_size = ucfirst(str_replace("_", " ", $size));
    368               //$thumbObj = $imageObj->getThumbnail($size);
    369382
    370383              if ($thumbObj === false)
     
    374387              }
    375388
    376               $url = $thumbObj->getURL(); //$fs->pathToURL($thumbObj); //wp_get_attachment_image_src($this->post_id, $size);
     389              $url = $thumbObj->getURL();
    377390              $filename = $thumbObj->getFullPath();
    378391              $fileDir = $thumbObj->getFileDir();
  • shortpixel-image-optimiser/trunk/class/Controller/View/ListMediaViewController.php

    r3347742 r3363822  
    1313
    1414use ShortPixel\Controller\ApiKeyController as ApiKeyController;
     15use ShortPixel\Controller\Optimizer\OptimizeAiController;
    1516use ShortPixel\Controller\QuotaController as QuotaController;
    1617use ShortPixel\Controller\QueueController as QueueController;
     
    6061  {
    6162    $defaults['wp-shortPixel'] = __('ShortPixel Compression', 'shortpixel-image-optimiser');
    62     /*if (true === \wpSPIO()->settings()->enable_ai)
    63     {
    64       $defaults['wp-spio-ai'] = __('AI By Shortpixel', 'shortpixel-image-optimiser');
    65     } */
     63
    6664
    6765    return $defaults;
     
    9997     $list_actions = array();
    10098
    101      if (true === \wpSPIO()->settings()->enable_ai)
     99     $optimizeAiController = OptimizeAiController::getInstance();
     100
     101
     102     if (true === $optimizeAiController->isAiEnabled())
    102103     {
    103104        $aiDataModel = $this->loadAiItem($id);
     
    174175  protected function loadAiItem($item_id)
    175176  {
    176      $AiDataModel = new AiDataModel($item_id);
     177     $AiDataModel = AiDataModel::getModelByAttachment($item_id);
    177178     $this->view->item_id = $item_id;
    178179
  • shortpixel-image-optimiser/trunk/class/Controller/View/SettingsViewController.php

    r3348311 r3363822  
    430430          }
    431431
     432         
     433
    432434                    // If the compression type setting changes, remove all queued items to prevent further optimizing with a wrong type.
    433435                    if (intval($this->postData['compressionType']) !== intval($this->model->compressionType))
     
    448450                    {
    449451                            $type = $this->model->getType($name);
    450                             if ('boolean' === $type && ! isset($this->postData[$name]))
     452                            if ('boolean' === $type )
    451453                            {
    452                                  $this->model->{$name} = false;
     454                if( ! isset($this->postData[$name]))
     455                {
     456                                  $this->model->{$name} = false;
     457                }
     458                else
     459                {
     460                   $this->model->{$name} = true;
     461                }
    453462                            }
    454463                    }
     
    864873              }
    865874          }
     875
     876        if (false === isset($post['enable_ai']))
     877        {
     878             if (isset($post['autoAI']))
     879             {
     880                unset($post['autoAI']);
     881             }
     882             if (isset($post['autoAIBulk']))
     883             {
     884                unset($post['autoAIBulk']);
     885             }
     886        }
    866887
    867888       
  • shortpixel-image-optimiser/trunk/class/Helper/UiHelper.php

    r3346930 r3363822  
    331331      }
    332332
    333       //$aiDataModel = new AiDataModel($id);
    334 
    335333      if ($mediaItem->isSomethingOptimized() )
    336334      {
     
    439437
    440438
    441       if (false === is_null($aiDataModel) && $aiDataModel->isProcessable() && 'media' === $mediaItem->get('type') && in_array($mediaItem->getExtension(), $aiDataModel->supportedExtensions()))
     439      if (false === is_null($aiDataModel) && $aiDataModel->isProcessable() && 'media' === $mediaItem->get('type') )
    442440      {
    443441         $list_actions['shortpixel-generateai'] = self::getAction('shortpixel-generateai', $id);
  • shortpixel-image-optimiser/trunk/class/Helper/UtilHelper.php

    r3346588 r3363822  
    3030  }
    3131
    32   static public function timestampToDB($timestamp)
     32  public static function timestampToDB($timestamp)
    3333  {
    3434    return date("Y-m-d H:i:s", $timestamp);
    3535  }
    3636
    37   static public function DBtoTimestamp($date)
     37  public static function DBtoTimestamp($date)
    3838  {
    3939    return strtotime($date);
     
    170170    }
    171171    return $bool;
     172  }
     173
     174  public static function getAiSettings($params = [])
     175  {
     176    $settings = \wpSPIO()->settings();
     177
     178    $defaults = [
     179    'ai_general_context' => $settings->ai_general_context,
     180    'ai_use_post' => $settings->ai_use_post,
     181    'ai_gen_alt' => $settings->ai_gen_alt,
     182    'ai_gen_caption' => $settings->ai_gen_caption,
     183    'ai_gen_description' => $settings->ai_gen_description,
     184    'ai_filename_prefercurrent' => $settings->ai_filename_prefercurrent,
     185    'ai_limit_alt_chars' => $settings->ai_limit_alt_chars,
     186    'ai_alt_context' => $settings->ai_alt_context,
     187    'ai_limit_description_chars' => $settings->ai_limit_description_chars,
     188    'ai_description_context' => $settings->ai_description_context,
     189    'ai_limit_caption_chars' => $settings->ai_limit_caption_chars,
     190    'ai_caption_context' => $settings->ai_caption_context,
     191    'ai_gen_filename' => $settings->ai_gen_filename,
     192    'ai_limit_filename_chars' => $settings->ai_limit_filename_chars,
     193    'ai_filename_context' => $settings->ai_filename_context,
     194    'ai_use_exif' => $settings->ai_use_exif,
     195    'ai_language' => $settings->ai_language,
     196    'aiPreserve' => $settings->aiPreserve,
     197    ];
     198
     199    $params = wp_parse_args($params, $defaults);
     200
     201    return $params;
    172202  }
    173203
  • shortpixel-image-optimiser/trunk/class/Model/AiDataModel.php

    r3346588 r3363822  
    1414class AiDataModel
    1515{
    16 
    1716    protected $id;
    1817    protected $attach_id;
    1918    protected $type;
     19
     20    protected static $models = [];
    2021
    2122    protected $original = [
     
    4546    private $current_is_set = false;
    4647
     48    private $processable_status = 0;
    4749
    4850    const TYPE_MEDIA = 1;
    4951    const TYPE_CUSTOM = 2;
    5052
     53    // Status for the whole image, in the main table.
    5154    const AI_STATUS_NOTHING = 0;
    5255    const AI_STATUS_GENERATED = 1;
     56
     57    // IsProcessable statii
     58    const P_PROCESSABLE = 0;
     59    const P_ALREADYDONE = 1;  // Data already there
     60    const P_EXIFAI = 2;  // When Exif Flag forbids AI doing
     61    const P_EXTENSION = 3;
     62    const P_NOJOB = 4;
     63    const P_NOFIELDS = 5;
     64
     65    // Descriptive status if certain field is not generated / left alone.
     66    const F_STATUS_OK = 1;
     67    const F_STATUS_EXCLUDESETTING = -3;
     68    const F_STATUS_PREVENTOVERRIDE = -4;
     69   
    5370
    5471    public function __construct($attach_id, $type = 'media')
     
    94111        $this->generated = array_merge($this->generated, $generatedData);
    95112
    96 
    97113    }
    98114
     
    108124
    109125        return (array) $data;
     126
     127    }
     128
     129    /** Get all data needed to send API for generating AI texts, depending on settings. This includes all settings minus URL
     130     *
     131     * @return array{paramlist: array<string, array{context: mixed, chars: mixed}>, returndatalist: array<string, array<string, int>>}
     132     */
     133    public function getOptimizeData($params = [])
     134    {
     135        $settings = (object) UtilHelper::getAiSettings($params);
     136
     137        $ignore_fields = [];
     138        if (true === $settings->aiPreserve)
     139        {
     140            $currentData = $this->getCurrentData();
     141            $ignore_fields = array_keys(array_filter($currentData));
     142        }
     143
     144
     145       // $fields = ['ai_gen_alt', 'ai_gen_caption', 'ai_gen_description', 'ai_gen_filename'];
     146        $fields = ['alt', 'caption', 'description', 'filename'];
     147
     148        $paramlist = [
     149            'languages' => $settings->ai_language,
     150            'context' => $settings->ai_general_context,
     151        ];
     152        $returnDataList = [];
     153        $field_status = false; // check if there are any fields to process / not all excluded.
     154
     155        foreach($fields as $field_name)
     156        {
     157            $api_name = $field_name;
     158            //$paramlist[$api_name] = [];
     159
     160            switch($api_name)
     161            {
     162                case 'description':
     163                    $api_name = 'image_description';
     164                break;
     165                case 'filename':
     166                    $api_name = 'file';
     167                break;
     168            }
     169
     170
     171            if (false === $settings->{'ai_gen_' . $field_name})
     172            {
     173                $returnDataList[$field_name]['status'] = self::F_STATUS_EXCLUDESETTING;
     174                continue;
     175            }
     176            elseif (true === in_array($field_name, $ignore_fields))
     177            {
     178                $returnDataList[$field_name]['status'] = self::F_STATUS_PREVENTOVERRIDE;
     179            }
     180            else
     181            {
     182                $paramlist[$api_name] = [
     183                        'context' => $settings->{'ai_' . $field_name . '_context'},
     184                        'chars' => $settings->{'ai_limit_' . $field_name . '_chars'},
     185                ];
     186                $returnDataList[$field_name]['status']  = self::F_STATUS_OK;
     187                $field_status = true;
     188            }
     189
     190           
     191        }
     192
     193        if (false === $field_status)
     194        {
     195            $this->processable_status = self::P_NOJOB;
     196        }
     197
     198        return ['paramlist' => $paramlist, 'returndatalist' => $returnDataList];
    110199
    111200    }
     
    143232        }
    144233
    145 
    146234        // Save to WordPress
    147         /*
    148         if (isset($this->generated['alt']) && false !== $this->generated['alt'])
    149         {
    150             $bool = update_post_meta($this->attach_id, '_wp_attachment_image_alt', $this->generated['alt']);
    151         } */
    152 
    153235        $this->updateWPPost($this->generated);
    154236        $this->updateWpMeta($this->generated);
    155237
    156 /*        $post = get_post($this->attach_id);
    157         $post_updated = false;
    158 
    159         if (isset($this->generated['caption']) && false !== $this->generated['caption'])
    160         {
    161             $post->post_excerpt = $this->generated['caption'];
    162             $post_updated = true;
    163         }
    164 
    165         if (isset($this->generated['description']) && false !== $this->generated['description'])
    166         {
    167             $post->post_content = $this->generated['description'];
    168             $post_updated = true;
    169         }
    170 
    171         if (true === $post_updated)
    172         {
    173             wp_update_post($post);
    174         } */
    175238    }
    176239
    177240    protected function updateWPPost($data)
    178241    {
    179      
    180       //  Log::addTemp('Update WpPost', $data);
    181242        $post = get_post($this->attach_id);
    182243        $post_updated = false;
    183244
    184         if (isset($data['caption']) && false !== $data['caption'])
     245        if (isset($data['caption']) && false !== $data['caption'] && false === is_int($data['caption']))
    185246        {
    186247            $post->post_excerpt = $data['caption'];
     
    188249        }
    189250
    190         if (isset($data['description']) && false !== $data['description'])
     251        if (isset($data['description']) && false !== $data['description'] && false === is_int($data['description']))
    191252        {
    192253            $post->post_content = $data['description'];
     
    203264    {
    204265        Log::addTemp('Update WpMeta', $data);
    205         if (isset($data['alt']) && false !== $data['alt'])
     266        if (isset($data['alt']) && false !== $data['alt'] && false === is_int($data['alt']))
    206267        {
    207268            $bool = update_post_meta($this->attach_id, '_wp_attachment_image_alt', $data['alt']);
     
    314375        if (true === $this->has_record)
    315376        {
     377             $this->processable_status = SELF::P_ALREADYDONE;
    316378             return false;
    317379        }
     380
     381        // Stash here other conditions on top with && to build a big processable function
     382        $processable = ( $this->isExifProcesssable() && $this->isExtensionIncluded() && $this->hasSomethingGeneratable() ) ? true : false;
     383        return $processable;
     384    }
     385
     386
     387    private function isExifProcesssable()
     388    {
     389        $fs = \wpSPIO()->filesystem();
     390        $imageModel = $fs->getMediaImage($this->attach_id);
     391
     392        if (false === $imageModel->isSomethingOptimized())
     393        {
     394            return true;
     395        }
     396
     397        $imageObj = $imageModel->getSomethingOptimized();
     398       
     399
     400        $keepExif = $imageObj->getMeta('did_keepExif');
     401
     402        // 2-3 are exif_ai combined settings with keep-exif. 0-1 are when default settings are used and unset / unused
     403        if (in_array($keepExif, [0,1,2,3]))
     404        {
     405            return true;
     406        }
     407
     408        $this->processable_status = self::P_EXIFAI;
     409        return false;
     410
     411    }
     412
     413    public function getProcessableReason($returnStatus = false )
     414    {
     415        $message = false;
     416       
     417        if (true === $returnStatus)
     418        {
     419            return $this->processable_status;
     420        }
     421
     422        switch($this->processable_status)
     423        {
     424            case self::P_PROCESSABLE:
     425                $message = __('AI is processable', 'shortpixel-image-optimiser');
     426            break;
     427            case self::P_ALREADYDONE:
     428                $message = __('This image already has generated data', 'shortpixel-image-optimiser');
     429            break;
     430            case self::P_EXIFAI:
     431                $message = __('Image Exif settings restrict AI usage', 'shortpixel-image-optimiser');
     432            break;
     433            case self::P_EXTENSION:
     434                 $message = __('File Extension not supported', 'shortpixel-image-optimiser');
     435            break;
     436            case self::P_NOJOB:
     437                $message = __('No fields to generate', 'shortpixel-image-optimiser');
     438            break;
     439            default:
     440                 $message = sprintf(__('Status %s unknown', 'shortpixel-image-optimiser'), $this->processable_status);
     441            break;
     442        }
     443
     444        return $message;
     445    }
     446
     447    protected function isExtensionIncluded()
     448    {
     449        $fs = \wpSPIO()->filesystem();
     450        $imageModel = $fs->getMediaImage($this->attach_id);
     451       
     452        // Gif removed here, since we (temporarily don't support it)
     453        $extensions = ['png', 'jpeg', 'webp', 'jpg'];
     454
     455        if (in_array($imageModel->getExtension(), $extensions))
     456        {
     457            return true;
     458        }
     459
     460        $this->processable_status = self::P_EXTENSION;
     461        return false;
     462    }
     463
     464    protected function hasSomethingGeneratable()
     465    {
     466        $optimizeData = $this->getOptimizeData();
     467
     468        if (self::P_NOJOB === $this->processable_status)
     469        {
     470             return false;
     471
     472        }
    318473        return true;
    319     }
    320 
    321     public function supportedExtensions()
    322     {
    323          return ['png', 'jpeg', 'gif', 'webp', 'jpg'];
    324474    }
    325475
     
    429579
    430580   
    431     /*public static function getAiDataByAttachment($attach_id)
    432     {
    433 
    434     } */
     581    public static function getModelByAttachment($attach_id, $type = 'media')
     582    {
     583        if (false === isset(self::$models[$attach_id]))
     584        {
     585             self::$models[$attach_id]  = new AiDataModel($attach_id, $type);
     586        }
     587
     588        return self::$models[$attach_id];
     589
     590    }
     591
     592    public static function flushModelCache($attach_id, $type = 'media')
     593    {
     594        if (isset(self::$models[$attach_id]))
     595        {
     596             unset(self::$models[$attach_id]);
     597        }
     598        else
     599        {
     600             Log::addTemp('Ai MODEL not found in cache!', $attach_id);
     601        }
     602
     603    }
    435604
    436605
  • shortpixel-image-optimiser/trunk/class/Model/Image/ImageModel.php

    r3346588 r3363822  
    478478                if (! isset($optimizeData['params']) || ! isset($optimizeData['urls']))
    479479                {
    480                     array(array(), 0);
     480                    array([], 0);
    481481                }
    482482
    483483                $count = 0;
    484                 $urls = array();
     484                $urls = [];
    485485                $i = 0;
    486486
  • shortpixel-image-optimiser/trunk/class/Model/Queue/QueueItem.php

    r3346588 r3363822  
    1717use ShortPixel\Controller\Optimizer\OptimizeAiController as OptimizeAiController;
    1818use ShortPixel\Controller\Optimizer\ActionController as ActionController;
     19use ShortPixel\Model\AiDataModel;
    1920
    2021class QueueItem
     
    176177      }
    177178
    178 
    179179      $enqueue = ['id' => $item_id, 'value' => $value, 'item_count' => $this->item_count];
    180180     
     
    185185
    186186      return $enqueue;
    187 
    188187     
    189188   }
     
    472471      $this->item_count = 1;
    473472
     473      $item_id = $this->imageModel->get('id');
     474
     475      $paramlist = [];
    474476
    475477      $preview_only = false;
    476478      if (isset($args['preview_only']) && true == $args['preview_only'])
    477479      {
    478          $this->data->paramlist = ['preview_only' => true];
     480         $paramlist['preview_only'] = true;
    479481         $preview_only = true;
    480482      }
    481483
     484      $aiDataModel = new AiDataModel($item_id);
     485     
     486      $data = $aiDataModel->getOptimizeData($args);
     487
     488      if (isset($data['paramlist']))
     489      {
     490         $this->data()->paramlist = $data['paramlist'];
     491      }
     492      if (isset($data['returndatalist']))
     493      {
     494         $this->data()->returndatalist = $data['returndatalist'];
     495         $this->data()->addKeepDataArgs('returndatalist');
     496      }
     497
     498
    482499      $this->data->action = 'requestAlt'; // For Queue
    483500
    484       $optimizer = $this->getAPIController($this->data->action);
    485       $optimizer->parseJSONForQItem($this, $args);
     501    //  $optimizer = $this->getAPIController($this->data->action);
     502   //   $optimizer->parseJSONForQItem($this, $args);
    486503
    487504      if ($this->data()->hasNextAction())
     
    498515         $this->data->next_actions = $next_actions;
    499516      }
     517
     518
    500519     
    501520   }
    502521
    503    public function retrieveAltAction($remote_id)
     522   public function retrieveAltAction($args)
    504523   {
    505524      $this->newAction();
     525
     526      $remote_id = $args['remote_id'];
     527     
     528      if (isset($args['returndatalist']))
     529      {
     530         $this->data()->returndatalist = $args['returndatalist'];
     531      }
     532
     533
    506534      $this->data->remote_id = $remote_id;
    507535      $this->data->tries = 0;
  • shortpixel-image-optimiser/trunk/class/Model/SettingsModel.php

    r3348311 r3363822  
    6868        'autoAI' => ['s' => 'boolean', 'default' => false],
    6969        'autoAIBulk' => ['s' => 'boolean', 'default' => false],
     70        'aiPreserve' => ['s' => 'boolean', 'default' => false ],
    7071        'ai_general_context' => ['s' => 'string', 'default' => 'callback', 'maxlength' => 500],
    7172        'ai_use_post' => ['s' => 'boolean', 'default' => true],
  • shortpixel-image-optimiser/trunk/class/external/offload/wp-offload-media.php

    r3289850 r3363822  
    355355                    }
    356356                }
    357             }
    358         }
    359 
     357
     358            }
     359
     360        }
    360361
    361362        return $source_id;
  • shortpixel-image-optimiser/trunk/class/external/wp-cli/wp-cli-bulk.php

    r3289850 r3363822  
    167167     * [--special=<migrate>]
    168168     * : Run the migration
    169      * ---
    170      *
     169     *
     170     * [--start-date=<start_date>]
     171     * : Filter, start from this date
     172     *
     173     * [--end-date=<end_date>]
     174     * : Filter, don't enqueue items old than this date.
     175     *
    171176     * ## EXAMPLES
    172177     *
     
    186191
    187192        $operation = null;
     193        $args = $filters = [];
    188194        if (isset($assoc['special'])) {
    189195            switch ($assoc['special']) {
    190196                case 'migrate':
    191197                    $operation = 'migrate';
     198                    $args['customOp'] = $operation;
    192199                    $queues = array('media'); // can only have one bulk, this.
    193200                    break;
     
    195202        }
    196203
     204        if (isset($assoc['start-date']))
     205        {
     206             $filters['start_date'] = sanitize_text_field($assoc['start-date']);
     207        }
     208        if (isset($assoc['end-date']))
     209        {
     210             $filters['end_date'] = sanitize_text_field($assoc['end-date']);
     211        }
     212
     213        if (count($filters) > 0)
     214        {
     215             $args['filters'] = $filters;
     216        }
     217
    197218        foreach ($queues as $qname) {
    198             $stats = $bulkControl->createNewBulk($qname, $operation);
     219            $stats = $bulkControl->createNewBulk($qname, $args);
    199220            $json->$qname->stats = $stats;
    200221
  • shortpixel-image-optimiser/trunk/class/view/bulk/part-selection.php

    r3346588 r3363822  
    11<?php
    22namespace ShortPixel;
     3
     4use ShortPixel\Controller\Optimizer\OptimizeAiController;
    35
    46if ( ! defined( 'ABSPATH' ) ) {
     
    4547
    4648
    47        </div>
     49      </div>
    4850
    4951       <div class="interface wrapper">
     
    103105                <?php endif; ?>
    104106
    105                 <?php if (true === \wpSPIO()->settings()->enable_ai):  ?>
     107                <?php
     108                $optimizeAiController = OptimizeAiController::getInstance();
     109                if (true === $optimizeAiController->isAiEnabled()):  ?>
    106110             <div class='ai-images optiongroup'>
    107111                <div class='switch_button'>
     
    118122                </div> 
    119123
     124                <div class='switch_button'>
     125                <label>
     126                       <input type="checkbox" class="switch" id="aipreserve_checkbox" name="aipreserve_checkbox"
     127                        <?php checked(\wpSPIO()->settings()->aiPreserve); ?>  />
     128                       <div class="the_switch">&nbsp; </div>
     129                 </label>
     130                 <h4><label for="aipreserve_checkbox">
     131                    <?php printf(esc_html__('Prevent overriding any of the existing data with the one generated by AI', 'shortpixel-image-optimiser'), '<a href="options-general.php?page=wp-shortpixel-settings&part=ai">', '</a>' ); ?>
     132                            <span class='new'><?php _e('New!', 'shortpixel-image-optimiser'); ?></span>
     133                 </label></h4>
     134
     135                </div> 
     136
    120137             </div>
     138
    121139            <?php endif ?>
    122140           
  • shortpixel-image-optimiser/trunk/class/view/settings/part-ai.php

    r3348311 r3363822  
    1212  <settinglist>
    1313
    14     <h2><?php esc_html_e('AI Image SEO', 'shortpixel-image-optimiser'); ?></h2>
     14    <h2><?php esc_html_e('AI Image SEO & Accessibility', 'shortpixel-image-optimiser'); ?></h2>
    1515
    1616    <gridbox class="width_half">
     
    2424            'checked' => $view->data->enable_ai,
    2525            'label' => esc_html__('Enable AI Image SEO', 'shortpixel-image-optimiser'),
    26           ]
     26            'data' => ['data-toggle="autoAiOptions"'],
     27
     28            ]
    2729        );
    2830        ?>
     
    3133        <name>
    3234
    33           <?php esc_html_e('Show AI image SEO options throughout ShortPixel Image Optimizer.', 'shortpixel-image-optimiser'); ?>
     35          <?php esc_html_e('Show AI image SEO options throughout ShortPixel Image Optimizer. The generated ALT tag is also very useful for accessibility.', 'shortpixel-image-optimiser'); ?>
    3436
    3537        </name>
     
    3739    </setting>
    3840
    39     <setting class='switch'>
     41    <setting class='switch toggleTarget autoAiOptions'>
    4042      <content>
    4143
     
    5860    </setting>
    5961
    60     <setting class='switch'>
     62    <setting class='switch toggleTarget autoAiOptions'>
    6163      <content>
    6264
     
    6567            'name' => 'autoAIBulk',
    6668            'checked' => $view->data->autoAIBulk,
    67             'label' => esc_html__('Generate image SEO data when running Bulk Processing', 'shortpixel-image-optimiser'),
     69            'label' => esc_html__('Generate image SEO data during Bulk Processing', 'shortpixel-image-optimiser'),
    6870          ]
    6971        );
     
    7880      </content>
    7981    </setting>
     82
     83
     84    <setting class='switch toggleTarget autoAiOptions'>
     85      <content>
     86
     87        <?php $this->printSwitchButton(
     88          [
     89            'name' => 'aiPreserve',
     90            'checked' => $view->data->aiPreserve,
     91            'label' => esc_html__('Preserve existing Image SEO data', 'shortpixel-image-optimiser'),
     92          ]
     93        );
     94        ?>
     95        <i class='documentation dashicons dashicons-editor-help' data-link="https://shortpixel.com/knowledge-base/article/ai-image-seo-settings-explained/#2-toc-title?target=iframe"></i>
     96
     97        <name>
     98
     99          <?php esc_html_e('When enabled, all existing ALT tags, captions and descriptions are retained. Disabling the switch means that the SEO data for images created with AI will overwrite the existing data.', 'shortpixel-image-optimiser'); ?>
     100
     101        </name>
     102      </content>
     103    </setting>
     104
    80105
    81106    </gridbox>
  • shortpixel-image-optimiser/trunk/class/view/snippets/part-aitext.php

    r3346588 r3363822  
    33use ShortPixel\Controller\ApiKeyController;
    44use ShortPixel\Controller\QuotaController;
     5use ShortPixel\Model\AiDataModel;
    56
    67$icon_url = plugins_url( '/res/images/icon/', SHORTPIXEL_PLUGIN_FILE );
     
    1011$item_id = $this->data['item_id'];
    1112$has_data = $this->data['has_data'];
    12 $isSupported = $this->data['isSupported'];
     13//$isSupported = $this->data['isSupported'];
    1314$isDifferent = $this->data['isDifferent'];
    1415$dataItems = implode(',',$this->data['dataItems']);
     16
     17$is_processable = $this->data['is_processable'];
     18$processable_reason = $this->data['processable_reason'];
     19$processable_status = $this->data['processable_status'];
    1520
    1621$quotaControl = QuotaController::getInstance();
     
    2328    <p class='hidden' id='shortpixel-noai'></p>
    2429<?php
     30endif;
    2531
    26 elseif (false === $isSupported):
     32if (false === $is_processable && $processable_status !== AiDataModel::P_ALREADYDONE):
    2733
    28     ?>
    29         <p><?php _e('Currently, ShortPixel AI cannot generate SEO data for GIF files.', 'shortpixel-image-optimiser'); ?></p>
    30     <?php
     34    if (true === in_array($processable_status, [AiDataModel::P_NOJOB])) // Silent fail if all done
     35    {
     36
     37    }
     38    else
     39    {
     40        printf('<p>%s</p>', $processable_reason);
     41    }
     42   
    3143
    3244elseif (false === $has_data):
  • shortpixel-image-optimiser/trunk/readme.txt

    r3348311 r3363822  
    55Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 6.3.3
     7Stable tag: 6.3.4
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    474474
    475475== Changelog ==
     476
     477= 6.3.4 =
     478
     479🤖 The Smarter AI Control Update
     480
     481Release Date: September 18, 2025
     482
     483✨ New Features & Improvements
     484
     485* Preserve Existing SEO Data: Added an option to keep your existing ALT, caption, and description fields untouched when using AI-generated image SEO — your manual work is safe!
     486* Better Block Editor Integration: AI-generated SEO data and image optimization now work flawlessly when uploading images directly from the block editor.
     487* AI Support Chatbot: Meet our new AI-powered support assistant — here to help you faster, 24/7.
     488
     489🛠️ Fixes & Behavioral Improvements
     490
     491* Full AI Deactivation: Disabling AI Image SEO now also disables auto-generation and hides all related options from WP Admin, including the Bulk Processing section.
     492* EXIF-Based AI Training Block: If AI training is blocked via EXIF data, the plugin now shows a clear message and prevents the image from being sent for processing.
     493* Re-Optimize Logic Fixed: The "Re-optimize with/without SmartCrop" buttons now respect the selected option instead of always using the saved setting.
     494* Inline Font Handling: Inline fonts will no longer be replaced with CDN links if the CSS option is disabled.
     495* AI SEO for Excluded Items: Bulk AI SEO generation now skips excluded items as expected.
     496
     497Update now for smarter AI behavior, more control over your data, and an improved support experience! 🚀
    476498
    477499= 6.3.3 =
  • shortpixel-image-optimiser/trunk/res/css/shortpixel-bulk.css

    r3348311 r3363822  
    6363}
    6464.wrap.is-shortpixel-bulk-page header .top-buttons a:hover, .wrap.is-shortpixel-bulk-page header .top-buttons button:hover {
    65   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     65  background: #32d7e5;
    6666}
    6767.wrap.is-shortpixel-bulk-page header .top-buttons a i.switch, .wrap.is-shortpixel-bulk-page header .top-buttons button i.switch {
     
    265265}
    266266.wrap.is-shortpixel-bulk-page section.panel.dashboard .bulk-wrapper button:hover {
    267   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     267  background: #32d7e5;
    268268}
    269269.wrap.is-shortpixel-bulk-page section.panel.dashboard .bulk-wrapper button:disabled {
     
    398398}
    399399.wrap.is-shortpixel-bulk-page .button-primary:hover {
    400   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     400  background: #32d7e5;
    401401}
    402402.wrap.is-shortpixel-bulk-page nav {
     
    605605}
    606606.wrap.is-shortpixel-bulk-page .processor-paused:hover {
    607   background: rgb(27.5926724138, 169.1163793103, 178.9073275862);
     607  background: #1ca9b3;
    608608}
    609609.wrap.is-shortpixel-bulk-page .processor-paused .dashicons {
     
    719719  opacity: 1;
    720720  height: auto;
     721  z-index: 10;
     722  position: relative;
    721723}
    722724.wrap.is-shortpixel-bulk-page section.panel.selection .load.wrapper .loading {
     
    12451247}
    12461248.wrap.is-shortpixel-bulk-page section.panel.finished nav button.finish:hover {
    1247   background: rgb(36.2392241379, 209.2887931034, 221.2607758621);
     1249  background: #24d1dd;
    12481250}
    12491251.wrap.is-shortpixel-bulk-page .part-debug {
  • shortpixel-image-optimiser/trunk/res/css/shortpixel-bulk.css.map

    r3348311 r3363822  
    1 {"version":3,"sourceRoot":"","sources":["../scss/elements/_fonts.scss","../scss/shortpixel-bulk.scss","../scss/bulk/_dashboard.scss","../scss/elements/_header.scss","../scss/elements/_mixins.scss","../scss/elements/_colors.scss","../scss/elements/_icons.scss","../scss/elements/_breakpoints.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAEA;EAEI;EACA;EACA;EACA;;AAGJ;EAGI;EACA;EACA;EACA;;ACXJ;ACLA;ADgBE;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmcE;AAiIF;;AEplBF;EAEI;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACH;;AAEM;EC5BL;EACA,YCKa;EDJb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACC;;AAKD;EACE;EACA;;AAEF;EAEE;EACA;EACA;;ADOM;EACE,QEXG;EFYH;EACA;EACP;EACA;;AG5BL;EAEE;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;;AACA;EACG;;AAOH;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AAIJ;EAGE;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;EACA;;AAGJ;EACE;EACA;EACA;;AJtDA;EAEG;EACA;EACA;;AAEA;EAAa;;AAIb;EAGK;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EACA,kBGtBA;EHwBA;EACA;EACA;EACP;EAEA;EACA;EACM;;AAEA;EACE;;AAID;EACE;EACA;;AAEF;EAEN;;AAML;EAGC,kBG1DS;EH2DT;;AACK;EACE;EACA;EACA;EACA;EACA;;AAKR;EACE;EACD;;AK7DD;EL2DA;IAIE;;;AAED;EACC;;AAIC;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;ADzFb;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAIF;EAEG;EACA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YItDiB;EJuDjB,OI1DU;EJ2DV;EACA;EACA;EACS;EACA;EACA;EACT;;AAEA;EACC,YIpES;EJqET;;AAGD;EAEC,YI1ES;;AJ4EV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YIvFS;EJwFT;EACA;EACA;EACA;;AACA;EAEE;;AAMN;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AACA;EACE;;AAIJ;EAAI;EAAuB;;AAK5B;EAGI;EACA;EACA;EACP;;AAGO;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACP;EACA;EACA;EACA;;AAEO;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AAQH;EAEI;;AACA;EAEE;EACA;EACA;EACA;EACE;;AAEF;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAEF;EAEE;EACA;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;;AAKG;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EOpgBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;APifnC;EAEE;EACD;;AAGD;EAEE;EACD;EACA;EACA;;AAIH;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAET;EACC;EACA;EACA;;AAGH;EACE;EACA;EACA;;AAIH;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAhJH;;AAiJG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE,YIttBS;EJutBT;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EArSH;;AAsSG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QI5xBD;EJ6xBC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AACL;EAEE;;AAIA;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEE;EACA;;AAKD;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;;AAIN;EAEC;EACA;EACA;;AAGK;EACI;EACA;EACA;EACP;EACA;;AAEO;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AACN;EACE;EACD;;AAMG;EACG;;AAKR;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EApmBH;;AAqmBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
     1{"version":3,"sourceRoot":"","sources":["../scss/elements/_fonts.scss","../scss/shortpixel-bulk.scss","../scss/bulk/_dashboard.scss","../scss/elements/_header.scss","../scss/elements/_mixins.scss","../scss/elements/_colors.scss","../scss/elements/_icons.scss","../scss/elements/_breakpoints.scss","../scss/elements/_animation.scss"],"names":[],"mappings":"AAEA;EAEI;EACA;EACA;EACA;;AAGJ;EAGI;EACA;EACA;EACA;;ACXJ;ACLA;ADgBE;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmcE;AAmIF;;AEtlBF;EAEI;EACA;EACA;EACA;EACA;EAEA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAEF;EACE;EACA;EACA;EACA;;AAEF;EACH;;AAEM;EC5BL;EACA,YCKa;EDJb;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACC;;AAKD;EACE;EACA;;AAEF;EAEE;EACA;EACA;;ADOM;EACE,QEXG;EFYH;EACA;EACP;EACA;;AG5BL;EAEE;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;;AACA;EACG;;AAOH;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AADF;EACE;;AAIJ;EAGE;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;EACA;;AAGJ;EACE;EACA;EACA;;AJtDA;EAEG;EACA;EACA;;AAEA;EAAa;;AAIb;EAGK;EACA;EACA;EACA;EACA;EACA;;AACA;EAEE;EACA;EACA;EAAS;;AAGX;EAEG;EACA,kBGtBA;EHwBA;EACA;EACA;EACP;EAEA;EACA;EACM;;AAEA;EACE;;AAID;EACE;EACA;;AAEF;EAEN;;AAML;EAGC,kBG1DS;EH2DT;;AACK;EACE;EACA;EACA;EACA;EACA;;AAKR;EACE;EACD;;AK7DD;EL2DA;IAIE;;;AAED;EACC;;AAIC;EAEG;EACA;EACA;;AACA;EAAK;EAAwB;;AAC7B;EACE;;AACA;EAAS;EAAqB;;AAI9B;EACE;EACA;;AAKF;EACE;EACA;EAGA;EACA;;AAHA;EAAgB;;AAChB;EAAgB;;AAGhB;EACG;EACA;EACA;;ADzFb;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEI;EACA;;AAGN;EACE;;AAIF;EAEG;EACA;;AAGL;EACC;EACA;EACA;EACA;EAEA;EACA;EACA,YItDiB;EJuDjB,OI1DU;EJ2DV;EACA;EACA;EACS;EACA;EACA;EACT;;AAEA;EACC,YIpES;EJqET;;AAGD;EAEC,YI1ES;;AJ4EV;EAEE;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;;AAID;EAEE,YIvFS;EJwFT;EACA;EACA;EACA;;AACA;EAEE;;AAMN;EACE;EAED;EACA;;AACA;EAEE;;AAIH;EACG;;AAGD;EACE;;AAGJ;EACG;EACA;;AAGJ;EACG;IACG;;EAEH;IACG;;;AAIN;EAEE;EACA;;AAID;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACC;EAEE;EACA;EACA;EACA;EACA;EACA;;AAEH;EAEE;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAED;EAEC;EACA;EACA;;AACA;EACC;;AACA;EAAW;;AACR;EAAS;EAA+B;;AAE3C;EAAQ;;AACR;EACE;EACD;;AACA;EACE;;AAIJ;EAAI;EAAuB;;AAK5B;EAGI;EACA;EACA;EACP;;AAGO;EAGG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGA;EAEE;EACA;EACA;EACN;EACA;;AAGG;EACI;EACA;EACA;EACA;EACA;EACA;;AACA;EAAU;;AACV;EAEN;EACA;;AAMH;EAEG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACP;EACA;EACA;EACA;;AAEO;EAEI;EACA;EACA;EACA;EACA;EACA;;AAGJ;EAEG;EAED;EACA;;AAKE;EAAK;;AACL;EACE;EACA;;AACA;EAAS;;AAGf;EACE;EACA;EAEA;EACL;EACK;EACA;EACA;EACA;EACA;EACL;EACA;EAEK;EACL;EACA;EACA;;AACA;EAAS;;AAMT;EAGE;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EAEE;;AACA;EAEG;EACA;EACA;;AAGH;EAAiB;;AAIvB;EAEE;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;;AACA;EACA;EACA;EACA;;AAEA;EAEC;EACA;EACC;;AAQH;EAEI;;AACA;EAEE;EACA;EACA;EACA;EACE;;AAEF;EAEG;EACA;EACA;EACA;EACA;;AAGH;EAEE;;AAEF;EAEE;EACA;;AAGF;EACE;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACN;;AAEI;EAEE;EACA;EACA;EACA;EACN;;AAoBG;EATH;;AAUG;EAAe;;AACf;EAAQ;;AAEX;EAEG;EACJ;EACI;;AAKA;EACE;EACL;EACA;;AAKE;EAEG;EACL;EACK;EACA;;AAKF;EAEG;EACA;EACA;EACA;EACA;EACA;;AACN;EACC;;AACA;EAAI;;AAEC;EACE;;AAEF;EOtgBV;EACI;EACA;EACA;EACA;;AAEL;EACC;IAAM;IAA2B;;;AAGlC;EACC;IAAM;IAA8B;;;AAGrC;EACC;IAAM;IAA+B;;;AAGtC;EACC;IAAM;IAAmC;;;AAG1C;EACC;IAAM;IAAgC;;;APmfnC;EAEE;EACD;;AAGD;EAEE;EACD;EACA;EACA;;AAIH;EAEE;EACA;;AACA;EACE;EACD;;AAED;EAAI;;AAWA;EACG;;AAEH;EACG;EACA;EACA;EACA;;AAEH;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAET;EACC;EACA;EACA;;AAGH;EACE;EACA;EACA;;AAIH;EAEE;;AAYI;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAlJH;;AAmJG;EAAe;;AACf;EAAQ;;AAEX;EAEI;EACA;EACA;;AACA;EACE;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;EACA;EACA;;AAKD;EACI;EACA;EACA;;AACA;EAEG;EACA;;AAIN;EAEG;EAEA;EACA;;AAEA;EACE;EACA;EACA;;AACA;EAEG;EACA;EACA;;AACA;EAEG;;AAIR;EAEG;EACA;EACA;EAEA;;AAOd;EAEE;EACA;EACA;EACA;;AAEA;EAAU;;AAGZ;EACI;EAEA;EAEL;;AACK;EAAM;;AACN;EACG;EACA;EACA;EACN;;AACM;EAAgB;EAAkB;;AACxC;EAAkB;;AAClB;EAAkB;;AAClB;EAAU;EAAc;;AAGrB;EACE;EACA;;AAIN;EAEG;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAEF;EACE;EACA;EACA;;AACA;EAAO;;AAET;EACE,YIxtBS;EJytBT;EACA;EACA;EACA;EACA;;AAGL;EAEI;EACA;EACA;;AASD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAvSH;;AAwSG;EAAe;;AACf;EAAQ;;AAGX;EAEE;EACA;EACA;EACA;EACA;EACJ;EACA;;AAEI;EACE;EACA;EACL;EAGA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGG;EACI;EACA;EACA;;AAEA;EACE;;AAEF;EACE,QI9xBD;EJ+xBC;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAEE;EACA;EACA;;AACA;EAAQ;;AACR;EACI;EACA;EACN;;AAEE;EACG;EACA;;AACL;EAEE;;AAIA;EAEE;EACA;EACA;EACA;EACA;EACL;;AAGK;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGP;EAEE;EACA;;AAKD;EAEE;;AAMJ;EACC;;AAIC;EACG;EACA;EACA;EACA;EACJ;;AAEA;EACG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;;AAIC;EAEG;EACA;EACA;EACA;;AAIN;EAEC;EACA;EACA;;AAGK;EACI;EACA;EACA;EACP;EACA;;AAEO;EACE;;AAEF;EACE;EACA;EACA;;AAEF;EAEE;EACA;EACA;EACA;EACA;;AAKR;EAIH;EACA;EAGK;;AAEL;EAIC;EACA;EACA;;AAID;EAEC;EACC;EACD;EACA;EACA;EACA;EACA;;AAED;EAEE;;AAIF;EAGC;;AASI;EAEG;EACA;EACA;EACA;;AAEA;EACE;EACR;EACA;EASA;EACA;;AATA;EAEC;;AAED;EAEC;;AAKF;EACE;EACA;EACA;EACA;EACA;EACA;;AAGK;EACE;EACA;EACA;EACA;EACA;EACA;;AAGL;EAEG;;AAEH;EAEE;;AACN;EACE;EACD;;AAMG;EACG;;AAKR;EACI;EACA;;AAYD;EAAQ;;AACR;EAAe;;AACf;EAAQ;;AAIR;EAtmBH;;AAumBG;EAAe;;AACf;EAAQ;;AAGX;EACI;;AAEF;EAEI;EACA;EACA;EACA;EACA;EACA;;AACA;EACE;;AAOV;EAEG;EACA;EACA;EACA;EACA;;AAKL;EAEE;EAEA;;AACA;EAAK;EACH;EACA;;AAEA;EAEE;EACA;EACA;;AAIJ;EAAW;;AAEX;EAEC;EACA;;AAEK;EACG;;AAEH;EACG;EACA;EACA;EACA;EACN;;AAEG;EACE;EAEA;;AACA;EACI;EACA;;AAEJ;EACI;;AAGX;EAEE;EACA","file":"shortpixel-bulk.css"}
  • shortpixel-image-optimiser/trunk/res/css/shortpixel-settings.css

    r3347742 r3363822  
    134134}
    135135.wrap.is-shortpixel-settings-page .shortpixel-settings button:hover, .wrap.is-shortpixel-settings-page .shortpixel-settings .button-setting:hover {
    136   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     136  background: #32d7e5;
    137137}
    138138.wrap.is-shortpixel-settings-page .shortpixel-settings button i.switch, .wrap.is-shortpixel-settings-page .shortpixel-settings .button-setting i.switch {
     
    240240}
    241241.wrap.is-shortpixel-settings-page .shortpixel-settings section.wrapper .setting-tab .save-buttons.saving button.save {
    242   background-color: rgb(14.3684210526, 104.4473684211, 111.6315789474);
     242  background-color: #0e6870;
    243243}
    244244.wrap.is-shortpixel-settings-page .shortpixel-settings section.wrapper .setting-tab .save-buttons.saving button.save i {
     
    461461}
    462462.wrap.is-shortpixel-settings-page header .top-buttons a:hover, .wrap.is-shortpixel-settings-page header .top-buttons button:hover {
    463   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     463  background: #32d7e5;
    464464}
    465465.wrap.is-shortpixel-settings-page header .top-buttons a i.switch, .wrap.is-shortpixel-settings-page header .top-buttons button i.switch {
     
    11921192}
    11931193.wrap.is-shortpixel-settings-page #tab-overview .wrapper .dashboard-button:hover {
    1194   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     1194  background: #32d7e5;
    11951195}
    11961196.wrap.is-shortpixel-settings-page #tab-overview .wrapper .dashboard-button i.switch {
     
    21432143  min-height: 44px;
    21442144  color: #1ABDCA;
    2145   background-color: rgb(232.25, 232.25, 232.25);
     2145  background-color: #e8e8e8;
    21462146  border-radius: 6px;
    21472147  font-weight: 700;
     
    21512151}
    21522152.wrap.is-shortpixel-settings-page #tab-tools .button:hover {
    2153   background: rgb(50.3421052632, 215.4868421053, 228.6578947368);
     2153  background: #32d7e5;
    21542154  color: #fff;
    21552155}
  • shortpixel-image-optimiser/trunk/res/js/screens/screen-bulk.js

    r3346588 r3363822  
    261261     data.webpActive = (document.getElementById('webp_checkbox').checked) ? true : false;
    262262     data.avifActive = (document.getElementById('avif_checkbox').checked) ? true : false;
     263     
    263264     if (null !== document.getElementById('autoai_checkbox'))
    264265     {
    265266        data.aiActive = (document.getElementById('autoai_checkbox').checked) ? true : false;
     267        data.aiPreserve = (document.getElementById('aipreserve_checkbox').checked) ? true : false;
    266268     }
    267269     else
    268270     {
    269271       data.aiActive = false;
     272     //  data.aiPreserve = false;
    270273     }
    271274     data.backgroundProcess = (document.getElementById('background_checkbox').checked) ? true : false;
     
    510513            }); // circles;
    511514    }
    512   DoSelection() // action to update response.
    513   {
    514       // @todo Check the future of this function, since checking this is now createBulk.
    515       var data = {screen_action: 'applyBulkSelection'}; //
    516       data.callback = 'shortpixel.applySelectionDone';
    517 
    518       data.mediaActive = (document.getElementById('media_checkbox').checked) ? true : false;
    519       data.customActive = (document.getElementById('custom_checkbox').checked) ? true : false;
    520       data.webpActive = (document.getElementById('webp_checkbox').checked) ? true : false;
    521       data.avifActive = (document.getElementById('avif_checkbox').checked) ? true : false;
    522       data.aiActive = (document.getElementById('autoai_checkbox').checked) ? true : false;
    523       data.backgroundProcess = (document.getElementById('background_checkbox').checked) ? true : false;
    524 
    525       window.addEventListener('shortpixel.applySelectionDone', function (e) { this.SwitchPanel('summary'); }.bind(this) , {'once': true} );
    526       this.processor.AjaxRequest(data);
    527 
    528   }
     515
    529516
    530517  UpdateStats(stats, type)
  • shortpixel-image-optimiser/trunk/res/js/screens/screen-item-base.js

    r3346588 r3363822  
    2727        }
    2828
    29 
    3029        // This is final, not more messing with this. In results (multiple) defined one level higher than result object, if single, it's in result.
    3130        var item_id = resultItem.item_id;
    3231        var message = resultItem.message;
    33 
    3432       
    3533        // This is the reporting element ( all the data, via getItemView? )
     
    6664        if ('ai' === apiName && typeof resultItem.aiData !== 'undefined')
    6765        {
    68            
    6966            if (null !== element)
    7067            {
     
    336333        window.addEventListener('shortpixel.HandleUndoAlt', function (event) {
    337334            var data = event.detail.media;
    338             var original = data.original;
     335            var original = data.current;
    339336   
    340337            if ('redo' == action_type)
  • shortpixel-image-optimiser/trunk/res/js/screens/screen-media.js

    r3347742 r3363822  
    7676        }
    7777
    78         if (typeof newAltText !== 'undefined')
     78        if (typeof newAltText !== 'undefined' || newAltText < 0)
    7979        {
    8080            var inputs = this.altInputNames;
     
    107107         let captionFields = ['attachment_caption', 'attachment-details-caption'];
    108108         let descriptionFields = ['attachment_content', 'attachment-details-description'];
    109 
    110 
    111109         
    112          if (typeof newCaption !== 'undefined')
     110         if (typeof newCaption !== 'undefined' || newCaption < 0)
    113111         {
    114112            for (var i = 0; i < captionFields.length; i++)
     
    120118                }               
    121119            }
    122 
    123                
    124120         }
    125121
    126          if (typeof newDescription !== 'undefined')
     122         if (typeof newDescription !== 'undefined' || newDescription < 0)
    127123         {
    128124            for (var i = 0; i < descriptionFields.length; i++)
     
    134130                }
    135131            }
    136 
    137 
    138132         }
    139 
    140133
    141134        if (null !== attachmentAlt)
     
    157150            window.addEventListener('shortpixel.AttachAiInterface', this.AttachAiInterface.bind(this), {once: true});
    158151        }
    159     /*  if (typeof aiData !== 'undefined')
    160         {
    161             this.processor.LoadItemView({ id: item_id, type: 'media' });
    162         } */
    163 
    164 
    165152    }
    166153
     
    291278        var res = super.HandleImage(resultItem, type);
    292279        var fileStatus = this.processor.fStatus[resultItem.fileStatus];
     280        var apiName = (typeof resultItem.apiName !== 'undefined') ? resultItem.apiName : 'optimize';
     281
    293282
    294283        // If image editor is active and file is being restored because of this reason ( or otherwise ), remove the warning if this one exists.
     
    299288            }
    300289        }
     290
     291        if (fileStatus == 'FILE_DONE' && apiName == 'ai')
     292        {
     293            this.UpdateGutenBerg(resultItem);
     294        }
    301295    }
    302296
     
    323317    ListenGallery() {
    324318        var self = this;
     319        var next_item_run_process = false;
    325320
    326321        if (this.settings.hide_spio_in_popups)
     
    349344
    350345                if (typeof this.fetchSPIOData === 'function') {
    351                     this.fetchSPIOData(this.model.get('id'));
    352                     this.spioBusy = true; // Note if this system turns out not to work, the perhaps render empties all if first was painted, second cancelled?
    353 
     346                    let attach_id = this.model.get('id');
     347
     348                    if (typeof attach_id !== 'undefined')
     349                    {
     350                        if (true === next_item_run_process )
     351                        {
     352                            window.ShortPixelProcessor.SetInterval(-1);
     353                            window.ShortPixelProcessor.RunProcess();
     354                            next_item_run_process = false;
     355                        }
     356                        else
     357                        {
     358                        this.fetchSPIOData(attach_id);
     359                        this.spioBusy = true; // Note if this system turns out not to work, the perhaps render empties all if first was painted, second cancelled?
     360                        }
     361                    }
     362                    else if (true == this.model.get('uploading'))
     363                    {
     364                        next_item_run_process = true;
     365                        console.log('Upload Start Detected');
     366                    }
     367                    else
     368                    {
     369                        console.log('Id not found on render');
     370                    }
    354371                }
    355372
     
    447464        wrapper.classList.add('shortpixel-ai-interface',element.getAttribute('id'));
    448465       
    449         wrapper.innerHTML = data.snippet;   
     466        wrapper.innerHTML = data.snippet;   
     467
     468
    450469        element.after(wrapper);
    451470
     
    579598    }
    580599
     600    UpdateGutenBerg(resultItem)
     601    {
     602       
     603        var attach_id = resultItem.item_id;
     604        var aiData = resultItem.aiData;
     605       
     606        if (! wp.data || ! wp.data.select('core'))
     607        {
     608            return false;
     609        }
     610
     611        console.log(wp.data.select( 'core/block-editor' ));
     612
     613        let blocks = wp.data.select( 'core/block-editor' ).getBlocks();
     614        console.log(blocks);
     615        for (let i = 0; i < blocks.length; i++)
     616        {
     617            let block = blocks[i];
     618
     619             if (block.attributes.id == attach_id)
     620             {
     621                //block.attributes.alt = "I CAME TO ALT";
     622                //block.attributes.caption = "CAPTION THIS";
     623                let clientId = block.clientId;
     624
     625                console.log('DATA DISPATCH ', clientId, aiData);               
     626                wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes( clientId,
     627                    aiData );
     628
     629             }
     630        }
     631    }
     632
    581633} // class
    582634
  • shortpixel-image-optimiser/trunk/res/scss/shortpixel-bulk.scss

    r3348311 r3363822  
    498498           opacity: 1;
    499499                     height: auto;
     500           z-index: 10;
     501           position: relative;
    500502        }
    501503      }
  • shortpixel-image-optimiser/trunk/shortpixel-plugin.php

    r3346588 r3363822  
    365365        wp_register_script('shortpixel-media', plugins_url('res/js/shortpixel-media.js',  SHORTPIXEL_PLUGIN_FILE), array('jquery'), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
    366366
    367         wp_register_script('shortpixel-inline-help', plugins_url('res/js/shortpixel-inline-help.js',  SHORTPIXEL_PLUGIN_FILE), array(), SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
     367        wp_register_script('shortpixel-inline-help', plugins_url('res/js/shortpixel-inline-help.js',  SHORTPIXEL_PLUGIN_FILE), [], SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
     368        wp_register_script('shortpixel-chatbot',
     369            apply_filters('shortpixel/plugin/nohelp', 'https://spcdn.shortpixel.ai/assets/js/ext/ai-chat-agent.js'), [], SHORTPIXEL_IMAGE_OPTIMISER_VERSION, true);
    368370
    369371        // This filter is from ListMediaViewController for the media library grid display, executive script in shortpixel-media.js.
     
    589591            $this->load_script( 'shortpixel-screen-nolist' ); // screen
    590592            $this->load_script( 'shortpixel-settings' );
     593            $this->load_script('shortpixel-chatbot');
    591594
    592595            // @todo Load onboarding only when no api key / onboarding required
     
    599602        } elseif ( $plugin_page == 'wp-short-pixel-bulk' ) {
    600603            $this->load_script( 'shortpixel-screen-bulk' );
     604            $this->load_script('shortpixel-chatbot');
    601605
    602606            $this->load_style( 'shortpixel-admin' );
     
    619623
    620624            $this->load_script( 'shortpixel-folderbrowser' );
     625            $this->load_script('shortpixel-chatbot');
    621626
    622627            $this->load_style( 'shortpixel-admin' );
  • shortpixel-image-optimiser/trunk/wp-shortpixel.php

    r3348311 r3363822  
    44 * Plugin URI: https://shortpixel.com/
    55 * Description: ShortPixel optimizes images automatically, while guarding the quality of your images. Check your <a href="https://plugins.trac.wordpress.org/wp-admin/options-general.php?page=wp-shortpixel-settings" target="_blank">Settings &gt; ShortPixel</a> page on how to start optimizing your image library and make your website load faster.
    6  * Version: 6.3.3
     6 * Version: 6.3.4
    77 * Author: ShortPixel - Convert WebP/AVIF & Optimize Images
    88 * Author URI: https://shortpixel.com
     
    3737define('SHORTPIXEL_PLUGIN_DIR', __DIR__);
    3838
    39 define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "6.3.3");
     39define('SHORTPIXEL_IMAGE_OPTIMISER_VERSION', "6.3.4");
    4040
    4141define('SHORTPIXEL_BACKUP', 'ShortpixelBackups');
Note: See TracChangeset for help on using the changeset viewer.