From f38c8fe453c12472c087e7418679911a433b60ef Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Fri, 24 Feb 2017 14:37:20 -0800 Subject: [PATCH 1/6] Update product handle when title changes Ensure the handle is always set to the slugified product title, custom input from user, the product id. --- .../plugins/core/revisions/server/hooks.js | 65 ++++++++++++++++++- .../containers/productDetailContainer.js | 6 +- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/revisions/server/hooks.js b/imports/plugins/core/revisions/server/hooks.js index 2bbbc0867f6..5cfc3829f46 100644 --- a/imports/plugins/core/revisions/server/hooks.js +++ b/imports/plugins/core/revisions/server/hooks.js @@ -3,6 +3,7 @@ import { diff } from "deep-diff"; import { Products, Revisions, Tags, Media } from "/lib/collections"; import { Logger } from "/server/api"; import { RevisionApi } from "../lib/api"; +import { getSlug } from "/lib/api"; function convertMetadata(modifierObject) { const metadata = {}; @@ -404,6 +405,8 @@ Products.before.update(function (userId, product, fieldNames, modifier, options) return true; } + const hasAncestors = Array.isArray(product.ancestors) && product.ancestors.length > 0; +console.log(modifier); for (const operation in modifier) { if (Object.hasOwnProperty.call(modifier, operation)) { if (!revisionModifier[operation]) { @@ -425,7 +428,7 @@ Products.before.update(function (userId, product, fieldNames, modifier, options) revisionModifier.$addToSet = {}; } revisionModifier.$addToSet[`documentData.${property}`] = modifier.$push[property]; - } else if (operation === "$set" && property === "price" && Array.isArray(product.ancestors) && product.ancestors.length) { + } else if (operation === "$set" && property === "price" && hasAncestors) { Revisions.update(revisionSelector, { $set: { "documentData.price": modifier.$set.price @@ -436,7 +439,7 @@ Products.before.update(function (userId, product, fieldNames, modifier, options) const priceRange = ProductRevision.getProductPriceRange(updateId); Meteor.call("products/updateProductField", updateId, "price", priceRange); - } else if (operation === "$set" && property === "isVisible" && Array.isArray(product.ancestors) && product.ancestors.length) { + } else if (operation === "$set" && property === "isVisible" && hasAncestors) { Revisions.update(revisionSelector, { $set: { "documentData.isVisible": modifier.$set.isVisible @@ -447,6 +450,64 @@ Products.before.update(function (userId, product, fieldNames, modifier, options) const priceRange = ProductRevision.getProductPriceRange(updateId); Meteor.call("products/updateProductField", updateId, "price", priceRange); + } else if (operation === "$set" && (property === "title" || property === "handle") && hasAncestors === false) { + // Special handling for product title and handle + // + // Summary: + // When a user updates the product title, if the handle matches the product id, + // then update the handle to be a sligified version of the title + // + // This block ensures that the handle is either a custom slug, slug of the title, or + // the _id of the product, but is never blank + + // New data + const newValue = modifier.$set[property]; + const newTitle = modifier.$set.title; + const newHandle = modifier.$set.handle; + + // Current revision data + const documentId = productRevision.documentId; + const slugDocId = getSlug(documentId); + const revisionTitle = productRevision.documentData.title; + const revisionHandle = productRevision.documentData.handle; + + // Checks + const hasNewHandle = _.isEmpty(newHandle) === false; + const hasExistingTitle = _.isEmpty(revisionTitle) === false; + const hasNewTitle = _.isEmpty(newTitle) === false; + const hasHandle = _.isEmpty(revisionHandle) === false; + const handleMatchesId = revisionHandle === documentId || revisionHandle === slugDocId || newValue === documentId || newValue === slugDocId; + + // Continue to set the title / handle as origionally requested + // Handle will get changed if conditions are met in the below if block + revisionModifier.$set[`documentData.${property}`] = newValue; + + if ((handleMatchesId || hasHandle === false) && (hasExistingTitle || hasNewTitle)) { + // Set the handle to be the slug of the product.title + // when documentId (product._id) matches the handle, then handle is enpty, and a title exists + revisionModifier.$set["documentData.handle"] = getSlug(newTitle || revisionTitle); + } else if (hasHandle === false && hasExistingTitle === false) { + // If the handle & title is empty, the handle becomes the product id + revisionModifier.$set["documentData.handle"] = documentId; + } else if (hasNewHandle === false && property === "handle") { + // If the handle is empty, the handle becomes the sligified product title, or document id if title does not exist. + // const newTitle = modifier.$set["title"]; + revisionModifier.$set["documentData.handle"] = hasExistingTitle ? getSlug(newTitle || revisionTitle) : documentId; + } + } else if (operation === "$unset" && property === "handle" && hasAncestors === false) { + // Special handling for product handle when it is going to be unset + // + // Summary: + // When a user updates the handle to a black string e.g. deltes all text in field in UI and saves, + // the handle will be adjusted so it will not be blank + const newValue = modifier.$unset[property]; + const revisionTitle = productRevision.documentData.title; + const hasExistingTitle = _.isEmpty(revisionTitle) === false; + + // If the new handle is going to be empty, the handle becomes the sligified product title, or document id if title does not exist. + if (_.isEmpty(newValue)) { + revisionModifier.$set["documentData.handle"] = hasExistingTitle ? getSlug(revisionTitle) : documentId; + } } else { // Let everything else through revisionModifier[operation][`documentData.${property}`] = modifier[operation][property]; diff --git a/imports/plugins/included/product-detail-simple/client/containers/productDetailContainer.js b/imports/plugins/included/product-detail-simple/client/containers/productDetailContainer.js index 3eb68a558df..0c036507a31 100644 --- a/imports/plugins/included/product-detail-simple/client/containers/productDetailContainer.js +++ b/imports/plugins/included/product-detail-simple/client/containers/productDetailContainer.js @@ -135,7 +135,11 @@ class ProductDetailContainer extends Component { } handleProductFieldChange = (productId, fieldName, value) => { - Meteor.call("products/updateProductField", productId, fieldName, value); + Meteor.call("products/updateProductField", productId, fieldName, value, (error) => { + if (error) { + Alerts.toast(error.message, "error"); + } + }); } handleViewContextChange = (event, value) => { From 1c2de4c08e178d0d76fedfbaf0db68e37368d088 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Fri, 24 Feb 2017 14:44:56 -0800 Subject: [PATCH 2/6] Remove log statement --- imports/plugins/core/revisions/server/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imports/plugins/core/revisions/server/hooks.js b/imports/plugins/core/revisions/server/hooks.js index 5cfc3829f46..eed14606cda 100644 --- a/imports/plugins/core/revisions/server/hooks.js +++ b/imports/plugins/core/revisions/server/hooks.js @@ -406,7 +406,7 @@ Products.before.update(function (userId, product, fieldNames, modifier, options) } const hasAncestors = Array.isArray(product.ancestors) && product.ancestors.length > 0; -console.log(modifier); + for (const operation in modifier) { if (Object.hasOwnProperty.call(modifier, operation)) { if (!revisionModifier[operation]) { From 65eb4f5ddaac017c3b7b0be921752fcbb535df88 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Wed, 1 Mar 2017 16:32:31 -0800 Subject: [PATCH 3/6] Redirect on handle change --- .../client/containers/publishContainer.js | 6 +++++- .../plugins/core/revisions/server/methods.js | 8 +++++++- .../client/containers/publishContainer.js | 18 ++++++++++++++++++ .../templates/products/productGrid/item.js | 2 +- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/imports/plugins/core/revisions/client/containers/publishContainer.js b/imports/plugins/core/revisions/client/containers/publishContainer.js index e065dfcf297..915c32f9dc6 100644 --- a/imports/plugins/core/revisions/client/containers/publishContainer.js +++ b/imports/plugins/core/revisions/client/containers/publishContainer.js @@ -23,12 +23,16 @@ class PublishContainer extends Component { const documentIdsSet = new Set(documentIds); // ensures they are unique documentIds = Array.from(documentIdsSet); Meteor.call("revisions/publish", documentIds, (error, result) => { - if (result === true) { + if (result && result.status === "success") { const message = i18next.t("revisions.changedPublished", { defaultValue: "Changes published successfully" }); Alerts.toast(message, "success"); + + if (this.props.onPublishSuccess) { + this.props.onPublishSuccess(result) + } } else { const message = i18next.t("revisions.noChangesPublished", { defaultValue: "There are no changes to publish" diff --git a/imports/plugins/core/revisions/server/methods.js b/imports/plugins/core/revisions/server/methods.js index 9fabae0925e..7f27387259b 100644 --- a/imports/plugins/core/revisions/server/methods.js +++ b/imports/plugins/core/revisions/server/methods.js @@ -109,10 +109,13 @@ Meteor.methods({ } let updatedDocuments = 0; + const previousDocuments = []; if (revisions) { for (const revision of revisions) { if (!revision.documentType || revision.documentType === "product") { + previousDocuments.push(Products.findOne(revision.documentId)); + const res = Products.update({ _id: revision.documentId }, { @@ -164,7 +167,10 @@ Meteor.methods({ } if (updatedDocuments > 0) { - return true; + return { + status: "success", + previousDocuments + }; } return false; diff --git a/imports/plugins/included/product-detail-simple/client/containers/publishContainer.js b/imports/plugins/included/product-detail-simple/client/containers/publishContainer.js index 4c9017952b2..b6a7ac850bf 100644 --- a/imports/plugins/included/product-detail-simple/client/containers/publishContainer.js +++ b/imports/plugins/included/product-detail-simple/client/containers/publishContainer.js @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from "react"; import { composeWithTracker } from "/lib/api/compose"; +import { Router } from "/client/api"; import { ReactionProduct } from "/lib/api"; import { Tags, Media } from "/lib/collections"; import PublishContainer from "/imports/plugins/core/revisions/client/containers/publishContainer"; @@ -23,10 +24,27 @@ class ProductPublishContainer extends Component { } } + handlePublishSuccess = (result) => { + if (result && result.status === "success" && this.props.product) { + const productDocument = result.previousDocuments.find((product) => this.props.product._id === product._id); + + if (this.props.product.handle !== productDocument.handle) { + const newProductPath = Router.pathFor("product", { + hash: { + handle: this.props.product.handle + } + }); + + window.location.href = newProductPath; + } + } + } + render() { return ( diff --git a/imports/plugins/included/product-variant/client/templates/products/productGrid/item.js b/imports/plugins/included/product-variant/client/templates/products/productGrid/item.js index 8a460c2a46b..47f3a53ed2b 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productGrid/item.js +++ b/imports/plugins/included/product-variant/client/templates/products/productGrid/item.js @@ -171,7 +171,7 @@ Template.productGridItems.events({ "dblclick [data-event-action=productClick]": function (event, template) { const instance = template; const product = instance.data; - const handle = product.handle; + const handle = product.__published && product.__published.handle || product.handle; Reaction.Router.go("product", { handle: handle From 8ca9c20fd48f6015093085b41a7594c6e7dc4859 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Thu, 2 Mar 2017 10:42:53 -0800 Subject: [PATCH 4/6] Fix product grid link to use published product handle, not revision --- .../products/productGrid/content.html | 2 +- .../templates/products/productGrid/content.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/imports/plugins/included/product-variant/client/templates/products/productGrid/content.html b/imports/plugins/included/product-variant/client/templates/products/productGrid/content.html index f461a99505c..ca5439f99bd 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productGrid/content.html +++ b/imports/plugins/included/product-variant/client/templates/products/productGrid/content.html @@ -1,6 +1,6 @@