Version 1
[yaffs-website] / web / modules / contrib / dropzonejs / src / Element / DropzoneJs.php
diff --git a/web/modules/contrib/dropzonejs/src/Element/DropzoneJs.php b/web/modules/contrib/dropzonejs/src/Element/DropzoneJs.php
new file mode 100644 (file)
index 0000000..c4ba01e
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+
+namespace Drupal\dropzonejs\Element;
+
+use Drupal\Component\Utility\Bytes;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\FormElement;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Url;
+
+/**
+ * Provides a DropzoneJS atop of the file element.
+ *
+ * Configuration options are:
+ * - #title
+ *   The main field title.
+ * - #description
+ *   Description under the field.
+ * - #dropzone_description
+ *   Will be visible inside the upload area.
+ * - #max_filesize
+ *   Used by dropzonejs and expressed in number + unit (i.e. 1.1M) This will be
+ *   converted to a form that DropzoneJs understands. See:
+ *   http://www.dropzonejs.com/#config-maxFilesize
+ * - #extensions
+ *   A string of valid extensions separated by a space.
+ * - #max_files
+ *   Number of files that can be uploaded.
+ *   If < 1, there is no limit.
+ *
+ * When submitted the element returns an array of temporary file locations. It's
+ * the duty of the environment that implements this element to handle the
+ * uploaded files.
+ *
+ * @FormElement("dropzonejs")
+ */
+class DropzoneJs extends FormElement {
+
+  /**
+   * A defualut set of valid extensions.
+   */
+  const DEFAULT_VALID_EXTENSIONS = 'jpg jpeg gif png txt doc xls pdf ppt pps odt ods odp';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+    return [
+      '#input' => TRUE,
+      '#process' => [[$class, 'processDropzoneJs']],
+      '#pre_render' => [[$class, 'preRenderDropzoneJs']],
+      '#theme' => 'dropzonejs',
+      '#theme_wrappers' => ['form_element'],
+      '#tree' => TRUE,
+      '#attached' => [
+        'library' => ['dropzonejs/integration'],
+      ],
+    ];
+  }
+
+  /**
+   * Processes a dropzone upload element.
+   */
+  public static function processDropzoneJs(&$element, FormStateInterface $form_state, &$complete_form) {
+    $element['uploaded_files'] = [
+      '#type' => 'hidden',
+      // @todo Handle defaults.
+      '#default_value' => '',
+      // If we send a url with a token through drupalSettings the placeholder
+      // doesn't get replaced, because the actual scripts markup is not there
+      // yet. So we pass this information through a data attribute.
+      '#attributes' => ['data-upload-path' => Url::fromRoute('dropzonejs.upload')->toString()],
+    ];
+
+    if (empty($element['#max_filesize'])) {
+      $element['#max_filesize'] = file_upload_max_size();
+    }
+
+    // Set #max_files to NULL (explicitly unlimited) if #max_files is not
+    // specified.
+    if (empty($element['#max_files'])) {
+      $element['#max_files'] = NULL;
+    }
+
+    if (!\Drupal::currentUser()->hasPermission('dropzone upload files')) {
+      $element['#access'] = FALSE;
+      drupal_set_message(new TranslatableMarkup("You don't have sufficent permissions to use the DropzoneJS uploader. Contact your system administrator"), 'warning');
+    }
+
+    return $element;
+  }
+
+  /**
+   * Prepares a #type 'dropzone' render element for dropzonejs.html.twig.
+   *
+   * @param array $element
+   *   An associative array containing the properties of the element.
+   *   Properties used: #title, #description, #required, #attributes,
+   *   #dropzone_description, #max_filesize.
+   *
+   * @return array
+   *   The $element with prepared variables ready for input.html.twig.
+   */
+  public static function preRenderDropzoneJs(array $element) {
+    // Convert the human size input to bytes, convert it to MB and round it.
+    $max_size = round(Bytes::toInt($element['#max_filesize']) / pow(Bytes::KILOBYTE, 2), 2);
+
+    $element['#attached']['drupalSettings']['dropzonejs'] = [
+      'instances' => [
+        // Configuration keys are matched with DropzoneJS configuration
+        // options.
+        $element['#id'] => [
+          'maxFilesize' => $max_size,
+          'dictDefaultMessage' => Html::escape($element['#dropzone_description']),
+          'acceptedFiles' => '.' . str_replace(' ', ',.', self::getValidExtensions($element)),
+          'maxFiles' => $element['#max_files'],
+        ],
+      ],
+    ];
+
+    static::setAttributes($element, ['dropzone-enable']);
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
+    $return['uploaded_files'] = [];
+
+    if ($input !== FALSE) {
+      $user_input = NestedArray::getValue($form_state->getUserInput(), $element['#parents'] + ['uploaded_files']);
+
+      if (!empty($user_input['uploaded_files'])) {
+        $file_names = array_filter(explode(';', $user_input['uploaded_files']));
+        $tmp_upload_scheme = \Drupal::configFactory()->get('dropzonejs.settings')->get('tmp_upload_scheme');
+
+        foreach ($file_names as $name) {
+          // The upload handler appended the txt extension to the file for
+          // security reasons. We will remove it in this callback.
+          $old_filepath = $tmp_upload_scheme . '://' . $name;
+
+          // The upload handler appended the txt extension to the file for
+          // security reasons. Because here we know the acceptable extensions
+          // we can remove that extension and sanitize the filename.
+          $name = self::fixTmpFilename($name);
+          $name = file_munge_filename($name, self::getValidExtensions($element));
+
+          // Potentially we moved the file already, so let's check first whether
+          // we still have to move.
+          if (file_exists($old_filepath)) {
+            // Finaly rename the file and add it to results.
+            $new_filepath = $tmp_upload_scheme . '://' . $name;
+            $move_result = file_unmanaged_move($old_filepath, $new_filepath);
+
+            if ($move_result) {
+              $return['uploaded_files'][] = [
+                'path' => $move_result,
+                'filename' => $name,
+              ];
+            }
+            else {
+              drupal_set_message(self::t('There was a problem while processing the file named @name', ['@name' => $name]), 'error');
+            }
+          }
+        }
+      }
+      $form_state->setValueForElement($element, $return);
+    }
+    return $return;
+  }
+
+  /**
+   * Gets valid file extensions for this element.
+   *
+   * @param array $element
+   *   The element array.
+   *
+   * @return string
+   *   A space separated list of extensions.
+   */
+  public static function getValidExtensions(array $element) {
+    return isset($element['#extensions']) ? $element['#extensions'] : self::DEFAULT_VALID_EXTENSIONS;
+  }
+
+  /**
+   * Fix temporary filename.
+   *
+   * The upload handler appended the txt extension to the file for
+   * security reasons.
+   *
+   * @param string $filename
+   *   The filename we need to fix.
+   *
+   * @return string
+   *   The fixed filename.
+   */
+  public static function fixTmpFilename($filename) {
+    $parts = explode('.', $filename);
+    array_pop($parts);
+    return implode('.', $parts);
+  }
+
+}