Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / comment / js / node-new-comments-link.es6.js
diff --git a/web/core/modules/comment/js/node-new-comments-link.es6.js b/web/core/modules/comment/js/node-new-comments-link.es6.js
new file mode 100644 (file)
index 0000000..9f1588f
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+ * @file
+ * Attaches behaviors for the Comment module's "X new comments" link.
+ *
+ * May only be loaded for authenticated users, with the History module
+ * installed.
+ */
+
+(function ($, Drupal, drupalSettings) {
+  /**
+   * Render "X new comments" links wherever necessary.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches new comment links behavior.
+   */
+  Drupal.behaviors.nodeNewCommentsLink = {
+    attach(context) {
+      // Collect all "X new comments" node link placeholders (and their
+      // corresponding node IDs) newer than 30 days ago that have not already
+      // been read after their last comment timestamp.
+      const nodeIDs = [];
+      const $placeholders = $(context)
+        .find('[data-history-node-last-comment-timestamp]')
+        .once('history')
+        .filter(function () {
+          const $placeholder = $(this);
+          const lastCommentTimestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+          const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+          if (Drupal.history.needsServerCheck(nodeID, lastCommentTimestamp)) {
+            nodeIDs.push(nodeID);
+            // Hide this placeholder link until it is certain we'll need it.
+            hide($placeholder);
+            return true;
+          }
+
+            // Remove this placeholder link from the DOM because we won't need
+            // it.
+          remove($placeholder);
+          return false;
+        });
+
+      if ($placeholders.length === 0) {
+        return;
+      }
+
+      // Perform an AJAX request to retrieve node read timestamps.
+      Drupal.history.fetchTimestamps(nodeIDs, () => {
+        processNodeNewCommentLinks($placeholders);
+      });
+    },
+  };
+
+  /**
+   * Hides a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   *
+   * @return {jQuery}
+   *   The placeholder element passed in as a parameter.
+   */
+  function hide($placeholder) {
+    return $placeholder
+      // Find the parent <li>.
+      .closest('.comment-new-comments')
+      // Find the preceding <li>, if any, and give it the 'last' class.
+      .prev().addClass('last')
+      // Go back to the parent <li> and hide it.
+      .end().hide();
+  }
+
+  /**
+   * Removes a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   */
+  function remove($placeholder) {
+    hide($placeholder).remove();
+  }
+
+  /**
+   * Shows a "new comment" element.
+   *
+   * @param {jQuery} $placeholder
+   *   The placeholder element of the new comment link.
+   *
+   * @return {jQuery}
+   *   The placeholder element passed in as a parameter.
+   */
+  function show($placeholder) {
+    return $placeholder
+      // Find the parent <li>.
+      .closest('.comment-new-comments')
+      // Find the preceding <li>, if any, and remove its 'last' class, if any.
+      .prev().removeClass('last')
+      // Go back to the parent <li> and show it.
+      .end().show();
+  }
+
+  /**
+   * Processes new comment links and adds appropriate text in relevant cases.
+   *
+   * @param {jQuery} $placeholders
+   *   The placeholder elements of the current page.
+   */
+  function processNodeNewCommentLinks($placeholders) {
+    // Figure out which placeholders need the "x new comments" links.
+    const $placeholdersToUpdate = {};
+    let fieldName = 'comment';
+    let $placeholder;
+    $placeholders.each((index, placeholder) => {
+      $placeholder = $(placeholder);
+      const timestamp = parseInt($placeholder.attr('data-history-node-last-comment-timestamp'), 10);
+      fieldName = $placeholder.attr('data-history-node-field-name');
+      const nodeID = $placeholder.closest('[data-history-node-id]').attr('data-history-node-id');
+      const lastViewTimestamp = Drupal.history.getLastRead(nodeID);
+
+      // Queue this placeholder's "X new comments" link to be downloaded from
+      // the server.
+      if (timestamp > lastViewTimestamp) {
+        $placeholdersToUpdate[nodeID] = $placeholder;
+      }
+      // No "X new comments" link necessary; remove it from the DOM.
+      else {
+        remove($placeholder);
+      }
+    });
+
+    // Perform an AJAX request to retrieve node view timestamps.
+    const nodeIDs = Object.keys($placeholdersToUpdate);
+    if (nodeIDs.length === 0) {
+      return;
+    }
+
+    /**
+     * Renders the "X new comments" links.
+     *
+     * Either use the data embedded in the page or perform an AJAX request to
+     * retrieve the same data.
+     *
+     * @param {object} results
+     *   Data about new comment links indexed by nodeID.
+     */
+    function render(results) {
+      for (const nodeID in results) {
+        if (results.hasOwnProperty(nodeID) && $placeholdersToUpdate.hasOwnProperty(nodeID)) {
+          $placeholdersToUpdate[nodeID]
+            .attr('href', results[nodeID].first_new_comment_link)
+            .text(Drupal.formatPlural(results[nodeID].new_comment_count, '1 new comment', '@count new comments'))
+            .removeClass('hidden');
+          show($placeholdersToUpdate[nodeID]);
+        }
+      }
+    }
+
+    if (drupalSettings.comment && drupalSettings.comment.newCommentsLinks) {
+      render(drupalSettings.comment.newCommentsLinks.node[fieldName]);
+    }
+    else {
+      $.ajax({
+        url: Drupal.url('comments/render_new_comments_node_links'),
+        type: 'POST',
+        data: { 'node_ids[]': nodeIDs, field_name: fieldName },
+        dataType: 'json',
+        success: render,
+      });
+    }
+  }
+}(jQuery, Drupal, drupalSettings));