From 83856706805138b50d429698dcd654483fbb02b0 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 3 Apr 2017 14:56:24 -0700 Subject: [PATCH 1/3] Don't subscribe to all media - Publish brand assets globally in media publication - Publish product media in the `Products` and `Product` publications - Limit media to only published products --- server/publications/collections/media.js | 6 +-- server/publications/collections/product.js | 29 +++++++++++-- server/publications/collections/products.js | 46 +++++++++++++++++---- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/server/publications/collections/media.js b/server/publications/collections/media.js index 8dc2daeb2f2..ddd872fe39d 100644 --- a/server/publications/collections/media.js +++ b/server/publications/collections/media.js @@ -72,9 +72,7 @@ Meteor.publish("Media", function (shops) { }); } - return Media.find(selector, { - sort: { - "metadata.priority": 1 - } + return Media.find({ + "metadata.type": "brandAsset" }); }); diff --git a/server/publications/collections/product.js b/server/publications/collections/product.js index 022abf1d61d..a857cb50dcb 100644 --- a/server/publications/collections/product.js +++ b/server/publications/collections/product.js @@ -1,4 +1,4 @@ -import { Products, Revisions } from "/lib/collections"; +import { Media, Products, Revisions } from "/lib/collections"; import { Logger, Reaction } from "/server/api"; import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; @@ -156,7 +156,17 @@ Meteor.publish("Product", function (productId) { handle2.stop(); }); - return this.ready(); + const mediaCursor = Media.find({ + "metadata.productId": _id + }, { + sort: { + "metadata.priority": 1 + } + }); + + return [ + mediaCursor + ]; } // Revision control is disabled @@ -164,5 +174,18 @@ Meteor.publish("Product", function (productId) { } // Everyone else gets the standard, visibile products and variants - return Products.find(selector); + const productCursor = Products.find(selector); + + const mediaCursor = Media.find({ + "metadata.productId": _id + }, { + sort: { + "metadata.priority": 1 + } + }); + + return [ + productCursor, + mediaCursor + ]; }); diff --git a/server/publications/collections/products.js b/server/publications/collections/products.js index 3dce1e564fa..d42ccc0c753 100644 --- a/server/publications/collections/products.js +++ b/server/publications/collections/products.js @@ -1,4 +1,4 @@ -import { Products, Revisions } from "/lib/collections"; +import { Media, Products, Revisions } from "/lib/collections"; import { Reaction, Logger } from "/server/api"; import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; @@ -369,10 +369,25 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so return this.ready(); } // Revision control is disabled - return Products.find(newSelector, { + const productCursor = Products.find(newSelector, { sort: sort, limit: productScrollLimit }); + + const mediaCursor = Media.find({ + "metadata.productId": { + $in: productCursor.fetch().map((p) => p._id) + } + }, { + sort: { + "metadata.priority": 1 + } + }); + + return [ + productCursor, + mediaCursor + ]; } // Everyone else gets the standard, visible products @@ -406,11 +421,26 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so }); } // Returning Complete product tree for top level products to avoid sold out warning. - return Products.find( - { - $or: [ { _id: { $in: productIds } }, - { ancestors: { $in: productIds } } - ] - }); + const productCursor = Products.find({ + $or: [ + { _id: { $in: productIds } }, + { ancestors: { $in: productIds } } + ] + }); + + const mediaCursor = Media.find({ + "metadata.productId": { + $in: productCursor.fetch().map((p) => p._id) + } + }, { + sort: { + "metadata.priority": 1 + } + }); + + return [ + productCursor, + mediaCursor + ]; } }); From 9701b760152d1114f60509b3d01c450881bf6ebe Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 3 Apr 2017 16:03:43 -0700 Subject: [PATCH 2/3] De-duplicate fetch product media functionality Ensure permissions and workflows are checked when fetching the media. --- server/publications/collections/product.js | 76 +++++++++++++++------ server/publications/collections/products.js | 38 ++++------- 2 files changed, 71 insertions(+), 43 deletions(-) diff --git a/server/publications/collections/product.js b/server/publications/collections/product.js index a857cb50dcb..2703d2decf2 100644 --- a/server/publications/collections/product.js +++ b/server/publications/collections/product.js @@ -2,6 +2,46 @@ import { Media, Products, Revisions } from "/lib/collections"; import { Logger, Reaction } from "/server/api"; import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; +export function findProductMedia(publicationInstance, productIds) { + const shopId = Reaction.getShopId(); + const selector = {}; + + if (!shopId) { + return publicationInstance.ready(); + } + + if (Array.isArray(productIds)) { + selector["metadata.productId"] = { + $in: productIds + }; + } else { + selector["metadata.productId"] = productIds; + } + + if (shopId) { + selector["metadata.shopId"] = shopId; + } + + // Product editors can see both published and unpublished images + if (!Reaction.hasPermission(["createProduct"], publicationInstance.userId)) { + selector["metadata.workflow"] = { + $in: [null, "published"] + }; + } else { + // but no one gets to see archived images + selector["metadata.workflow"] = { + $nin: ["archived"] + }; + } + + return Media.find(selector, { + sort: { + "metadata.priority": 1 + } + }); +} + + /** * product detail publication * @param {String} productId - productId or handle @@ -71,7 +111,10 @@ Meteor.publish("Product", function (productId) { }; if (RevisionApi.isRevisionControlEnabled()) { - const handle = Products.find(selector).observeChanges({ + const productCursor = Products.find(selector); + const productIds = productCursor.map(p => p._id); + + const handle = productCursor.observeChanges({ added: (id, fields) => { const revisions = Revisions.find({ "documentId": id, @@ -156,33 +199,26 @@ Meteor.publish("Product", function (productId) { handle2.stop(); }); - const mediaCursor = Media.find({ - "metadata.productId": _id - }, { - sort: { - "metadata.priority": 1 - } - }); - return [ - mediaCursor + findProductMedia(this, productIds) ]; } - // Revision control is disabled - return Products.find(selector); + // Revision control is disabled, but is an admin + const productCursor = Products.find(selector); + const productIds = productCursor.map(p => p._id); + const mediaCursor = findProductMedia(this, productIds); + + return [ + productCursor, + mediaCursor + ]; } // Everyone else gets the standard, visibile products and variants const productCursor = Products.find(selector); - - const mediaCursor = Media.find({ - "metadata.productId": _id - }, { - sort: { - "metadata.priority": 1 - } - }); + const productIds = productCursor.map(p => p._id); + const mediaCursor = findProductMedia(this, productIds); return [ productCursor, diff --git a/server/publications/collections/products.js b/server/publications/collections/products.js index d42ccc0c753..60a385b2565 100644 --- a/server/publications/collections/products.js +++ b/server/publications/collections/products.js @@ -1,6 +1,7 @@ -import { Media, Products, Revisions } from "/lib/collections"; +import { Products, Revisions } from "/lib/collections"; import { Reaction, Logger } from "/server/api"; import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; +import { findProductMedia } from "./product"; // // define search filters as a schema so we can validate @@ -273,7 +274,8 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so } if (RevisionApi.isRevisionControlEnabled()) { - const handle = Products.find(newSelector).observeChanges({ + const productCursor = Products.find(newSelector); + const handle = productCursor.observeChanges({ added: (id, fields) => { const revisions = Revisions.find({ "$or": [ @@ -366,23 +368,20 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so handle2.stop(); }); - return this.ready(); + const mediaProductIds = productCursor.fetch().map((p) => p._id); + const mediaCursor = findProductMedia(this, mediaProductIds); + + return [ + mediaCursor + ]; } - // Revision control is disabled + // Revision control is disabled, but is admin const productCursor = Products.find(newSelector, { sort: sort, limit: productScrollLimit }); - - const mediaCursor = Media.find({ - "metadata.productId": { - $in: productCursor.fetch().map((p) => p._id) - } - }, { - sort: { - "metadata.priority": 1 - } - }); + const mediaProductIds = productCursor.fetch().map((p) => p._id); + const mediaCursor = findProductMedia(this, mediaProductIds); return [ productCursor, @@ -428,15 +427,8 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so ] }); - const mediaCursor = Media.find({ - "metadata.productId": { - $in: productCursor.fetch().map((p) => p._id) - } - }, { - sort: { - "metadata.priority": 1 - } - }); + const mediaProductIds = productCursor.fetch().map((p) => p._id); + const mediaCursor = findProductMedia(this, mediaProductIds); return [ productCursor, From 13809d1981e57529dd86632b3acf74bb039ee972 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Tue, 4 Apr 2017 14:45:59 -0700 Subject: [PATCH 3/3] Update logic of if / else inclusion / exclusion by workflow --- server/publications/collections/product.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/publications/collections/product.js b/server/publications/collections/product.js index 2703d2decf2..c5b9d0c7bc7 100644 --- a/server/publications/collections/product.js +++ b/server/publications/collections/product.js @@ -22,16 +22,14 @@ export function findProductMedia(publicationInstance, productIds) { selector["metadata.shopId"] = shopId; } + // No one needs to see archived images on products + selector["metadata.workflow"] = { + $nin: ["archived"] + }; + // Product editors can see both published and unpublished images if (!Reaction.hasPermission(["createProduct"], publicationInstance.userId)) { - selector["metadata.workflow"] = { - $in: [null, "published"] - }; - } else { - // but no one gets to see archived images - selector["metadata.workflow"] = { - $nin: ["archived"] - }; + selector["metadata.workflow"].$in = [null, "published"]; } return Media.find(selector, {