|  | @@ -0,0 +1,274 @@
 | 
	
		
			
				|  |  | +<?php
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @file
 | 
	
		
			
				|  |  | + * Contains \Drupal\npq\Form\NodePublisherQueueForm.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace Drupal\images_styles_gen\Form;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +use Drupal\Core\Form\FormBase;
 | 
	
		
			
				|  |  | +use Drupal\Core\Form\FormStateInterface;
 | 
	
		
			
				|  |  | +use Drupal\Core\Queue\QueueFactory;
 | 
	
		
			
				|  |  | +use Drupal\Core\Queue\QueueInterface;
 | 
	
		
			
				|  |  | +use Drupal\Core\Queue\QueueWorkerInterface;
 | 
	
		
			
				|  |  | +use Drupal\Core\Queue\QueueWorkerManagerInterface;
 | 
	
		
			
				|  |  | +use Drupal\Core\Queue\SuspendQueueException;
 | 
	
		
			
				|  |  | +use Symfony\Component\DependencyInjection\ContainerInterface;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class ImgStylesGenQueueForm extends FormBase {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @var QueueFactory
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  protected $queueFactory;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * @var QueueWorkerManagerInterface
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  protected $queueManager;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * {@inheritdoc}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  public function __construct(QueueFactory $queue, QueueWorkerManagerInterface $queue_manager) {
 | 
	
		
			
				|  |  | +    $this->queueFactory = $queue;
 | 
	
		
			
				|  |  | +    $this->queueManager = $queue_manager;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * {@inheritdoc}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  public static function create(ContainerInterface $container) {
 | 
	
		
			
				|  |  | +    return new static(
 | 
	
		
			
				|  |  | +      $container->get('queue'),
 | 
	
		
			
				|  |  | +      $container->get('plugin.manager.queue_worker')
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * {@inheritdoc}.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  public function getFormId() {
 | 
	
		
			
				|  |  | +    return 'images_styles_gen_form';
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * {@inheritdoc}.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  public function buildForm(array $form, FormStateInterface $form_state) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $styles_storage = \Drupal::entityTypeManager()->getStorage('image_style');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $content_type_id = $form_state->getValue('node_type');
 | 
	
		
			
				|  |  | +    $style_id = $form_state->getValue('style');
 | 
	
		
			
				|  |  | +    $batch_size = (int)$form_state->getValue('batch_size');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $num_rows = 0;
 | 
	
		
			
				|  |  | +    if ( $content_type_id && $style_id ) {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      $database = \Drupal::database();
 | 
	
		
			
				|  |  | +      $query = $database->select('file_managed', 'fm');
 | 
	
		
			
				|  |  | +      $query->join('file_usage', 'fu', 'fm.fid = fu.fid');
 | 
	
		
			
				|  |  | +      $query->join('node', 'n', 'fu.id = n.nid');
 | 
	
		
			
				|  |  | +      $query
 | 
	
		
			
				|  |  | +        ->condition('fm.filemime', 'image/%', 'LIKE')
 | 
	
		
			
				|  |  | +        ->condition('fu.type', 'node')
 | 
	
		
			
				|  |  | +        ->condition('n.type', $content_type_id)
 | 
	
		
			
				|  |  | +        ->fields('fm', ['fid', 'uri'])
 | 
	
		
			
				|  |  | +        ->fields('fu', ['id']);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      $results = $query->execute();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      /** @var \Drupal\image\Entity\ImageStyle $style */
 | 
	
		
			
				|  |  | +      $style = $styles_storage->load($style_id);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      foreach ($results as $file) {
 | 
	
		
			
				|  |  | +        $destination = $style->buildUri($file->uri);
 | 
	
		
			
				|  |  | +        if (file_exists($destination)) continue; // if destination exists skip
 | 
	
		
			
				|  |  | +        $num_rows++;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // select from content type we'll generate derivatives
 | 
	
		
			
				|  |  | +    $contentTypes = \Drupal::service('entity.manager')->
 | 
	
		
			
				|  |  | +    getStorage('node_type')->loadMultiple();
 | 
	
		
			
				|  |  | +    $contentTypesList = [0 => "please choose a content type"];
 | 
	
		
			
				|  |  | +    foreach ($contentTypes as $contentType) {
 | 
	
		
			
				|  |  | +        $contentTypesList[$contentType->id()] = $contentType->label();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    $form['node_type'] = array(
 | 
	
		
			
				|  |  | +      "#type" => "select",
 | 
	
		
			
				|  |  | +      "#title" => "Content type",
 | 
	
		
			
				|  |  | +      '#options' => $contentTypesList,
 | 
	
		
			
				|  |  | +      '#ajax' => [
 | 
	
		
			
				|  |  | +        'callback' => '::countAjaxCallback', // don't forget :: when calling a class method.
 | 
	
		
			
				|  |  | +        //'callback' => [$this, 'myAjaxCallback'], //alternative notation
 | 
	
		
			
				|  |  | +        'disable-refocus' => FALSE, // Or TRUE to prevent re-focusing on the triggering element.
 | 
	
		
			
				|  |  | +        'event' => 'change',
 | 
	
		
			
				|  |  | +        'wrapper' => 'edit-help-wrapper', // This element is updated with this AJAX callback.
 | 
	
		
			
				|  |  | +        'progress' => [
 | 
	
		
			
				|  |  | +          'type' => 'throbber',
 | 
	
		
			
				|  |  | +          'message' => $this->t('counting entries...'),
 | 
	
		
			
				|  |  | +        ],
 | 
	
		
			
				|  |  | +      ]
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // select for which style will generate derivatives
 | 
	
		
			
				|  |  | +    $styles = $styles_storage->loadMultiple(); //, 'card_full', 'linkedmaterial_card'
 | 
	
		
			
				|  |  | +    $stylesList = [0 => "please choose a style"];
 | 
	
		
			
				|  |  | +    foreach ($styles as $style) {
 | 
	
		
			
				|  |  | +        $stylesList[$style->id()] = $style->label();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    $form['style'] = array(
 | 
	
		
			
				|  |  | +      "#type" => "select",
 | 
	
		
			
				|  |  | +      "#title" => "Images Styles",
 | 
	
		
			
				|  |  | +      '#options' => $stylesList,
 | 
	
		
			
				|  |  | +      '#ajax' => [
 | 
	
		
			
				|  |  | +        'callback' => '::countAjaxCallback', // don't forget :: when calling a class method.
 | 
	
		
			
				|  |  | +        //'callback' => [$this, 'myAjaxCallback'], //alternative notation
 | 
	
		
			
				|  |  | +        'disable-refocus' => FALSE, // Or TRUE to prevent re-focusing on the triggering element.
 | 
	
		
			
				|  |  | +        'event' => 'change',
 | 
	
		
			
				|  |  | +        'wrapper' => 'edit-help-wrapper', // This element is updated with this AJAX callback.
 | 
	
		
			
				|  |  | +        'progress' => [
 | 
	
		
			
				|  |  | +          'type' => 'throbber',
 | 
	
		
			
				|  |  | +          'message' => $this->t('counting entries...'),
 | 
	
		
			
				|  |  | +        ],
 | 
	
		
			
				|  |  | +      ]
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $form['batch_size'] = array(
 | 
	
		
			
				|  |  | +      '#type' => 'select',
 | 
	
		
			
				|  |  | +      '#title' => 'Batch size',
 | 
	
		
			
				|  |  | +      '#options' => array(
 | 
	
		
			
				|  |  | +        10 => '10',
 | 
	
		
			
				|  |  | +        100 => '100',
 | 
	
		
			
				|  |  | +        500 => '500',
 | 
	
		
			
				|  |  | +        1000 => '1000',
 | 
	
		
			
				|  |  | +        50000 => '5000',
 | 
	
		
			
				|  |  | +        10000 => '10000',
 | 
	
		
			
				|  |  | +        0 => 'no limit'
 | 
	
		
			
				|  |  | +      ),
 | 
	
		
			
				|  |  | +      '#ajax' => [
 | 
	
		
			
				|  |  | +        'callback' => '::countAjaxCallback', // don't forget :: when calling a class method.
 | 
	
		
			
				|  |  | +        //'callback' => [$this, 'myAjaxCallback'], //alternative notation
 | 
	
		
			
				|  |  | +        'disable-refocus' => FALSE, // Or TRUE to prevent re-focusing on the triggering element.
 | 
	
		
			
				|  |  | +        'event' => 'change',
 | 
	
		
			
				|  |  | +        'wrapper' => 'edit-help-wrapper', // This element is updated with this AJAX callback.
 | 
	
		
			
				|  |  | +        'progress' => [
 | 
	
		
			
				|  |  | +          'type' => 'throbber',
 | 
	
		
			
				|  |  | +          'message' => $this->t('counting entries...'),
 | 
	
		
			
				|  |  | +        ],
 | 
	
		
			
				|  |  | +      ]
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $form['help-wrapper'] = array(
 | 
	
		
			
				|  |  | +      '#type' => 'container'
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $form['help-wrapper']['help'] = array(
 | 
	
		
			
				|  |  | +      '#type' => 'markup',
 | 
	
		
			
				|  |  | +      // '#prefix' => '<div>',
 | 
	
		
			
				|  |  | +      '#markup' => $this->t('Please choose a content type and a style.'),
 | 
	
		
			
				|  |  | +      // '#suffix' => '</div>',
 | 
	
		
			
				|  |  | +      '#attributes' => array(
 | 
	
		
			
				|  |  | +        'id' => 'help'
 | 
	
		
			
				|  |  | +      )
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if ($num_rows) {
 | 
	
		
			
				|  |  | +      if($batch_size == 0){
 | 
	
		
			
				|  |  | +        $batch_size = $num_rows;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      $form['help-wrapper']['help']['#markup'] = $this->t('Submitting this form will process @batch_size of @number images.', array('@batch_size' => $batch_size, '@number' => $num_rows));
 | 
	
		
			
				|  |  | +      // $form['actions']['submit']['#disabled'] = FALSE;
 | 
	
		
			
				|  |  | +      // unset($form['actions']['submit']['#attributes']['disabled']);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $form['help-wrapper']['num_rows'] = array(
 | 
	
		
			
				|  |  | +      '#type' => 'hidden',
 | 
	
		
			
				|  |  | +      '#value' => $num_rows,
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // Actions
 | 
	
		
			
				|  |  | +    $form['actions']['#type'] = 'actions';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $form['actions']['submit'] = array(
 | 
	
		
			
				|  |  | +      '#type' => 'submit',
 | 
	
		
			
				|  |  | +      '#value' => $this->t('Create image derivatives'),
 | 
	
		
			
				|  |  | +      '#button_type' => 'primary',
 | 
	
		
			
				|  |  | +      // '#disabled' => true,
 | 
	
		
			
				|  |  | +      '#states' => [
 | 
	
		
			
				|  |  | +        //show this submit only if the num rows is not empty
 | 
	
		
			
				|  |  | +        'visible' => [
 | 
	
		
			
				|  |  | +          //don't mistake :input for the type of field. You'll always use
 | 
	
		
			
				|  |  | +          //:input here, no matter whether your source is a select, radio or checkbox element.
 | 
	
		
			
				|  |  | +          ':input[name="num_rows"]' => ['!value' => 0],
 | 
	
		
			
				|  |  | +        ],
 | 
	
		
			
				|  |  | +      ],
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    return $form;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Get the value from example select field and fill
 | 
	
		
			
				|  |  | +  // the textbox with the selected text.
 | 
	
		
			
				|  |  | +  public function countAjaxCallback(array &$form, FormStateInterface $form_state) {
 | 
	
		
			
				|  |  | +    $form['help-wrapper']['#id'] = 'edit-help-wrapper';
 | 
	
		
			
				|  |  | +    return $form['help-wrapper'];
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /**
 | 
	
		
			
				|  |  | +   * {@inheritdoc}
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  public function submitForm(array &$form, FormStateInterface $form_state) {
 | 
	
		
			
				|  |  | +    $content_type_id = $form_state->getValue('node_type');
 | 
	
		
			
				|  |  | +    $style_id = $form_state->getValue('style');
 | 
	
		
			
				|  |  | +    $batch_size = (int)$form_state->getValue('batch_size');
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $database = \Drupal::database();
 | 
	
		
			
				|  |  | +    $query = $database->select('file_managed', 'fm');
 | 
	
		
			
				|  |  | +    $query->join('file_usage', 'fu', 'fm.fid = fu.fid');
 | 
	
		
			
				|  |  | +    $query->join('node', 'n', 'fu.id = n.nid');
 | 
	
		
			
				|  |  | +    $query
 | 
	
		
			
				|  |  | +      ->condition('fm.filemime', 'image/%', 'LIKE')
 | 
	
		
			
				|  |  | +      ->condition('fu.type', 'node')
 | 
	
		
			
				|  |  | +      ->condition('n.type', $content_type_id)
 | 
	
		
			
				|  |  | +      ->fields('fm', ['fid', 'uri'])
 | 
	
		
			
				|  |  | +      ->fields('fu', ['id']);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $results = $query->execute();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $styles_storage = \Drupal::entityTypeManager()->getStorage('image_style');
 | 
	
		
			
				|  |  | +    /** @var \Drupal\image\Entity\ImageStyle $style */
 | 
	
		
			
				|  |  | +    $style = $styles_storage->load($style_id);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $operations = [];
 | 
	
		
			
				|  |  | +    foreach ($results as $file) {
 | 
	
		
			
				|  |  | +      $destination = $style->buildUri($file->uri);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (file_exists($destination)) continue; // skip existing files
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      $batch_item = new \stdClass();
 | 
	
		
			
				|  |  | +      $batch_item->style = $style;
 | 
	
		
			
				|  |  | +      $batch_item->uri = $file->uri;
 | 
	
		
			
				|  |  | +      $batch_item->destination = $destination;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      $operations[] = ['\Drupal\images_styles_gen\CreateImagesStyles::createStyles', [$batch_item]];
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if($batch_size && count($operations) >= $batch_size){
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    $batch = array(
 | 
	
		
			
				|  |  | +      'title' => t('Creating images derivatives ...'),
 | 
	
		
			
				|  |  | +      'operations' => $operations,
 | 
	
		
			
				|  |  | +      'finished' => '\Drupal\images_styles_gen\CreateImagesStyles::createStylesFinishedCallback',
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    batch_set($batch);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 |