diff --git a/imports/collections/defineCollections.js b/imports/collections/defineCollections.js index 30f0436b920..0ac3982a0cb 100644 --- a/imports/collections/defineCollections.js +++ b/imports/collections/defineCollections.js @@ -22,7 +22,6 @@ export default function defineCollections(db, collections) { Orders: db.collection("Orders"), Packages: db.collection("Packages"), Products: db.collection("Products"), - Revisions: db.collection("Revisions"), roles: db.collection("roles"), SellerShops: db.collection("SellerShops"), Shipping: db.collection("Shipping"), diff --git a/imports/plugins/core/revisions/client/components/publishControls.js b/imports/plugins/core/catalog/client/components/publishControls.js similarity index 78% rename from imports/plugins/core/revisions/client/components/publishControls.js rename to imports/plugins/core/catalog/client/components/publishControls.js index 8381547b691..0e977b59eae 100644 --- a/imports/plugins/core/revisions/client/components/publishControls.js +++ b/imports/plugins/core/catalog/client/components/publishControls.js @@ -4,14 +4,9 @@ import { Components } from "@reactioncommerce/reaction-components"; import { Button, FlatButton, - IconButton, - Divider, - DropDownMenu, - MenuItem, Switch, Icon } from "/imports/plugins/core/ui/client/components"; -import SimpleDiff from "./simpleDiff"; import { Translatable } from "/imports/plugins/core/ui/client/providers"; /** TMP **/ @@ -176,19 +171,6 @@ class PublishControls extends Component { return false; } - renderChanges() { - if (this.showDiffs) { - const diffs = this.props.revisions.map((revision) => ); - - return ( -
- {diffs} -
- ); - } - return null; - } - renderDeletionStatus() { if (this.hasChanges) { if (this.primaryRevision && this.primaryRevision.documentData.isDeleted) { @@ -232,48 +214,6 @@ class PublishControls extends Component { ); } - renderMoreOptionsButton() { - return ( - } - handleMenuItemChange={this.handleAction} - > - - - - - - - - - - - ); - } - renderViewControls() { if (this.props.showViewAsControls) { let tooltip = "Private"; @@ -304,19 +244,6 @@ class PublishControls extends Component { return null; } - renderUndoButton() { - return ( - - ); - } - renderArchiveButton() { return ( - {this.renderDeletionStatus()} - {this.renderUndoButton()} - {this.renderArchiveButton()} - {this.renderViewControls()} - {this.renderPublishButton()} - {/* this.renderMoreOptionsButton() */} - - ); - } - - return null; + return ( + + {this.renderDeletionStatus()} + {this.renderArchiveButton()} + {this.renderViewControls()} + {this.renderPublishButton()} + + ); } } diff --git a/imports/plugins/core/catalog/client/containers/publishContainer.js b/imports/plugins/core/catalog/client/containers/publishContainer.js new file mode 100644 index 00000000000..c9c1d21efc2 --- /dev/null +++ b/imports/plugins/core/catalog/client/containers/publishContainer.js @@ -0,0 +1,83 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { composeWithTracker } from "@reactioncommerce/reaction-components"; +import PublishControls from "../components/publishControls"; +import { Meteor } from "meteor/meteor"; +import TranslationProvider from "/imports/plugins/core/ui/client/providers/translationProvider"; +import { Reaction, i18next } from "/client/api"; + +/* + * PublishContainer is a container component connected to Meteor data source. + */ +class PublishContainer extends Component { + publishToCatalog(collection, documentIds) { + Meteor.call(`catalog/publish/${collection}`, documentIds, (error, result) => { + if (result) { + Alerts.toast(i18next.t("admin.catalogProductPublishSuccess", { defaultValue: "Product published to catalog" }), "success"); + } else if (error) { + Alerts.toast(error.message, "error"); + } + }); + } + + handlePublishClick = () => { + const productIds = this.props.documents + .filter((doc) => doc.type === "simple") + .map((doc) => doc._id); + + this.publishToCatalog("products", productIds); + } + + handlePublishActions = (event, action) => { + if (action === "archive" && this.props.onAction) { + this.props.onAction(event, action, this.props.documentIds); + } + } + + render() { + return ( + + + + ); + } +} + +PublishContainer.propTypes = { + documentIds: PropTypes.arrayOf(PropTypes.string), + documents: PropTypes.arrayOf(PropTypes.object), + isEnabled: PropTypes.bool, + isPreview: PropTypes.bool, + onAction: PropTypes.func, + onPublishSuccess: PropTypes.func, + onVisibilityChange: PropTypes.func, + product: PropTypes.object +}; + +function composer(props, onData) { + const viewAs = Reaction.getUserPreferences("reaction-dashboard", "viewAs", "administrator"); + + if (Array.isArray(props.documentIds) && props.documentIds.length) { + onData(null, { + documentIds: props.documentIds, + documents: props.documents, + isPreview: viewAs === "customer" + }); + + return; + } + + onData(null, { + isPreview: viewAs === "customer" + }); +} + +export default composeWithTracker(composer)(PublishContainer); diff --git a/imports/plugins/core/catalog/server/methods/catalog.app-test.js b/imports/plugins/core/catalog/server/methods/catalog.app-test.js index 5cf4b5247c3..1e975f99cfc 100644 --- a/imports/plugins/core/catalog/server/methods/catalog.app-test.js +++ b/imports/plugins/core/catalog/server/methods/catalog.app-test.js @@ -6,13 +6,12 @@ import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; import { Factory } from "meteor/dburles:factory"; import { Reaction } from "/server/api"; -import { Products, Revisions, Tags } from "/lib/collections"; +import { Products, Tags } from "/lib/collections"; import { expect } from "meteor/practicalmeteor:chai"; import { sinon } from "meteor/practicalmeteor:sinon"; import { Roles } from "meteor/alanning:roles"; import { addProduct, addProductSingleVariant } from "/server/imports/fixtures/products"; import Fixtures from "/server/imports/fixtures"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; Fixtures(); @@ -43,8 +42,6 @@ describe("core product methods", function () { beforeEach(function () { sandbox = sinon.sandbox.create(); - sandbox.stub(RevisionApi, "isRevisionControlEnabled", () => true); - Revisions.remove({}); }); afterEach(function () { @@ -166,7 +163,7 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not update individual variant by admin passing in full object", function (done) { + it("should update individual variant by admin passing in full object", function (done) { sandbox.stub(Reaction, "hasPermission", () => true); const product = addProduct(); let variant = Products.findOne({ ancestors: [product._id] }); @@ -174,55 +171,11 @@ describe("core product methods", function () { variant["price"] = 7; Meteor.call("products/updateVariant", variant); variant = Products.findOne({ ancestors: [product._id] }); - expect(variant.price).to.not.equal(7); - expect(variant.title).to.not.equal("Updated Title"); - - return done(); - }); - - it("should update individual variant revision by admin passing in full object", function (done) { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.find({ ancestors: [product._id] }).fetch()[0]; - variant["title"] = "Updated Title"; - variant["price"] = 7; - Meteor.call("products/updateVariant", variant); - const variantRevision = Revisions.find({ documentId: variant._id }).fetch()[0]; - expect(variantRevision.documentData.price).to.equal(7); - expect(variantRevision.documentData.title).to.equal("Updated Title"); + expect(variant.price).to.equal(7); + expect(variant.title).to.equal("Updated Title"); return done(); }); - - it("should not update individual variant by admin passing in partial object", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.findOne({ ancestors: [product._id] }); - Meteor.call("products/updateVariant", { - _id: variant._id, - title: "Updated Title", - price: 7 - }); - const updatedVariant = Products.findOne(variant._id); - expect(updatedVariant.price).to.not.equal(7); - expect(updatedVariant.title).to.not.equal("Updated Title"); - expect(updatedVariant.optionTitle).to.equal(variant.optionTitle); - }); - - it("should update individual variant revision by admin passing in partial object", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.find({ ancestors: [product._id] }).fetch()[0]; - Meteor.call("products/updateVariant", { - _id: variant._id, - title: "Updated Title", - price: 7 - }); - const updatedVariantRevision = Revisions.findOne({ documentId: variant._id }); - expect(updatedVariantRevision.documentData.price).to.equal(7); - expect(updatedVariantRevision.documentData.title).to.equal("Updated Title"); - expect(updatedVariantRevision.documentData.optionTitle).to.equal(variant.optionTitle); - }); }); describe("products/deleteVariant", function () { @@ -235,38 +188,16 @@ describe("core product methods", function () { expect(removeProductSpy).to.not.have.been.called; }); - it("should not mark top-level variant as deleted", function () { + it("should mark top-level variant as deleted", function () { sandbox.stub(Reaction, "hasPermission", () => true); const product = addProduct(); let variant = Products.findOne({ ancestors: [product._id] }); expect(variant.isDeleted).to.equal(false); Meteor.call("products/deleteVariant", variant._id); variant = Products.findOne(variant._id); - expect(variant.isDeleted).to.not.equal(true); + expect(variant.isDeleted).to.equal(true); }); - it("should mark top-level variant revision as deleted", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.findOne({ ancestors: [product._id] }); - expect(variant.isDeleted).to.equal(false); - Meteor.call("products/deleteVariant", variant._id); - const variantRevision = Revisions.findOne({ documentId: variant._id }); - expect(variantRevision.documentData.isDeleted).to.equal(true); - }); - - it("should publish top-level variant as deleted", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.findOne({ ancestors: [product._id] }); - expect(variant.isDeleted).to.equal(false); - Meteor.call("products/deleteVariant", variant._id); - Meteor.call("revisions/publish", variant._id); - const publishedProduct = Products.findOne(variant._id); - expect(publishedProduct.isDeleted).to.equal(true); - }); - - it("should mark all child variants (options) as deleted if top-level variant deleted", function () { sandbox.stub(Reaction, "hasPermission", () => true); const product = addProduct(); @@ -435,28 +366,11 @@ describe("core product methods", function () { expect(removeProductSpy).to.not.have.been.called; }); - it("should not mark product as deleted by admin", function () { + it("should mark product as deleted by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); Meteor.call("products/archiveProduct", product._id); product = Products.findOne(product._id); - expect(product.isDeleted).to.equal(false); - }); - - it("should mark product revision as deleted by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - Meteor.call("products/archiveProduct", product._id); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.isDeleted).to.equal(true); - }); - - it("should publish product revision marked as deleted by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/archiveProduct", product._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); expect(product.isDeleted).to.equal(true); }); }); @@ -473,55 +387,19 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not update product field by admin", function () { + it("should update product field by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); Meteor.call("products/updateProductField", product._id, "title", "Updated Title"); product = Products.findOne(product._id); - expect(product.title).to.not.equal("Updated Title"); - }); - - it("should update product revision field by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "Updated Title"); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.title).to.equal("Updated Title"); - }); - - it("should publish changes to product field by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "Updated Title"); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); expect(product.title).to.equal("Updated Title"); }); - it("should not update variant fields", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - let variant = Products.findOne({ ancestors: [product._id] }); - Meteor.call("products/updateProductField", variant._id, "title", "Updated Title"); - variant = Products.findOne(variant._id); - expect(variant.title).to.not.equal("Updated Title"); - }); - - it("should update variant revision fields", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const variant = Products.findOne({ ancestors: [product._id] }); - Meteor.call("products/updateProductField", variant._id, "title", "Updated Title"); - const variantRevision = Revisions.findOne({ documentId: variant._id }); - expect(variantRevision.documentData.title).to.equal("Updated Title"); - }); - - it("should publish update for variant fields", function () { + it("should update variant fields", function () { sandbox.stub(Reaction, "hasPermission", () => true); const product = addProduct(); let variant = Products.findOne({ ancestors: [product._id] }); Meteor.call("products/updateProductField", variant._id, "title", "Updated Title"); - Meteor.call("revisions/publish", product._id); variant = Products.findOne(variant._id); expect(variant.title).to.equal("Updated Title"); }); @@ -542,31 +420,7 @@ describe("core product methods", function () { expect(insertTagsSpy).to.not.have.been.called; }); - it("should not new tag when passed tag name and null ID by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const tagName = "Product Tag"; - expect(Tags.findOne({ name: tagName })).to.be.undefined; - Meteor.call("products/updateProductTags", product._id, tagName, null); - const tag = Tags.findOne({ name: tagName }); - expect(tag.slug).to.equal(Reaction.getSlug(tagName)); - product = Products.findOne(product._id); - expect(product.hashtags).to.not.contain(tag._id); - }); - - it("should add new tag to product revision when passed tag name and null ID by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const tagName = "Product Tag"; - expect(Tags.findOne({ name: tagName })).to.be.undefined; - Meteor.call("products/updateProductTags", product._id, tagName, null); - const tag = Tags.findOne({ name: tagName }); - expect(tag.slug).to.equal(Reaction.getSlug(tagName)); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.hashtags).to.contain(tag._id); - }); - - it("should publish new product tag when passed tag name and null ID by admin", function () { + it("should create new tag when passed tag name and null ID by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); const tagName = "Product Tag"; @@ -574,36 +428,11 @@ describe("core product methods", function () { Meteor.call("products/updateProductTags", product._id, tagName, null); const tag = Tags.findOne({ name: tagName }); expect(tag.slug).to.equal(Reaction.getSlug(tagName)); - Meteor.call("revisions/publish", product._id); product = Products.findOne(product._id); expect(product.hashtags).to.contain(tag._id); }); - it("should not add existing tag when passed existing tag and tag._id by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const tag = Factory.create("tag"); - expect(Tags.find().count()).to.equal(1); - expect(product.hashtags).to.not.contain(tag._id); - Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); - expect(Tags.find().count()).to.equal(1); - product = Products.findOne(product._id); - expect(product.hashtags).to.not.contain(tag._id); - }); - - it("should add existing tag to product revision when passed existing tag and tag._id by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const tag = Factory.create("tag"); - expect(Tags.find().count()).to.equal(1); - expect(product.hashtags).to.not.contain(tag._id); - Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); - expect(Tags.find().count()).to.equal(1); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.hashtags).to.contain(tag._id); - }); - - it("should publish existing tag for product when passed existing tag and tag._id by admin", function () { + it("should add existing tag when passed existing tag and tag._id by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); const tag = Factory.create("tag"); @@ -611,7 +440,6 @@ describe("core product methods", function () { expect(product.hashtags).to.not.contain(tag._id); Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); expect(Tags.find().count()).to.equal(1); - Meteor.call("revisions/publish", product._id); product = Products.findOne(product._id); expect(product.hashtags).to.contain(tag._id); }); @@ -633,70 +461,21 @@ describe("core product methods", function () { expect(removeTagsSpy).to.not.have.been.called; }); - it("should not remove product tag by admin", function () { + it("should remove product tag by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); const tag = Factory.create("tag"); - // Update product tags and publish so the original prodcut will have the tags + // Update product tags and publish so the original product will have the tags Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); - Meteor.call("revisions/publish", product._id); product = Products.findOne(product._id); expect(product.hashtags).to.contain(tag._id); expect(Tags.find().count()).to.equal(1); - // Remove the tag from the published prouct and ensure it didn't succeed. - // Revision control should stop the published product from being changed. + // Remove the tag from the published product and ensure it succeed. Meteor.call("products/removeProductTag", product._id, tag._id); product = Products.findOne(product._id); - expect(product.hashtags).to.contain(tag._id); - expect(Tags.find().count()).to.equal(1); - }); - - it("should remove tag in product revision by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const tag = Factory.create("tag"); - - // Update product tags and publish so the original prodcut will have the tags - Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.hashtags).to.contain(tag._id); - expect(Tags.find().count()).to.equal(1); - - // Remove the tag from the published prouct and ensure it changed in the revision. - Meteor.call("products/removeProductTag", product._id, tag._id); - const productRevision = Revisions.findOne({ - "documentId": product._id, - "workflow.status": { $nin: ["revision/published"] } - }); - expect(productRevision.documentData.hashtags).to.not.contain(tag._id); - expect(Tags.find().count()).to.equal(1); - }); - - it("should publish remove product tag by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - - // Update product tags and publish so the original prodcut will have the tags - const tag = Factory.create("tag"); - Meteor.call("products/updateProductTags", product._id, tag.name, tag._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.hashtags).to.contain(tag._id); - expect(Tags.find().count()).to.equal(1); - - // Remove the tag from the published prouct which should create a revision. - // Then publish that revision and ensure that it published product changed. - Meteor.call("products/removeProductTag", product._id, tag._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - const tags = Tags.find(); expect(product.hashtags).to.not.contain(tag._id); - expect(tags.count()).to.equal(1); - // Tag should not be deleted, it should just be removed from the product - expect(tags.fetch()[0].isDeleted).to.equal(false); }); }); @@ -711,70 +490,13 @@ describe("core product methods", function () { expect(productUpdateSpy).to.not.have.been.called; }); - it("should not set handle for product by admin", function () { + it("should set handle for product by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); - const productHandle = product.handle; Meteor.call("products/updateProductField", product._id, "title", "new product name"); Meteor.call("products/setHandle", product._id); product = Products.findOne(product._id); - expect(product.handle).to.equal(productHandle); - }); - - it("should set handle correctly on product revision", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - const revision = Revisions.findOne({ documentId: product._id }); - expect(revision.documentData.handle).to.not.equal("new-second-product-name"); - }); - - it("should not set handle on published product", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - product = Products.findOne(product._id); - expect(product.handle).to.not.equal("new-second-product-name"); - }); - - it("should publish handle correctly", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.handle).to.not.equal("new-second-product-name"); - }); - - it("unpublished products with the same title should not receive correct handle", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - product = Products.findOne(product._id); - expect(product.handle).to.not.equal("new-second-product-name-copy"); - }); - - it("products with the same title should receive correct handle on revision", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.handle).to.not.equal("new-second-product-name-copy"); - }); - - it("products with the same title should receive correct handle when published", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateProductField", product._id, "title", "new second product name"); - Meteor.call("products/setHandle", product._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.handle).to.not.equal("new-second-product-name-copy"); + expect(product.handle).to.equal("new-product-name"); }); }); @@ -794,31 +516,12 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not set handle tag for product by admin", function () { + it("should set handle tag for product by admin", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); const tag = Factory.create("tag"); Meteor.call("products/setHandleTag", product._id, tag._id); product = Products.findOne(product._id); - expect(product.handle).to.not.equal(tag.slug); - }); - - it("should set handle tag for product revision by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const tag = Factory.create("tag"); - Meteor.call("products/setHandleTag", product._id, tag._id); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.handle).to.equal(tag.slug); - }); - - it("should publish set handle tag for product by admin", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const tag = Factory.create("tag"); - Meteor.call("products/setHandleTag", product._id, tag._id); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); expect(product.handle).to.equal(tag.slug); }); }); @@ -840,7 +543,7 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not update product position by admin", function (done) { + it("should update product position by admin", function (done) { sandbox.stub(Reaction, "hasPermission", () => true); const product = addProduct(); const tag = Factory.create("tag"); @@ -851,49 +554,10 @@ describe("core product methods", function () { }; expect(() => Meteor.call( "products/updateProductPosition", - product._id, position, tag._id + product._id, position, tag.slug )).to.not.throw(Meteor.Error, /Access Denied/); const updatedProduct = Products.findOne(product._id); - expect(updatedProduct.positions).to.be.undefined; - - return done(); - }); - - it("should update product revision position by admin", function (done) { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const tag = Factory.create("tag"); - const position = { - position: 0, - weight: 0, - updatedAt: new Date() - }; - expect(() => Meteor.call( - "products/updateProductPosition", - product._id, position, tag._id - )).to.not.throw(Meteor.Error, /Access Denied/); - const updatedProductRevision = Revisions.findOne({ documentId: product._id }); - expect(updatedProductRevision.documentData.positions[tag._id].position).to.equal(0); - - return done(); - }); - - it("should publish product position by admin", function (done) { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - const tag = Factory.create("tag"); - const position = { - position: 0, - weight: 0, - updatedAt: new Date() - }; - expect(() => Meteor.call( - "products/updateProductPosition", - product._id, position, tag._id - )).to.not.throw(Meteor.Error, /Access Denied/); - Meteor.call("revisions/publish", product._id); - const updatedProduct = Products.findOne(product._id); - expect(updatedProduct.positions[tag._id].position).to.equal(0); + expect(updatedProduct.positions).to.be.defined; return done(); }); @@ -910,7 +574,7 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not update variants' position", function () { + it("should update variants' position", function () { sandbox.stub(Reaction, "hasPermission", () => true); const { variant: variant1 } = addProductSingleVariant(); const { variant: variant2 } = addProductSingleVariant(); @@ -926,54 +590,9 @@ describe("core product methods", function () { const modifiedVariant1 = Products.findOne(variant1._id); const modifiedVariant2 = Products.findOne(variant2._id); const modifiedVariant3 = Products.findOne(variant3._id); - expect(modifiedVariant1.index).to.be.undefined; - expect(modifiedVariant2.index).to.be.undefined; - expect(modifiedVariant3.index).to.be.undefined; - }); - - it("should update variants' revision position", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const { variant: variant1 } = addProductSingleVariant(); - const { variant: variant2 } = addProductSingleVariant(); - const { variant: variant3 } = addProductSingleVariant(); - - expect(variant1.index).to.be.undefined; - expect(variant2.index).to.be.undefined; - expect(variant3.index).to.be.undefined; - - Meteor.call("products/updateVariantsPosition", [ - variant2._id, variant3._id, variant1._id - ]); - const modifiedVariantRevision1 = Revisions.findOne({ documentId: variant1._id }); - const modifiedVariantRevision2 = Revisions.findOne({ documentId: variant2._id }); - const modifiedVariantRevision3 = Revisions.findOne({ documentId: variant3._id }); - expect(modifiedVariantRevision1.documentData.index).to.equal(2); - expect(modifiedVariantRevision2.documentData.index).to.equal(0); - expect(modifiedVariantRevision3.documentData.index).to.equal(1); - }); - - it("should publish variants' revision position", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const { variant: variant1 } = addProductSingleVariant(); - const { variant: variant2 } = addProductSingleVariant(); - const { variant: variant3 } = addProductSingleVariant(); - - expect(variant1.index).to.be.undefined; - expect(variant2.index).to.be.undefined; - expect(variant3.index).to.be.undefined; - - Meteor.call("products/updateVariantsPosition", [ - variant2._id, variant3._id, variant1._id - ]); - Meteor.call("revisions/publish", [ - variant1._id, variant2._id, variant3._id - ]); - const modifiedVariant1 = Products.findOne(variant1._id); - const modifiedVariant2 = Products.findOne(variant2._id); - const modifiedVariant3 = Products.findOne(variant3._id); - expect(modifiedVariant1.index).to.equal(2); - expect(modifiedVariant2.index).to.equal(0); - expect(modifiedVariant3.index).to.equal(1); + expect(modifiedVariant1.index).to.be.equal(2); + expect(modifiedVariant2.index).to.be.equal(0); + expect(modifiedVariant3.index).to.be.equal(1); }); }); @@ -989,44 +608,15 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should not add meta fields by admin", function (done) { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - Meteor.call("products/updateMetaFields", product._id, { - key: "Material", - value: "Spandex" - }); - product = Products.findOne(product._id); - expect(product.metafields.length).to.be.equal(0); - - return done(); - }); - - it("should add meta fields to product revision by admin", function (done) { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - Meteor.call("products/updateMetaFields", product._id, { - key: "Material", - value: "Spandex" - }); - const productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.metafields[0].key).to.equal("Material"); - expect(productRevision.documentData.metafields[0].value).to.equal("Spandex"); - - return done(); - }); - - it("should publish add meta fields by admin", function (done) { + it("should add meta fields by admin", function (done) { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); Meteor.call("products/updateMetaFields", product._id, { key: "Material", value: "Spandex" }); - Meteor.call("revisions/publish", product._id); product = Products.findOne(product._id); - expect(product.metafields[0].key).to.equal("Material"); - expect(product.metafields[0].value).to.equal("Spandex"); + expect(product.metafields.length).to.be.equal(1); return done(); }); @@ -1041,60 +631,15 @@ describe("core product methods", function () { expect(updateProductSpy).to.not.have.been.called; }); - it("should let admin publish product changes", function () { + it("should let admin toggle product visibility", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); const { isVisible } = product; expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Access Denied/); - Meteor.call("revisions/publish", product._id); product = Products.findOne(product._id); - // We switch the visible state in `products/publishProdct` for revisions expect(product.isVisible).to.equal(!isVisible); }); - it("should not let admin toggle product visibility", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const { isVisible } = product; - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Access Denied/); - product = Products.findOne(product._id); - expect(product.isVisible).to.equal(isVisible); - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Bad Request/); - product = Products.findOne(product._id); - expect(product.isVisible).to.equal(isVisible); - }); - - it("should let admin toggle product revision visibility", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - const product = addProduct(); - let productRevision = Revisions.findOne({ documentId: product._id }); - const { isVisible } = productRevision.documentData; - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Access Denied/); - productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.isVisible).to.equal(!isVisible); - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Bad Request/); - productRevision = Revisions.findOne({ documentId: product._id }); - expect(productRevision.documentData.isVisible).to.equal(!isVisible); - }); - - it("should publish admin toggle product visibility", function () { - sandbox.stub(Reaction, "hasPermission", () => true); - let product = addProduct(); - const { isVisible } = product; // false - - // Toggle visible - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Access Denied/); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.isVisible).to.equal(!isVisible); - - // Toggle not visible - expect(() => Meteor.call("products/publishProduct", product._id)).to.not.throw(Meteor.Error, /Bad Request/); - Meteor.call("revisions/publish", product._id); - product = Products.findOne(product._id); - expect(product.isVisible).to.equal(isVisible); - }); - it("should not publish product when missing title", function () { sandbox.stub(Reaction, "hasPermission", () => true); let product = addProduct(); diff --git a/imports/plugins/core/catalog/server/methods/catalog.js b/imports/plugins/core/catalog/server/methods/catalog.js index 75e9e9fe9dc..e33d33da7db 100644 --- a/imports/plugins/core/catalog/server/methods/catalog.js +++ b/imports/plugins/core/catalog/server/methods/catalog.js @@ -5,11 +5,11 @@ import { EJSON } from "meteor/ejson"; import { Meteor } from "meteor/meteor"; import { ReactionProduct } from "/lib/api"; import { Hooks, Logger, Reaction } from "/server/api"; -import { MediaRecords, Products, Revisions, Tags } from "/lib/collections"; +import { MediaRecords, Products, Tags } from "/lib/collections"; import { Media } from "/imports/plugins/core/files/server"; import rawCollections from "/imports/collections/rawCollections"; -import getProductPriceRange from "/imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange"; -import getVariants from "/imports/plugins/core/revisions/server/no-meteor/utils/getVariants"; +import getProductPriceRange from "../no-meteor/utils/getProductPriceRange"; +import getVariants from "../no-meteor/utils/getVariants"; import isSoldOut from "../no-meteor/utils/isSoldOut"; import isLowQuantity from "../no-meteor/utils/isLowQuantity"; import isBackorder from "../no-meteor/utils/isBackorder"; @@ -189,7 +189,7 @@ function copyMedia(newId, variantOldId, variantNewId) { }) .then((fileRecords) => { fileRecords.forEach((fileRecord) => { - // Copy File and insert directly, bypasing revision control + // Copy File and insert fileRecord .fullClone({ productId: newId, @@ -259,7 +259,6 @@ function denormalize(id, field) { } } - // TODO: Determine if product revision needs to be updated as well. Products.update( id, { @@ -338,9 +337,7 @@ function createProduct(props = null) { /** * @function * @name updateCatalogProduct - * @summary Updates a product's revision and conditionally updates - * the underlying product. - * + * @summary Updates a product document. * @param {String} userId - currently logged in user * @param {Object} selector - selector for product to update * @param {Object} modifier - Object describing what parts of the document to update. @@ -350,23 +347,17 @@ function createProduct(props = null) { function updateCatalogProduct(userId, selector, modifier, validation) { const product = Products.findOne(selector); - const shouldUpdateProduct = Hooks.Events.run("beforeUpdateCatalogProduct", product, { + Hooks.Events.run("beforeUpdateCatalogProduct", product, { userId, modifier, validation }); - if (shouldUpdateProduct) { - const result = Products.update(selector, modifier, validation); - - Hooks.Events.run("afterUpdateCatalogProduct", product, { modifier }); - - return result; - } + const result = Products.update(selector, modifier, validation); - Logger.debug(`beforeUpdateCatalogProduct hook returned falsy, not updating catalog product`); + Hooks.Events.run("afterUpdateCatalogProduct", product, { modifier }); - return false; + return result; } Meteor.methods({ @@ -396,8 +387,7 @@ Meteor.methods({ } // Verify that this variant and any ancestors are not deleted. - // Child variants cannot be added if a parent product or product revision - // is marked as `{ isDeleted: true }` + // Child variants cannot be added if a parent product is marked as `{ isDeleted: true }` if (ReactionProduct.isAncestorDeleted(variant, true)) { throw new Meteor.Error("server-error", "Unable to create product variant"); } @@ -472,7 +462,6 @@ Meteor.methods({ let newId; try { - Hooks.Events.run("beforeInsertCatalogProductInsertRevision", clone); newId = Products.insert(clone, { validate: false }); const newProduct = Products.findOne(newId); Hooks.Events.run("afterInsertCatalogProduct", newProduct); @@ -515,8 +504,7 @@ Meteor.methods({ const { ancestors } = product; // Verify that the parent variant and any ancestors are not deleted. - // Child variants cannot be added if a parent product or product revision - // is marked as `{ isDeleted: true }` + // Child variants cannot be added if a parent product is marked as `{ isDeleted: true }` if (ReactionProduct.isAncestorDeleted(product, true)) { throw new Meteor.Error("server-error", "Unable to create product variant"); } @@ -543,11 +531,9 @@ Meteor.methods({ } Hooks.Events.run("beforeInsertCatalogProduct", assembledVariant); - const _id = Products.insert(assembledVariant); + Products.insert(assembledVariant); Hooks.Events.run("afterInsertCatalogProduct", assembledVariant); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", Products.findOne({ _id })); - Logger.debug(`products/createVariant: created variant: ${newVariantId} for ${parentId}`); return newVariantId; @@ -649,9 +635,17 @@ Meteor.methods({ // out if nothing to delete if (!Array.isArray(toDelete) || toDelete.length === 0) return false; - // Flag the variant and all its children as deleted in Revisions collection. + // Flag the variant and all its children as deleted. toDelete.forEach((product) => { Hooks.Events.run("beforeRemoveCatalogProduct", product, { userId: this.userId }); + Products.update({ + _id: product._id, + type: product.type + }, { + $set: { + isDeleted: true + } + }); Hooks.Events.run("afterRemoveCatalogProduct", this.userId, product); }); @@ -760,7 +754,6 @@ Meteor.methods({ newProduct.title = createTitle(newProduct.title, newProduct._id); newProduct.handle = createHandle(Reaction.getSlug(newProduct.title), newProduct._id); } - Hooks.Events.run("beforeInsertCatalogProductInsertRevision", newProduct); result = Products.insert(newProduct, { validate: false }); Hooks.Events.run("afterInsertCatalogProduct", newProduct); results.push(result); @@ -789,7 +782,6 @@ Meteor.methods({ delete newVariant.createdAt; delete newVariant.publishedAt; // TODO can variant have this param? - Hooks.Events.run("beforeInsertCatalogProductInsertRevision", newVariant); result = Products.insert(newVariant, { validate: false }); Hooks.Events.run("afterInsertCatalogProduct", newVariant); copyMedia(productNewId, variant._id, variantNewId); @@ -822,27 +814,20 @@ Meteor.methods({ throw new Meteor.Error("invalid-parameter", "Product should have a valid shopId"); } - // Create product revision - Hooks.Events.run("beforeInsertCatalogProductInsertRevision", product); - return Products.insert(product); } + // Create a product const newSimpleProduct = createProduct(); - // Create simple product revision - Hooks.Events.run("afterInsertCatalogProductInsertRevision", newSimpleProduct); - - const newVariant = createProduct({ + // Create a product variant + createProduct({ ancestors: [newSimpleProduct._id], price: 0.0, title: "", type: "variant" // needed for multi-schema }); - // Create variant revision - Hooks.Events.run("afterInsertCatalogProductInsertRevision", newVariant); - return newSimpleProduct._id; }, @@ -902,18 +887,25 @@ Meteor.methods({ return ids; }); - // Flag the product and all its variants as deleted in the Revisions collection. + // Flag the product and all of it's variants as deleted. productsWithVariants.forEach((toArchiveProduct) => { Hooks.Events.run("beforeRemoveCatalogProduct", toArchiveProduct, { userId: this.userId }); - + Products.update({ + _id: toArchiveProduct._id, + type: toArchiveProduct.type + }, { + $set: { + isDeleted: true + } + }); Hooks.Events.run("afterRemoveCatalogProduct", this.userId, toArchiveProduct); }); - const numFlaggedAsDeleted = Revisions.find({ - "documentId": { + const numFlaggedAsDeleted = Products.find({ + _id: { $in: ids }, - "documentData.isDeleted": true + isDeleted: true }).count(); if (numFlaggedAsDeleted > 0) { @@ -1017,7 +1009,6 @@ Meteor.methods({ } // If we get a result from the product update, - // meaning the update went past revision control, // denormalize and attach results to top-level product if (result === 1) { if (type === "variant" && toDenormalize.indexOf(field) >= 0) { diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange.js b/imports/plugins/core/catalog/server/no-meteor/utils/getProductPriceRange.js similarity index 91% rename from imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getProductPriceRange.js index 7107ce9a4be..b92229840bf 100644 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange.js +++ b/imports/plugins/core/catalog/server/no-meteor/utils/getProductPriceRange.js @@ -1,4 +1,4 @@ -import getPriceRange from "/imports/plugins/core/catalog/server/no-meteor/utils/getPriceRange"; +import getPriceRange from "./getPriceRange"; import getVariants from "./getVariants"; import getVariantPriceRange from "./getVariantPriceRange"; diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange.test.js b/imports/plugins/core/catalog/server/no-meteor/utils/getProductPriceRange.test.js similarity index 100% rename from imports/plugins/core/revisions/server/no-meteor/utils/getProductPriceRange.test.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getProductPriceRange.test.js diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.js similarity index 83% rename from imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.js index 843ad196f4e..8a1411993e4 100644 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.js +++ b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.js @@ -1,5 +1,4 @@ -import getPriceRange from "/imports/plugins/core/catalog/server/no-meteor/utils/getPriceRange"; -import getProduct from "./getProduct"; +import getPriceRange from "./getPriceRange"; import getVariants from "./getVariants"; /** @@ -12,11 +11,12 @@ import getVariants from "./getVariants"; * @return {Promise} Product PriceRange object */ export default async function getVariantPriceRange(variantId, collections) { + const { Products } = collections; const options = await getVariants(variantId, collections); const visibleOptions = options.filter((option) => option.isVisible && !option.isDeleted); if (visibleOptions.length === 0) { - const topVariant = await getProduct(variantId, collections); + const topVariant = await Products.findOne({ _id: variantId }); // topVariant could be undefined when we removing last top variant return topVariant && getPriceRange([topVariant.price]); } diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.test.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.test.js similarity index 93% rename from imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.test.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.test.js index 10fb69d61f1..7a92df815b6 100644 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantPriceRange.test.js +++ b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantPriceRange.test.js @@ -1,10 +1,8 @@ import mockContext from "/imports/test-utils/helpers/mockContext"; -import { rewire as rewire$getProduct, restore as restore$getProduct } from "./getProduct"; import { rewire as rewire$getVariants, restore as restore$getVariants } from "./getVariants"; import getVariantPriceRange from "./getVariantPriceRange"; const mockCollections = { ...mockContext.collections }; -const mockGetProduct = jest.fn().mockName("getProducts"); const mockGetVariants = jest.fn().mockName("getVariants"); const internalShopId = "123"; @@ -96,19 +94,17 @@ const mockVariants = [ ]; beforeAll(() => { - rewire$getProduct(mockGetProduct); rewire$getVariants(mockGetVariants); }); afterAll(() => { - restore$getProduct(); restore$getVariants(); }); // expect topVariant price if no children test("expect topVariants price string if no child variants", async () => { mockGetVariants.mockReturnValueOnce(Promise.resolve([{ isDeleted: true }])); - mockGetProduct.mockReturnValueOnce(Promise.resolve(mockVariants[0])); + mockCollections.Products.findOne.mockReturnValueOnce(Promise.resolve(mockVariants[0])); const spec = await getVariantPriceRange(internalVariantIds[0], mockCollections); const success = { range: "2.99", diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantQuantity.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantQuantity.js similarity index 100% rename from imports/plugins/core/revisions/server/no-meteor/utils/getVariantQuantity.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getVariantQuantity.js diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariantQuantity.test.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariantQuantity.test.js similarity index 100% rename from imports/plugins/core/revisions/server/no-meteor/utils/getVariantQuantity.test.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getVariantQuantity.test.js diff --git a/imports/plugins/core/catalog/server/no-meteor/utils/getVariants.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariants.js new file mode 100644 index 00000000000..abcca793f6d --- /dev/null +++ b/imports/plugins/core/catalog/server/no-meteor/utils/getVariants.js @@ -0,0 +1,19 @@ +/** + * + * @method getVariants + * @summary Get all of a Product's Variants or only a Product's top level Variants. + * @param {string} productOrVariantId - A Product or top level Product Variant ID. + * @param {Object} collections - Raw mongo collections. + * @param {boolean} topOnly - True to return only a products top level variants. + * @return {Promise} Array of Product Variant objects. + */ +export default async function getVariants(productOrVariantId, collections, topOnly) { + const { Products } = collections; + + return Products.find({ + ancestors: topOnly ? [productOrVariantId] : productOrVariantId, + type: "variant", + isVisible: true, + isDeleted: { $ne: true } + }).toArray(); +} diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariants.test.js b/imports/plugins/core/catalog/server/no-meteor/utils/getVariants.test.js similarity index 70% rename from imports/plugins/core/revisions/server/no-meteor/utils/getVariants.test.js rename to imports/plugins/core/catalog/server/no-meteor/utils/getVariants.test.js index 852638031d2..0eec0acc542 100644 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getVariants.test.js +++ b/imports/plugins/core/catalog/server/no-meteor/utils/getVariants.test.js @@ -92,37 +92,11 @@ const mockVariants = [ } ]; -const mockRevision = { - _id: "333", - documentId: internalVariantIds[0], - documentData: { - _id: internalVariantIds[0], - isRevision: internalVariantIds[0], - isVisible: true - }, - workflow: { - status: "revision/published" - }, - documentType: "product", - createdAt, - updatedAt, - diff: [] -}; - // pass in product id return all variants -test("expect an array of product top level variants from revisions", async () => { +test("expect an array of product top level variants", async () => { mockCollections.Products.toArray.mockReturnValueOnce(Promise.resolve([mockVariants[0]])); - mockCollections.Revisions.findOne.mockReturnValueOnce(Promise.resolve(mockRevision)); const spec = await getVariants(internalProductId, mockCollections, true); - const success = [mockRevision.documentData]; + const success = [mockVariants[0]]; expect(spec).toEqual(success); }); -// pass a variant id return all child options -test("expect an array of product variant options, one from revisions the other from products", async () => { - mockCollections.Products.toArray.mockReturnValueOnce(Promise.resolve(mockVariants)); - mockCollections.Revisions.findOne.mockReturnValueOnce(Promise.resolve(mockRevision)); - const spec = await getVariants(internalProductId, mockCollections); - const success = [mockRevision.documentData, mockVariants[1]]; - expect(spec).toEqual(success); -}); diff --git a/imports/plugins/core/dashboard/client/components/toolbar.js b/imports/plugins/core/dashboard/client/components/toolbar.js index 8983333e70b..2579c046a7f 100644 --- a/imports/plugins/core/dashboard/client/components/toolbar.js +++ b/imports/plugins/core/dashboard/client/components/toolbar.js @@ -35,7 +35,8 @@ class PublishControls extends Component { } static defaultProps = { - showViewAsControls: true + showViewAsControls: true, + isEnabled: true } componentDidMount() { diff --git a/imports/plugins/core/dashboard/client/containers/toolbarContainer.js b/imports/plugins/core/dashboard/client/containers/toolbarContainer.js index 72d5b12b406..4e89ce0e2ae 100644 --- a/imports/plugins/core/dashboard/client/containers/toolbarContainer.js +++ b/imports/plugins/core/dashboard/client/containers/toolbarContainer.js @@ -5,7 +5,6 @@ import { composeWithTracker } from "@reactioncommerce/reaction-components"; import { Reaction, i18next } from "/client/api"; import { Tags, Shops } from "/lib/collections"; import { AdminContextProvider } from "/imports/plugins/core/ui/client/providers"; -import { isRevisionControlEnabled } from "/imports/plugins/core/revisions/lib/api"; const handleAddProduct = () => { Reaction.setUserPreferences("reaction-dashboard", "viewAs", "administrator"); @@ -81,7 +80,6 @@ function composer(props, onData) { packageButtons, dashboardHeaderTemplate: props.data.dashboardHeader, isPreview: Reaction.isPreview(), - isEnabled: isRevisionControlEnabled(), isActionViewAtRootView: Reaction.isActionViewAtRootView(), actionViewIsOpen: Reaction.isActionViewOpen(), hasCreateProductAccess: Reaction.hasPermission("createProduct", Meteor.userId(), Reaction.getShopId()), diff --git a/imports/plugins/core/files/server/methods.js b/imports/plugins/core/files/server/methods.js index 52d54cb37c7..0dbd6b62cb5 100644 --- a/imports/plugins/core/files/server/methods.js +++ b/imports/plugins/core/files/server/methods.js @@ -1,9 +1,7 @@ import { Meteor } from "meteor/meteor"; import { check } from "meteor/check"; -import { Hooks, Reaction } from "/server/api"; -import { MediaRecords, Revisions } from "/lib/collections"; -import { Media } from "/imports/plugins/core/files/server"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api"; +import { Reaction } from "/server/api"; +import { MediaRecords } from "/lib/collections"; /** * Media-related Meteor methods @@ -13,7 +11,7 @@ import { RevisionApi } from "/imports/plugins/core/revisions/lib/api"; /** * @method updateMediaMetadata * @memberof Media/Methods - * @summary updates media record in revision control. + * @summary Updates a media record. * @param {String} fileRecordId - _id of updated file record. * @param {Object} metadata - metadata from updated media file. * @return {Boolean} @@ -22,95 +20,35 @@ import { RevisionApi } from "/imports/plugins/core/revisions/lib/api"; async function updateMediaMetadata(fileRecordId, metadata) { check(fileRecordId, String); check(metadata, Object); - if (RevisionApi.isRevisionControlEnabled()) { - if (metadata.productId) { - const existingRevision = Revisions.findOne({ - "documentId": fileRecordId, - "workflow.status": { - $nin: [ - "revision/published" - ] - } - }); - if (existingRevision) { - const updatedMetadata = Object.assign({}, existingRevision.documentData, metadata); - // Special case where if we have both added and reordered images before publishing we don't want to overwrite - // the workflow status since it would be "unpublished" - if (existingRevision.documentData.workflow === "published" || existingRevision.changeType === "insert") { - updatedMetadata.workflow = "published"; - } - Revisions.update({ _id: existingRevision._id }, { - $set: { - documentData: updatedMetadata - } - }); - Hooks.Events.run("afterRevisionsUpdate", Meteor.userId(), { - ...existingRevision, - documentData: updatedMetadata - }); - } else { - Revisions.insert({ - documentId: fileRecordId, - documentData: metadata, - documentType: "image", - parentDocument: metadata.productId, - changeType: "update", - workflow: { - status: "revision/update" - } - }); - } - return false; + const result = MediaRecords.update({ + _id: fileRecordId + }, { + $set: { + metadata } - } - // for non-product images, just ignore and keep on moving - return true; + }); + + return result === 1; } /** * @name media/insert * @method * @memberof Media/Methods - * @summary insert a new media record and add it to revision control. + * @summary Insert a new media record. * @param {Object} fileRecord - document from file collection upload. * @return {String} - _id of the new inserted media record. */ export async function insertMedia(fileRecord) { check(fileRecord, Object); - const mediaRecordId = await MediaRecords.insert(fileRecord); - - if (RevisionApi.isRevisionControlEnabled() && fileRecord.metadata.workflow !== "published") { - if (fileRecord.metadata.productId) { - const revisionMetadata = Object.assign({}, fileRecord.metadata); - revisionMetadata.workflow = "published"; - Revisions.insert({ - documentId: mediaRecordId, - documentData: revisionMetadata, - documentType: "image", - parentDocument: fileRecord.metadata.productId, - changeType: "insert", - workflow: { - status: "revision/update" - } - }); - MediaRecords.update({ - _id: mediaRecordId - }, { - $set: { - "metadata.workflow": "unpublished" - } - }); - } else { - MediaRecords.update({ - _id: mediaRecordId - }, { - $set: { - "metadata.workflow": "published" - } - }); + const mediaRecordId = await MediaRecords.insert({ + ...fileRecord, + metadata: { + ...fileRecord.metadata, + workflow: "published" } - } + }); return mediaRecordId; } @@ -119,33 +57,22 @@ export async function insertMedia(fileRecord) { * @name media/remove * @method * @memberof Media/Methods - * @summary removes media file and updates record in revision control. + * @summary Unpublish a media record by updating it's workflow * @param {String} fileRecordId - _id of file record to be deleted. * @return {Boolean} */ export async function removeMedia(fileRecordId) { check(fileRecordId, String); - const { metadata } = MediaRecords.findOne({ _id: fileRecordId }); - if (RevisionApi.isRevisionControlEnabled() && metadata.workflow && metadata.workflow === "unpublished") { - Revisions.remove({ - documentId: fileRecordId - }); - Media.remove(fileRecordId); - return true; - } else if (metadata.productId) { - Revisions.insert({ - documentId: fileRecordId, - documentData: metadata, - documentType: "image", - parentDocument: metadata.productId, - changeType: "remove", - workflow: { - status: "revision/update" - } - }); - return true; - } - return false; + + const result = MediaRecords.update({ + _id: fileRecordId + }, { + $set: { + "metadata.workflow": "archived" + } + }); + + return result === 1; } /** diff --git a/imports/plugins/core/revisions/client/components/settings.js b/imports/plugins/core/revisions/client/components/settings.js deleted file mode 100644 index 9b6f7c4a11a..00000000000 --- a/imports/plugins/core/revisions/client/components/settings.js +++ /dev/null @@ -1,45 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { Translation } from "/imports/plugins/core/ui/client/components"; - - -class RevisionControlSettings extends Component { - get settings() { - return this.props.settings; - } - - render() { - let message; - - if (this.settings.general.enabled) { - message = ( - - ); - } else { - message = ( - - ); - } - - return ( -
-

{message}

-
- ); - } -} - -RevisionControlSettings.propTypes = { - checked: PropTypes.bool, - label: PropTypes.string, - onUpdateSettings: PropTypes.func, - settings: PropTypes.object -}; - -export default RevisionControlSettings; diff --git a/imports/plugins/core/revisions/client/components/simpleDiff.js b/imports/plugins/core/revisions/client/components/simpleDiff.js deleted file mode 100644 index d4d2275a534..00000000000 --- a/imports/plugins/core/revisions/client/components/simpleDiff.js +++ /dev/null @@ -1,86 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; - -class SimpleDiff extends Component { - renderDiff() { - const { diff } = this.props; - - return diff.map((change, index) => { - const rightHandSide = change.rhs && change.rhs.toString(); - const leftHandSide = change.lhs && change.lhs.toString(); - - switch (change.kind) { - // Array change - case "A": - return ( - - - {leftHandSide} - {rightHandSide} - - ); - - // Added property / element - case "N": - return ( - - - {leftHandSide} - {rightHandSide} - - ); - - // Edited property or element - case "E": - return ( - - - {leftHandSide} - {rightHandSide} - - ); - - // Removed property / element - case "D": - return ( - - - {leftHandSide} - {rightHandSide} - - ); - default: - return null; - } - }); - } - - render() { - return ( -
- - - - - - - - - {this.renderDiff()} - -
- {"Current"}{"Change"}
-
- ); - } -} - -SimpleDiff.defaultProps = { - diff: [] -}; - -SimpleDiff.propTypes = { - diff: PropTypes.arrayOf(PropTypes.object) -}; - -export default SimpleDiff; diff --git a/imports/plugins/core/revisions/client/containers/publishContainer.js b/imports/plugins/core/revisions/client/containers/publishContainer.js deleted file mode 100644 index e5c456b9fe4..00000000000 --- a/imports/plugins/core/revisions/client/containers/publishContainer.js +++ /dev/null @@ -1,171 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { composeWithTracker } from "@reactioncommerce/reaction-components"; -import PublishControls from "../components/publishControls"; -import { Revisions } from "/lib/collections"; -import { Meteor } from "meteor/meteor"; -import TranslationProvider from "/imports/plugins/core/ui/client/providers/translationProvider"; -import { isRevisionControlEnabled } from "../../lib/api"; -import { Reaction, i18next } from "/client/api"; - -/* - * PublishContainer is a container component connected to Meteor data source. - */ -class PublishContainer extends Component { - publishToCatalog(collection, documentIds) { - Meteor.call(`catalog/publish/${collection}`, documentIds, (error, result) => { - if (result) { - Alerts.toast(i18next.t("admin.catalogProductPublishSuccess", { defaultValue: "Product published to catalog" }), "success"); - } else if (error) { - Alerts.toast(error.message, "error"); - } - }); - } - - handlePublishClick = (revisions) => { - const productIds = this.props.documents - .filter((doc) => doc.type === "simple") - .map((doc) => doc._id); - - if (Array.isArray(revisions) && revisions.length) { - let documentIds = revisions.map((revision) => { - if (revision.parentDocument && revision.documentType !== "product") { - return revision.parentDocument; - } - return revision.documentId; - }); - - const documentIdsSet = new Set(documentIds); // ensures they are unique - documentIds = Array.from(documentIdsSet); - Meteor.call("revisions/publish", documentIds, (error, result) => { - 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); - } - - // Publish to catalog after revisions have been published - this.publishToCatalog("products", productIds); - } else { - Alerts.toast((error && error.message) || (result && result.status) || i18next.t("app.error"), "error"); - } - }); - } else { - // Publish to catalog immediately if there are no revisions to publish beforehand - this.publishToCatalog("products", productIds); - } - } - - handlePublishActions = (event, action, documentIds) => { - switch (action) { - case "archive": - if (this.props.onAction) { - this.props.onAction(event, action, this.props.documentIds); - } - break; - case "discard": - Meteor.call("revisions/discard", documentIds, (error, result) => { - if (result === true) { - const message = i18next.t("revisions.changesDiscarded", { - defaultValue: "Changes discarded successfully" - }); - - Alerts.toast(message, "success"); - } else { - const message = i18next.t("revisions.noChangesDiscarded", { - defaultValue: "There are no changes to discard" - }); - - Alerts.toast(message, "warning"); - } - }); - break; - default: - } - } - - render() { - return ( - - - - ); - } -} - -PublishContainer.propTypes = { - documentIds: PropTypes.arrayOf(PropTypes.string), - documents: PropTypes.arrayOf(PropTypes.object), - isEnabled: PropTypes.bool, - isPreview: PropTypes.bool, - onAction: PropTypes.func, - onPublishSuccess: PropTypes.func, - onVisibilityChange: PropTypes.func, - product: PropTypes.object, - revisions: PropTypes.arrayOf(PropTypes.object) -}; - -function composer(props, onData) { - const viewAs = Reaction.getUserPreferences("reaction-dashboard", "viewAs", "administrator"); - - if (Array.isArray(props.documentIds) && props.documentIds.length) { - const subscription = Meteor.subscribe("ProductRevisions", props.documentIds); - - if (subscription.ready()) { - const revisions = Revisions.find({ - "$or": [ - { - documentId: { - $in: props.documentIds - } - }, - { - "documentData.ancestors": { - $in: props.documentIds - } - }, - { - parentDocument: { - $in: props.documentIds - } - } - ], - "workflow.status": { - $nin: [ - "revision/published" - ] - } - }).fetch(); - - onData(null, { - isEnabled: isRevisionControlEnabled(), - documentIds: props.documentIds, - documents: props.documents, - revisions, - isPreview: viewAs === "customer" - }); - - return; - } - } - - onData(null, { - isEnabled: isRevisionControlEnabled(), - isPreview: viewAs === "customer" - }); -} - -export default composeWithTracker(composer)(PublishContainer); diff --git a/imports/plugins/core/revisions/client/containers/settingsContainer.js b/imports/plugins/core/revisions/client/containers/settingsContainer.js deleted file mode 100644 index 01a39387281..00000000000 --- a/imports/plugins/core/revisions/client/containers/settingsContainer.js +++ /dev/null @@ -1,70 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { composeWithTracker } from "@reactioncommerce/reaction-components"; -import { Meteor } from "meteor/meteor"; -import SettingsComponent from "../components/settings"; -import { Packages } from "/lib/collections"; - -class RevisionSettingsContainer extends Component { - constructor(props) { - super(props); - - this.state = { - settings: this.props.packageInfo.settings - }; - } - - componentWillReceiveProps(nextProps) { - this.setState({ - settings: nextProps.packageInfo.settings - }); - } - - get settings() { - return this.state.settings; - } - - handleUpdateSettings = (settings) => { - this.setState({ settings }, () => { - Meteor.call("revisions/settings/update", settings); - }); - } - - /** - * Publish container is a stateless container component connected to Meteor data source. - * @return {PropTypes.node} react node - */ - render() { - return ( -
- -
- ); - } -} - -RevisionSettingsContainer.propTypes = { - packageInfo: PropTypes.object -}; - -export function handlePublishClick(revisions) { - if (Array.isArray(revisions)) { - const documentIds = revisions.map((revision) => revision.documentId); - Meteor.call("revisions/publish", documentIds); - } -} - -function composer(props, onData) { - const packageInfo = Packages.findOne({ - name: "reaction-revisions" - }); - - onData(null, { - packageInfo - }); -} - -export default composeWithTracker(composer)(RevisionSettingsContainer); diff --git a/imports/plugins/core/revisions/client/index.js b/imports/plugins/core/revisions/client/index.js deleted file mode 100644 index de2ea13734f..00000000000 --- a/imports/plugins/core/revisions/client/index.js +++ /dev/null @@ -1,2 +0,0 @@ -import "./templates/settings.html"; -import "./templates/settings.js"; diff --git a/imports/plugins/core/revisions/client/templates/settings.html b/imports/plugins/core/revisions/client/templates/settings.html deleted file mode 100644 index 03253ad97cd..00000000000 --- a/imports/plugins/core/revisions/client/templates/settings.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/imports/plugins/core/revisions/client/templates/settings.js b/imports/plugins/core/revisions/client/templates/settings.js deleted file mode 100644 index b12d8df528c..00000000000 --- a/imports/plugins/core/revisions/client/templates/settings.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Template } from "meteor/templating"; -import SearchContainer from "../containers/settingsContainer.js"; - -Template.revisionControlSettings.helpers({ - SearchContainerComponent() { - return { - component: SearchContainer - }; - } -}); diff --git a/imports/plugins/core/revisions/index.js b/imports/plugins/core/revisions/index.js deleted file mode 100644 index d0eb62211f5..00000000000 --- a/imports/plugins/core/revisions/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as PublishContainer } from "./client/containers/publishContainer"; diff --git a/imports/plugins/core/revisions/lib/api/index.js b/imports/plugins/core/revisions/lib/api/index.js deleted file mode 100644 index 4442f1f0bed..00000000000 --- a/imports/plugins/core/revisions/lib/api/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./revisions"; diff --git a/imports/plugins/core/revisions/lib/api/revisions.js b/imports/plugins/core/revisions/lib/api/revisions.js deleted file mode 100644 index b72ddb46af1..00000000000 --- a/imports/plugins/core/revisions/lib/api/revisions.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Packages } from "/lib/collections"; -import { Reaction } from "/lib/api"; - -export function getPackageSettings() { - const shopId = Reaction.getPrimaryShopId(); - - const packageInfo = Packages.findOne({ - name: "reaction-revisions", - shopId - }); - - if (packageInfo && packageInfo.enabled && packageInfo.settings) { - return packageInfo.settings; - } - - return null; -} - -export function isRevisionControlEnabled() { - const settings = getPackageSettings(); - - if (settings && settings.general && typeof settings.general.enabled === "boolean") { - return settings.general.enabled; - } - - return false; -} - -export const RevisionApi = { - isRevisionControlEnabled, - getPackageSettings -}; diff --git a/imports/plugins/core/revisions/register.js b/imports/plugins/core/revisions/register.js deleted file mode 100644 index fa545f380bd..00000000000 --- a/imports/plugins/core/revisions/register.js +++ /dev/null @@ -1,21 +0,0 @@ -import { Reaction } from "/server/api"; - -Reaction.registerPackage({ - label: "Revisions", - name: "reaction-revisions", - autoEnable: true, - settings: { - general: { - enabled: true - } - }, - registry: [ - // Settings Panel in Catalog - { - label: "Product Revisions", - name: "catalog/settings/revisions/general", - provides: ["catalogSettings"], - template: "revisionControlSettings" - } - ] -}); diff --git a/imports/plugins/core/revisions/server/functions.js b/imports/plugins/core/revisions/server/functions.js deleted file mode 100644 index 693c481d597..00000000000 --- a/imports/plugins/core/revisions/server/functions.js +++ /dev/null @@ -1,473 +0,0 @@ -import _ from "lodash"; -import { Meteor } from "meteor/meteor"; -import { Products, Revisions, Tags } from "/lib/collections"; -import { Hooks, Logger } from "/server/api"; -import { RevisionApi } from "../lib/api"; -import { getSlug } from "/lib/api"; -import rawCollections from "/imports/collections/rawCollections"; - -import getProductPriceRange from "./no-meteor/utils/getProductPriceRange"; - -/** - * @name insertRevision - * @method - * @summary Inserts a new revision for a given product - * @param {Object} product - * @returns {undefined} - * @private - */ -export function insertRevision(product) { - if (RevisionApi.isRevisionControlEnabled() === false) { - return true; - } - - if ( - product.workflow && - Array.isArray(product.workflow.workflow) && - product.workflow.workflow.indexOf("imported") !== -1 - ) { - // Mark imported products as published by default. - return true; - } - - const productRevision = Revisions.findOne({ - "documentId": product._id, - "workflow.status": { - $nin: ["revision/published"] - } - }); - - // Prevent this product from being created if a parent product / variant ancestor is deleted. - // - // This will prevent cases where a parent variant has been deleted and a user tries to create a - // child variant. You cannot create the child variant because the parent will no longer exist when - // changes have been published; resulting in a broken inheritance and UI - const productHasAncestors = Array.isArray(product.ancestors); - - if (productHasAncestors) { - // Verify there are no deleted ancestors, - // Variants cannot be restored if their parent product / variant is deleted - const archivedCount = Revisions.find({ - "documentId": { $in: product.ancestors }, - "documentData.isDeleted": true, - "workflow.status": { - $nin: ["revision/published"] - } - }).count(); - - if (archivedCount > 0) { - Logger.debug(`Cannot create product ${ - product._id - } as a product/variant higher in it's ancestors tree is marked as 'isDeleted'.`); - throw new Meteor.Error("unable-to-create-variant", "Unable to create product variant"); - } - } - - if (!productRevision) { - Logger.debug(`No revision found for product ${product._id}. Creating new revision`); - - Revisions.insert({ - documentId: product._id, - documentData: product - }); - } -} - -/** - * @name updateRevision - * @method - * @summary Update a product's revision - * @param {String} userId - * @param {Object} product - Product to update - * @param {Object} options - Options include userId, modifier and validation properties - * @returns {Boolean} true if underlying product should be updated, otherwise false. - * @private - */ -export function updateRevision(product, options = {}) { - if (RevisionApi.isRevisionControlEnabled() === false) { - return true; - } - - const { userId, modifier } = options; - - let productRevision = Revisions.findOne({ - "documentId": product._id, - "workflow.status": { - $nin: ["revision/published"] - } - }); - - // Prevent this product revision from being restored from isDeleted state if a product / variant - // ancestor is also deleted. - // - // This will prevent cases where a parent variant has been deleted and a user tries to restore a - // child variant. You cannot restore the child variant, because the parent will no longer exist when - // changes have been published; resulting in a broken inheritance and UI - const revisionHasAncestors = - productRevision && productRevision.documentData && Array.isArray(productRevision.documentData.ancestors); - const modifierContainsIsDeleted = modifier.$set && modifier.$set.isDeleted === false; - - if (revisionHasAncestors && modifierContainsIsDeleted) { - // Verify there are no deleted ancestors, - // Variants cannot be restored if their parent product / variant is deleted - const archivedCount = Revisions.find({ - "documentId": { $in: productRevision.documentData.ancestors }, - "documentData.isDeleted": true, - "workflow.status": { - $nin: ["revision/published"] - } - }).count(); - - if (archivedCount > 0) { - Logger.debug(`Cannot restore product ${ - product._id - } as a product/variant higher in it's ancestors tree is marked as 'isDeleted'.`); - throw new Meteor.Error("unable-to-delete-variant", "Unable to delete product variant"); - } - } - - if (!productRevision) { - Logger.debug(`No revision found for product ${product._id}. Creating new revision`); - - // Create a new revision - Revisions.insert({ - documentId: product._id, - documentData: product - }); - - // Fetch newly created revision - productRevision = Revisions.findOne({ - documentId: product._id - }); - } - - // Create a new selector for the revision - // - // This is especially important since we may need to update some fields - // like metadata, and the selector is very important to that. - const revisionSelector = { - "documentId": product._id, - "workflow.status": { - $nin: ["revision/published"] - } - }; - - // Create a new modifier for the revision - const revisionModifier = { - $set: { - "workflow.status": "revision/update" - } - }; - - let publish = false; - if (Object.prototype.hasOwnProperty.call(options, "publish")) { - ({ publish } = options); - } - - if (publish === true || (product.workflow && product.workflow.status === "product/publish")) { - // Maybe mark the revision as published - - Logger.debug(`Publishing revision for product ${product._id}.`); - Revisions.update(revisionSelector, { - $set: { - "workflow.status": "revision/published" - } - }); - Hooks.Events.run("afterRevisionsUpdate", userId, { - ...productRevision, - workflow: { ...productRevision.workflow, status: "revisions/published" } - }); - return true; - } - - const hasAncestors = Array.isArray(product.ancestors) && product.ancestors.length > 0; - - for (const operation in modifier) { - if (Object.hasOwnProperty.call(modifier, operation)) { - if (!revisionModifier[operation]) { - revisionModifier[operation] = {}; - } - - for (const property in modifier[operation]) { - if ({}.hasOwnProperty.call(modifier[operation], property)) { - if (operation === "$set" && property === "metafields.$") { - // Special handling for meta fields with $ operator - // We need to update the selector otherwise the operation would completely fail. - // - // This does NOT apply to metafield.0, metafield.1, metafield.n operations - // where 0, 1, n represent an array index. - - // const originalSelector = options.selector; - revisionSelector["documentData.metafields"] = options.metafields; - revisionModifier.$set[`documentData.${property}`] = modifier.$set[property]; - } else if (operation === "$push" && property === "hashtags") { - if (!revisionModifier.$addToSet) { - revisionModifier.$addToSet = {}; - } - revisionModifier.$addToSet[`documentData.${property}`] = modifier.$push[property]; - } else if (operation === "$set" && property === "price" && hasAncestors) { - Revisions.update(revisionSelector, { - $set: { - "documentData.price": modifier.$set.price - } - }); - Hooks.Events.run("afterRevisionsUpdate", userId, { - ...productRevision, - documentData: { ...productRevision.documentData, price: modifier.$set.price } - }); - - const updateId = product.ancestors[0] || product._id; - const priceRange = Promise.await(getProductPriceRange(updateId, rawCollections)); - - Meteor.call("products/updateProductField", updateId, "price", priceRange); - } else if (operation === "$set" && property === "isVisible" && hasAncestors) { - Revisions.update(revisionSelector, { - $set: { - "documentData.isVisible": modifier.$set.isVisible - } - }); - Hooks.Events.run("afterRevisionsUpdate", userId, { - ...productRevision, - documentData: { ...productRevision.documentData, isVisible: modifier.$set.isVisible } - }); - - const updateId = product.ancestors[0] || product._id; - const priceRange = Promise.await(getProductPriceRange(updateId, rawCollections)); - - 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 slugified 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; - 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 originally 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) && - hasNewHandle === false - ) { - // Set the handle to be the slug of the product.title - // when documentId (product._id) matches the handle, then handle is empty, and a title exists - revisionModifier.$set["documentData.handle"] = getSlug(newTitle || revisionTitle); - } else if (hasHandle === false && hasExistingTitle === false && hasNewHandle === 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 slugified 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. deletes 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 slugified product title, or document id if title does not exist. - if (_.isEmpty(newValue)) { - revisionModifier.$set["documentData.handle"] = hasExistingTitle - ? getSlug(revisionTitle) - : productRevision.documentId; - } - } else { - // Let everything else through - revisionModifier[operation][`documentData.${property}`] = modifier[operation][property]; - } - } - } - } - } - - Revisions.update(revisionSelector, revisionModifier); - const updatedRevision = Revisions.findOne({ documentId: product._id }); - Hooks.Events.run("afterRevisionsUpdate", userId, updatedRevision); - - Logger.debug(`Revision updated for product ${product._id}.`); - - if (modifier.$pull && modifier.$pull.hashtags) { - const tagId = modifier.$pull.hashtags; - - const productCount = Products.find({ - hashtags: tagId - }).count(); - - const relatedTagsCount = Tags.find({ - relatedTagIds: tagId - }).count(); - - if (productCount === 0 && relatedTagsCount === 0) { - // Mark tag as deleted - Tags.update( - { - _id: tagId - }, - { - $set: { - isDeleted: true - } - } - ); - } else { - Tags.update( - { - _id: tagId - }, - { - $set: { - isDeleted: false - } - } - ); - } - } - - // If we are using $set or $inc, and the fields are one of the ignoredFields, - // allow product to be updated without going through revision control - if ((modifier.$set || modifier.$inc) && !modifier.$pull && !modifier.$push) { - const newSet = {}; - const newInc = {}; - let hasIgnoredFields = false; - const ignoredFields = ["isLowQuantity", "isSoldOut", "inventoryQuantity"]; - - for (const field of ignoredFields) { - if ( - modifier.$set && - (typeof modifier.$set[field] === "number" || - typeof modifier.$set[field] === "boolean" || - typeof modifier.$set[field] === "string") - ) { - newSet[field] = modifier.$set[field]; - hasIgnoredFields = true; - } - - if ( - modifier.$inc && - (typeof modifier.$inc[field] === "number" || - typeof modifier.$inc[field] === "boolean" || - typeof modifier.$set[field] === "string") - ) { - newInc[field] = modifier.$inc[field]; - hasIgnoredFields = true; - } - } - if (_.isEmpty(newSet) === false) { - modifier.$set = newSet; - } - - if (_.isEmpty(newInc) === false) { - modifier.$inc = newInc; - } - - return hasIgnoredFields === true; - } - - // prevent the underlying document from being modified as it is in draft mode - return false; -} -/** - * @name markRevisionAsDeleted - * @method - * @summary Flag a product's revision as deleted - * @param {Object} product - The product whose revision will be flagged as deleted. - * @param {Object} options - Contains userId - * @returns {undefined} - * @private - */ -export function markRevisionAsDeleted(product, options) { - if (RevisionApi.isRevisionControlEnabled() === false) { - return true; - } - - const { userId } = options; - - let productRevision = Revisions.findOne({ - documentId: product._id - }); - - if (!productRevision) { - Logger.debug(`No revision found for product ${product._id}. Creating new revision`); - - Revisions.insert({ - documentId: product._id, - documentData: product - }); - productRevision = Revisions.findOne({ - documentId: product._id - }); - } - - // Set the revision as deleted "isDeleted: true" - Revisions.update( - { - documentId: product._id - }, - { - $set: { - "documentData.isDeleted": true, - "workflow.status": "revision/remove" - } - } - ); - Hooks.Events.run("afterRevisionsUpdate", userId, { - ...productRevision, - documentData: { ...productRevision.documentData, isDeleted: true }, - workflow: { ...productRevision.workflow, workflow: "revision/remove" } - }); - - Logger.debug(`Revision updated for product ${product._id}.`); - Logger.debug(`Product ${product._id} is now marked as deleted.`); - - // If the original product is deleted, and the user is trying to delete it again, - // then actually remove it completely. - // - // This acts like a trash. Where the product is sent to trash before it can actually - // be deleted permanently. - if (product.isDeleted === true) { - Logger.debug(`Allowing write to product ${product._id} for Collection.remove().`); - - return true; - } - - Logger.debug(`Preventing write to product ${product._id} for Collection.remove().`); - return false; -} diff --git a/imports/plugins/core/revisions/server/hooks.js b/imports/plugins/core/revisions/server/hooks.js deleted file mode 100644 index ad77ef877a9..00000000000 --- a/imports/plugins/core/revisions/server/hooks.js +++ /dev/null @@ -1,92 +0,0 @@ -// import _ from "lodash"; -import { diff } from "deep-diff"; -import { RevisionApi } from "../lib/api"; -import { insertRevision, updateRevision, markRevisionAsDeleted } from "./functions"; -import { Products, Revisions } from "/lib/collections"; -import { Hooks } from "/server/api"; -import { Media } from "/imports/plugins/core/files/server"; - -/** - * @summary Executes the provided function when beforeInsertCatalogProductInsertRevision - * hook is ran. The hook is ran before a product is inserted, and it will insert a - * corresponding revision for the provided product. - * @param {Function} Callback to execute - * @return {Object} product - the product in which the callback was called on. - * @private - */ -Hooks.Events.add("beforeInsertCatalogProductInsertRevision", (product) => { - insertRevision(product); - - return product; -}); - -/** - * @summary Executes the provided function when beforeInsertCatalogProductInsertRevision - * hook is ran. The hook is ran after a product is inserted, and it will insert a - * corresponding revision for the provided product. - * @param {Function} Callback to execute - * @return {Object} product - the product in which the callback was called on. - * @private - */ -Hooks.Events.add("afterInsertCatalogProductInsertRevision", (product) => { - insertRevision(product); - - return product; -}); - -/** - * @summary Executes the provided function when beforeUpdateCatalogProduct - * hook is ran. The hook is ran before a product is updated, and it will updated the - * corresponding revisions for the provided product. - * @param {Function} Callback to execute - * @return {Boolean} true|false - Used to determine whether the underlying product should be updated. - * @private - */ -Hooks.Events.add("beforeUpdateCatalogProduct", (product, options) => updateRevision(product, options)); - -/** - * @summary Executes the provided function when beforeRemoveCatalogProduct - * hook is ran. The hook is ran before a product or variant is archived, and it will updated the - * corresponding revisions for the provided product or variant. - * @param {Function} Callback to execute - * @return {Boolean} true|false - Used to determine whether the underlying product should be updated. - * @private - */ -Hooks.Events.add("beforeRemoveCatalogProduct", (product, options) => markRevisionAsDeleted(product, options)); - -Hooks.Events.add( - "afterRevisionsUpdate", - (userId, revision) => { - if (RevisionApi.isRevisionControlEnabled() === false) { - return true; - } - let differences; - - if (!revision.documentType || revision.documentType === "product") { - // Make diff - const product = Products.findOne({ - _id: revision.documentId - }); - differences = diff(product, revision.documentData); - } - - if (revision.documentType && revision.documentType === "image") { - const image = Promise.await(Media.findOne(revision.documentId, { raw: true })); - differences = image && diff(image.metadata, revision.documentData); - } - - Revisions.update( - { - _id: revision._id - }, - { - $set: { - diff: differences && differences.map((d) => Object.assign({}, d)) - } - } - ); - }, - { - fetchPrevious: false - } -); diff --git a/imports/plugins/core/revisions/server/i18n/ar.json b/imports/plugins/core/revisions/server/i18n/ar.json deleted file mode 100644 index fa66d8c3a7a..00000000000 --- a/imports/plugins/core/revisions/server/i18n/ar.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "ar", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "معاينة", - "revisionsLabel": "التنقيحات", - "revisionsTitle": "التنقيحات", - "revisionsDescription": "مراجعة مراقبة" - }, - "catalogSettings": { - "productRevisionsLabel": "مراجعات المنتج" - }, - "toolbar": { - "publishAll": "نشر جميع" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/bg.json b/imports/plugins/core/revisions/server/i18n/bg.json deleted file mode 100644 index 9e0fbcf8cdc..00000000000 --- a/imports/plugins/core/revisions/server/i18n/bg.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "bg", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "предварителен преглед", - "revisionsLabel": "ревизии", - "revisionsTitle": "ревизии", - "revisionsDescription": "контрол на контрол" - }, - "catalogSettings": { - "productRevisionsLabel": "ревизии на продукта" - }, - "toolbar": { - "publishAll": "публикува всички" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/cs.json b/imports/plugins/core/revisions/server/i18n/cs.json deleted file mode 100644 index 72230d99c70..00000000000 --- a/imports/plugins/core/revisions/server/i18n/cs.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "cs", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Náhled", - "revisionsLabel": "revize", - "revisionsTitle": "revize", - "revisionsDescription": "ovládání revize" - }, - "catalogSettings": { - "productRevisionsLabel": "Revize produktu" - }, - "toolbar": { - "publishAll": "publikovat vše" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/de.json b/imports/plugins/core/revisions/server/i18n/de.json deleted file mode 100644 index 777ea5115e5..00000000000 --- a/imports/plugins/core/revisions/server/i18n/de.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "de", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Vorschau", - "revisionsLabel": "Revisionen", - "revisionsTitle": "Revisionen", - "revisionsDescription": "Revisionskontrolle" - }, - "catalogSettings": { - "productRevisionsLabel": "Produkt Revisions" - }, - "toolbar": { - "publishAll": "veröffentlichen Alle" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/el.json b/imports/plugins/core/revisions/server/i18n/el.json deleted file mode 100644 index 016cffe5f80..00000000000 --- a/imports/plugins/core/revisions/server/i18n/el.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "el", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Πρεμιέρα", - "revisionsLabel": "αναθεωρήσεις", - "revisionsTitle": "αναθεωρήσεις", - "revisionsDescription": "έλεγχο αναθεώρηση" - }, - "catalogSettings": { - "productRevisionsLabel": "αναθεωρήσεις του προϊόντος" - }, - "toolbar": { - "publishAll": "Δημοσιεύστε Όλα" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/en.json b/imports/plugins/core/revisions/server/i18n/en.json deleted file mode 100644 index 4d97abd4707..00000000000 --- a/imports/plugins/core/revisions/server/i18n/en.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "en", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Preview", - "revisionsLabel": "Revisions", - "revisionsTitle": "Revisions", - "revisionsDescription": "Revision control" - }, - "catalogSettings": { - "productRevisionsLabel": "Product Revisions" - }, - "toolbar": { - "publishAll": "Publish All" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/es.json b/imports/plugins/core/revisions/server/i18n/es.json deleted file mode 100644 index 87b73d9a09e..00000000000 --- a/imports/plugins/core/revisions/server/i18n/es.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "es", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Preestreno", - "revisionsLabel": "Las revisiones", - "revisionsTitle": "Las revisiones", - "revisionsDescription": "Control de revisión" - }, - "catalogSettings": { - "productRevisionsLabel": "Las revisiones del producto" - }, - "toolbar": { - "publishAll": "publicar todo" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/fr.json b/imports/plugins/core/revisions/server/i18n/fr.json deleted file mode 100644 index 61f4afef428..00000000000 --- a/imports/plugins/core/revisions/server/i18n/fr.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "fr", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Aperçu", - "revisionsLabel": "Versions", - "revisionsTitle": "Versions", - "revisionsDescription": "Gestion de versions" - }, - "catalogSettings": { - "productRevisionsLabel": "Gestion de versions des produits" - }, - "toolbar": { - "publishAll": "Tout publier" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/he.json b/imports/plugins/core/revisions/server/i18n/he.json deleted file mode 100644 index 7a2cc9d0e73..00000000000 --- a/imports/plugins/core/revisions/server/i18n/he.json +++ /dev/null @@ -1,5 +0,0 @@ -[{ - "i18n": "he", - "ns": "reaction-revisions", - "translation": { } -}] diff --git a/imports/plugins/core/revisions/server/i18n/hr.json b/imports/plugins/core/revisions/server/i18n/hr.json deleted file mode 100644 index ce55ba26011..00000000000 --- a/imports/plugins/core/revisions/server/i18n/hr.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "hr", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "pregled", - "revisionsLabel": "Izmjene", - "revisionsTitle": "Izmjene", - "revisionsDescription": "kontrola Revizija" - }, - "catalogSettings": { - "productRevisionsLabel": "Izmjene proizvoda" - }, - "toolbar": { - "publishAll": "objaviti sve" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/hu.json b/imports/plugins/core/revisions/server/i18n/hu.json deleted file mode 100644 index 1357f81f0e3..00000000000 --- a/imports/plugins/core/revisions/server/i18n/hu.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "hu", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Előnézet", - "revisionsLabel": "Változatok", - "revisionsTitle": "Változatok", - "revisionsDescription": "Revision szabályozás" - }, - "catalogSettings": { - "productRevisionsLabel": "termék Változatok" - }, - "toolbar": { - "publishAll": "közzétenni minden" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/index.js b/imports/plugins/core/revisions/server/i18n/index.js deleted file mode 100644 index 3f6a8d87fb9..00000000000 --- a/imports/plugins/core/revisions/server/i18n/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import { loadTranslations } from "/server/startup/i18n"; - -// import ar from "./ar.json"; -// import bg from "./bg.json"; -// import de from "./de.json"; -// import el from "./el.json"; -import en from "./en.json"; -// import es from "./es.json"; -// import fr from "./fr.json"; -// import he from "./he.json"; -// import hr from "./hr.json"; -// import it from "./it.json"; -// import my from "./my.json"; -// import nb from "./nb.json"; -// import nl from "./nl.json"; -// import pl from "./pl.json"; -// import pt from "./pt.json"; -// import ro from "./ro.json"; -// import ru from "./ru.json"; -// import sl from "./sl.json"; -// import sv from "./sv.json"; -// import tr from "./tr.json"; -// import vi from "./vi.json"; -// import zh from "./zh.json"; - -// -// we want all the files in individual -// imports for easier handling by -// automated translation software -// -loadTranslations([en]); -// loadTranslations([ar, bg, de, el, en, es, fr, he, hr, it, my, nb, nl, pl, pt, ro, ru, sl, sv, tr, vi, zh]); diff --git a/imports/plugins/core/revisions/server/i18n/it.json b/imports/plugins/core/revisions/server/i18n/it.json deleted file mode 100644 index f3e540553ba..00000000000 --- a/imports/plugins/core/revisions/server/i18n/it.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "it", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Anteprima", - "revisionsLabel": "revisioni", - "revisionsTitle": "revisioni", - "revisionsDescription": "controllo di revisione" - }, - "catalogSettings": { - "productRevisionsLabel": "Le revisioni del prodotto" - }, - "toolbar": { - "publishAll": "pubblicare Tutto" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/my.json b/imports/plugins/core/revisions/server/i18n/my.json deleted file mode 100644 index b67ca31ef7c..00000000000 --- a/imports/plugins/core/revisions/server/i18n/my.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "my", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "ကို preview", - "revisionsLabel": "တည်းဖြတ်မူများကို", - "revisionsTitle": "တည်းဖြတ်မူများကို", - "revisionsDescription": "တည်းဖြတ်မူထိန်းချုပ်မှု" - }, - "catalogSettings": { - "productRevisionsLabel": "ကုန်ပစ္စည်းပြင်ဆင်ချက်များကို" - }, - "toolbar": { - "publishAll": "အားလုံး Publish" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/nb.json b/imports/plugins/core/revisions/server/i18n/nb.json deleted file mode 100644 index 80dd19ca6f7..00000000000 --- a/imports/plugins/core/revisions/server/i18n/nb.json +++ /dev/null @@ -1,5 +0,0 @@ -[{ - "i18n": "nb", - "ns": "reaction-revisions", - "translation": { } -}] diff --git a/imports/plugins/core/revisions/server/i18n/nl.json b/imports/plugins/core/revisions/server/i18n/nl.json deleted file mode 100644 index be3191b745f..00000000000 --- a/imports/plugins/core/revisions/server/i18n/nl.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "nl", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Voorbeeld", - "revisionsLabel": "herzieningen", - "revisionsTitle": "herzieningen", - "revisionsDescription": "controle Revision" - }, - "catalogSettings": { - "productRevisionsLabel": "product revisies" - }, - "toolbar": { - "publishAll": "Alle publiceren" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/pl.json b/imports/plugins/core/revisions/server/i18n/pl.json deleted file mode 100644 index b0fadff8835..00000000000 --- a/imports/plugins/core/revisions/server/i18n/pl.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "pl", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Zapowiedź", - "revisionsLabel": "rewizje", - "revisionsTitle": "rewizje", - "revisionsDescription": "kontroli wersji" - }, - "catalogSettings": { - "productRevisionsLabel": "Wersje produktu" - }, - "toolbar": { - "publishAll": "publikuje wszystkie" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/pt.json b/imports/plugins/core/revisions/server/i18n/pt.json deleted file mode 100644 index 50d7723561b..00000000000 --- a/imports/plugins/core/revisions/server/i18n/pt.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "pt", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "visualização", - "revisionsLabel": "revisões", - "revisionsTitle": "revisões", - "revisionsDescription": "controle de revisão" - }, - "catalogSettings": { - "productRevisionsLabel": "As revisões de produtos" - }, - "toolbar": { - "publishAll": "publicar todos" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/ro.json b/imports/plugins/core/revisions/server/i18n/ro.json deleted file mode 100644 index 3f3be9ba368..00000000000 --- a/imports/plugins/core/revisions/server/i18n/ro.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "ro", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "previzualizare", - "revisionsLabel": "reviziile", - "revisionsTitle": "reviziile", - "revisionsDescription": "controlul revizuirii" - }, - "catalogSettings": { - "productRevisionsLabel": "Revizuiri produsului" - }, - "toolbar": { - "publishAll": "publicaţi toate" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/ru.json b/imports/plugins/core/revisions/server/i18n/ru.json deleted file mode 100644 index 6242c1d6dd0..00000000000 --- a/imports/plugins/core/revisions/server/i18n/ru.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "ru", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Предварительный просмотр", - "revisionsLabel": "Ревизии", - "revisionsTitle": "Ревизии", - "revisionsDescription": "контроль версий" - }, - "catalogSettings": { - "productRevisionsLabel": "Ревизии продукта" - }, - "toolbar": { - "publishAll": "Опубликовать все" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/sl.json b/imports/plugins/core/revisions/server/i18n/sl.json deleted file mode 100644 index c2ec0e0e7f2..00000000000 --- a/imports/plugins/core/revisions/server/i18n/sl.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "sl", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "predogled", - "revisionsLabel": "Revizije", - "revisionsTitle": "Revizije", - "revisionsDescription": "nadzor revizija" - }, - "catalogSettings": { - "productRevisionsLabel": "Revizije izdelka" - }, - "toolbar": { - "publishAll": "objavi Vse" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/sv.json b/imports/plugins/core/revisions/server/i18n/sv.json deleted file mode 100644 index 7bb54c470c7..00000000000 --- a/imports/plugins/core/revisions/server/i18n/sv.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "sv", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Förhandsvisning", - "revisionsLabel": "revideringar", - "revisionsTitle": "revideringar", - "revisionsDescription": "revisionskontroll" - }, - "catalogSettings": { - "productRevisionsLabel": "produkt~~POS=TRUNC Revideringar" - }, - "toolbar": { - "publishAll": "publicera All" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/tr.json b/imports/plugins/core/revisions/server/i18n/tr.json deleted file mode 100644 index e08af4a707e..00000000000 --- a/imports/plugins/core/revisions/server/i18n/tr.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "tr", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Önizleme", - "revisionsLabel": "Düzeltmeler", - "revisionsTitle": "Düzeltmeler", - "revisionsDescription": "Revizyon kontrolü" - }, - "catalogSettings": { - "productRevisionsLabel": "Ürün Düzeltmeler" - }, - "toolbar": { - "publishAll": "Tümünü Yayınla" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/vi.json b/imports/plugins/core/revisions/server/i18n/vi.json deleted file mode 100644 index 62efd5cb679..00000000000 --- a/imports/plugins/core/revisions/server/i18n/vi.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "vi", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "Xem trước", - "revisionsLabel": "Revisions", - "revisionsTitle": "Revisions", - "revisionsDescription": "kiểm soát sửa đổi" - }, - "catalogSettings": { - "productRevisionsLabel": "Sửa đổi sản phẩm" - }, - "toolbar": { - "publishAll": "Xuất bản Tất cả" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/i18n/zh.json b/imports/plugins/core/revisions/server/i18n/zh.json deleted file mode 100644 index 9cc0052a7a6..00000000000 --- a/imports/plugins/core/revisions/server/i18n/zh.json +++ /dev/null @@ -1,22 +0,0 @@ -[{ - "i18n": "zh", - "ns": "reaction-revisions", - "translation": { - "reaction-revisions": { - "admin": { - "dashboard": { - "preview": "预习", - "revisionsLabel": "修订", - "revisionsTitle": "修订", - "revisionsDescription": "版本控制" - }, - "catalogSettings": { - "productRevisionsLabel": "产品修订" - }, - "toolbar": { - "publishAll": "所有发布" - } - } - } - } -}] diff --git a/imports/plugins/core/revisions/server/index.js b/imports/plugins/core/revisions/server/index.js deleted file mode 100644 index 62d4d463737..00000000000 --- a/imports/plugins/core/revisions/server/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import "./hooks"; -import "./methods"; -import "./i18n"; -import "./publications"; diff --git a/imports/plugins/core/revisions/server/methods.js b/imports/plugins/core/revisions/server/methods.js deleted file mode 100644 index 10d3f53cac6..00000000000 --- a/imports/plugins/core/revisions/server/methods.js +++ /dev/null @@ -1,216 +0,0 @@ -import { Meteor } from "meteor/meteor"; -import { check, Match } from "meteor/check"; -import { Products, MediaRecords, Revisions, Packages } from "/lib/collections"; -import { Hooks, Logger } from "/server/api"; - -function handleImageRevision(revision) { - let result = 0; - if (revision.changeType === "insert") { - result = MediaRecords.update({ - _id: revision.documentId - }, { - $set: { - metadata: { ...revision.documentData, workflow: "published" } - } - }); - } else if (revision.changeType === "remove") { - result = MediaRecords.update({ - _id: revision.documentId - }, { - $set: { - "metadata.workflow": "archived" - } - }); - } else if (revision.changeType === "update") { - result = MediaRecords.update({ - _id: revision.documentId - }, { - $set: { - metadata: { ...revision.documentData, workflow: "published" } - } - }); - Logger.debug(`setting metadata for ${revision.documentId} to ${JSON.stringify(revision.documentData, null, 4)}`); - } - // mark revision published whether we are publishing the image or not - Revisions.update({ - _id: revision._id - }, { - $set: { - "workflow.status": "revision/published" - } - }); - - return result; -} - -export function updateSettings(settings) { - check(settings, Object); - - Packages.update({ - name: "reaction-revisions" - }, { - $set: { - settings - } - }); -} - -/** - * @name publishCatalogProduct - * @method - * @summary Updates revision and publishes a product. - * - * @param {String} userId - currently logged in user - * @param {Object} selector - selector for product to update - * @param {Object} modifier - Object describing what parts of the document to update. - * @param {Object} validation - simple schema validation - * @return {String} _id of updated document - * @private - */ -function publishCatalogProduct(userId, selector, modifier, validation) { - const product = Products.findOne(selector); - const options = { - userId, - modifier, - validation, - publish: true - }; - - Hooks.Events.run("beforeUpdateCatalogProduct", product, options); - - const result = Products.update(selector, modifier, validation); - - Hooks.Events.run("afterUpdateCatalogProduct", product, options); - - // Records are not remove from the Products collection, they are only flagged as deleted. - // Run Hook to remove search record, if a product is being published as deleted - // Which is the equivalent to removing a product. - if (modifier.$set.isDeleted === true) { - Hooks.Events.run("afterRemoveProduct", product); - } - - return result; -} - -export function discardDrafts(documentIds) { - check(documentIds, Match.OneOf(String, Array)); - - let documentIdArray; - - if (Array.isArray(documentIds)) { - documentIdArray = documentIds; - } else { - documentIdArray = [documentIds]; - } - - const selector = { - "workflow.status": { - $nin: [ - "revision/published" - ] - }, - "$or": [ - { - documentId: { - $in: documentIdArray - } - }, - { - "documentData.ancestors": { - $in: documentIdArray - } - }, - { - parentDocument: { - $in: documentIds - } - } - ] - }; - - const result = Revisions.remove(selector); - - return result > 0; -} - -Meteor.methods({ - "revisions/settings/update": updateSettings, - "revisions/discard": discardDrafts, - "revisions/publish"(documentIds) { - check(documentIds, Match.OneOf(String, Array)); - - // Also publish variants if they have a draft - let revisions; - - if (Array.isArray(documentIds)) { - revisions = Revisions.find({ - "workflow.status": { - $nin: [ - "revision/published" - ] - }, - "$or": [ - { - documentId: { - $in: documentIds - } - }, - { - "documentData.ancestors": { - $in: documentIds - } - }, - { - parentDocument: { - $in: documentIds - } - } - ] - }).fetch(); - } else { - revisions = Revisions.find({ - "workflow.status": { - $nin: [ - "revision/published" - ] - }, - "$or": [ - { documentId: documentIds }, - { - "documentData.ancestors": { - $in: [documentIds] - } - } - ] - }).fetch(); - } - - let updatedDocuments = 0; - if (revisions) { - for (const revision of revisions) { - if (!revision.documentType || revision.documentType === "product") { - const res = publishCatalogProduct( - this.userId, - { - _id: revision.documentId - }, - { - $set: revision.documentData - } - ); - updatedDocuments += res; - } else if (revision.documentType === "image") { - updatedDocuments += handleImageRevision(revision); - } - } - } - - if (updatedDocuments > 0) { - return { - status: "success" - }; - } - - return false; - } -}); diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.js b/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.js deleted file mode 100644 index 461b4adab4b..00000000000 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * - * @method getProduct - * @summary Get a Product object by variant ID. - * This function will return an UNPUBLISHED Product Revision. - * @todo: Revisit why this function is returning unpublished product revision. - * @param {string} variantId - A product variant ID. - * @param {Object} collections - Raw mongo collections - * @return {Promise} Product object - */ -export default async function getProduct(variantId, collections) { - const { Products, Revisions } = collections; - const revision = await Revisions.findOne({ - "documentId": variantId, - "workflow.status": { - $nin: ["revision/published"] - } - }); - - if (revision && revision.documentData) { - return revision.documentData; - } - - const product = await Products.findOne({ _id: variantId }); - return product; -} diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.test.js b/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.test.js deleted file mode 100644 index 7e847814a34..00000000000 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getProduct.test.js +++ /dev/null @@ -1,217 +0,0 @@ -import mockContext from "/imports/test-utils/helpers/mockContext"; -import getProduct from "./getProduct"; - -const mockCollections = { ...mockContext.collections }; - -const internalShopId = "123"; -const opaqueShopId = "cmVhY3Rpb24vc2hvcDoxMjM="; // reaction/shop:123 -const internalCatalogItemId = "999"; -const internalCatalogProductId = "999"; -const internalProductId = "999"; -const internalTagIds = ["923", "924"]; -const internalVariantIds = ["875", "874"]; - -const productSlug = "fake-product"; - -const createdAt = new Date("2018-04-16T15:34:28.043Z"); -const updatedAt = new Date("2018-04-17T15:34:28.043Z"); -const positionUpdatedAt = new Date("2018-04-15T15:34:28.043Z"); - -const mockVariants = [ - { - _id: internalVariantIds[0], - ancestors: [internalCatalogProductId], - barcode: "barcode", - createdAt, - height: 0, - index: 0, - inventoryManagement: true, - inventoryPolicy: false, - isLowQuantity: true, - isSoldOut: false, - isDeleted: false, - isVisible: true, - length: 0, - lowInventoryWarningThreshold: 0, - metafields: [ - { - value: "value", - namespace: "namespace", - description: "description", - valueType: "valueType", - scope: "scope", - key: "key" - } - ], - minOrderQuantity: 0, - optionTitle: "Untitled Option", - originCountry: "US", - price: 0, - shopId: internalShopId, - sku: "sku", - taxable: true, - taxCode: "0000", - taxDescription: "taxDescription", - title: "Small Concrete Pizza", - updatedAt, - variantId: internalVariantIds[0], - weight: 0, - width: 0 - }, - { - _id: internalVariantIds[1], - ancestors: [internalCatalogProductId, internalVariantIds[0]], - barcode: "barcode", - height: 2, - index: 0, - inventoryManagement: true, - inventoryPolicy: true, - isLowQuantity: true, - isSoldOut: false, - isDeleted: false, - isVisible: true, - length: 2, - lowInventoryWarningThreshold: 0, - metafields: [ - { - value: "value", - namespace: "namespace", - description: "description", - valueType: "valueType", - scope: "scope", - key: "key" - } - ], - minOrderQuantity: 0, - optionTitle: "Awesome Soft Bike", - originCountry: "US", - price: 992.0, - shopId: internalShopId, - sku: "sku", - taxable: true, - taxCode: "0000", - taxDescription: "taxDescription", - title: "One pound bag", - variantId: internalVariantIds[1], - weight: 2, - width: 2 - } -]; - -const mockProduct = { - _id: internalCatalogItemId, - shopId: internalShopId, - barcode: "barcode", - createdAt, - description: "description", - facebookMsg: "facebookMessage", - fulfillmentService: "fulfillmentService", - googleplusMsg: "googlePlusMessage", - height: 11.23, - isBackorder: false, - isLowQuantity: false, - isSoldOut: false, - length: 5.67, - lowInventoryWarningThreshold: 2, - metafields: [ - { - value: "value", - namespace: "namespace", - description: "description", - valueType: "valueType", - scope: "scope", - key: "key" - } - ], - metaDescription: "metaDescription", - minOrderQuantity: 5, - originCountry: "originCountry", - pageTitle: "pageTitle", - parcel: { - containers: "containers", - length: 4.44, - width: 5.55, - height: 6.66, - weight: 7.77 - }, - pinterestMsg: "pinterestMessage", - positions: { - _default: { - weight: 1, - position: 1, - pinned: true, - updatedAt: positionUpdatedAt.toISOString() - } - }, - price: { - max: 5.99, - min: 2.99, - range: "2.99 - 5.99" - }, - media: [ - { - metadata: { - toGrid: 1, - priority: 1, - productId: internalProductId, - variantId: null - }, - thumbnail: "http://localhost/thumbnail", - small: "http://localhost/small", - medium: "http://localhost/medium", - large: "http://localhost/large", - image: "http://localhost/original" - } - ], - productId: internalProductId, - productType: "productType", - requiresShipping: true, - shop: { - _id: opaqueShopId - }, - sku: "ABC123", - handle: productSlug, - hashtags: internalTagIds, - taxCode: "taxCode", - taxDescription: "taxDescription", - taxable: false, - title: "Fake Product Title", - twitterMsg: "twitterMessage", - type: "product-simple", - updatedAt, - mockVariants, - vendor: "vendor", - weight: 15.6, - width: 8.4 -}; - -const mockRevision = { - _id: "333", - documentId: internalProductId, - documentData: { - _id: internalProductId - }, - workflow: { - status: "revision/published" - }, - documentType: "product", - createdAt, - updatedAt, - diff: [] -}; - -// expect to return revision object if one exist -test("expect to return a product revision object if one exist", async () => { - mockCollections.Products.findOne.mockReturnValueOnce(Promise.resolve(mockProduct)); - mockCollections.Revisions.findOne.mockReturnValueOnce(Promise.resolve(mockRevision)); - const spec = await getProduct(internalVariantIds[0], mockCollections); - expect(spec).toEqual(mockRevision.documentData); -}); - -// expect to return a product object if no revision exist -test("expect to return a product object if no revision exist", async () => { - mockCollections.Products.findOne.mockReturnValueOnce(Promise.resolve(mockProduct)); - mockCollections.Revisions.findOne.mockReturnValueOnce(Promise.resolve(undefined)); - const spec = await getProduct(internalVariantIds[0], mockCollections); - expect(spec).toEqual(mockProduct); -}); diff --git a/imports/plugins/core/revisions/server/no-meteor/utils/getVariants.js b/imports/plugins/core/revisions/server/no-meteor/utils/getVariants.js deleted file mode 100644 index ef74c4e7f7a..00000000000 --- a/imports/plugins/core/revisions/server/no-meteor/utils/getVariants.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * - * @method getVariants - * @summary Get all of a Product's Variants or only a Product's top level Variants. - * This function will return UNPUBLISHED variant Revisions. - * @todo: Revisit why this function is returning unpublished variant revisions. - * @param {string} productOrVariantId - A Product or top level Product Variant ID. - * @param {Object} collections - Raw mongo collections. - * @param {boolean} topOnly - True to return only a products top level variants. - * @return {Promise} Array of Product Variant objects. - */ -export default async function getVariants(productOrVariantId, collections, topOnly) { - const { Products, Revisions } = collections; - const variants = []; - - const productVariants = await Products.find({ - ancestors: topOnly ? [productOrVariantId] : productOrVariantId, - type: "variant", - isDeleted: { $ne: true } - }).toArray(); - - await Promise.all(productVariants.map(async (variant) => { - const revision = await Revisions.findOne({ - "documentId": variant._id, - "workflow.status": { - $nin: ["revision/published"] - } - }); - - if (revision && revision.documentData.isVisible) { - variants.push(revision.documentData); - } else if (!revision && variant.isVisible) { - variants.push(variant); - } - })); - - return variants; -} diff --git a/imports/plugins/core/revisions/server/publications.js b/imports/plugins/core/revisions/server/publications.js deleted file mode 100644 index 920769bf5a3..00000000000 --- a/imports/plugins/core/revisions/server/publications.js +++ /dev/null @@ -1,47 +0,0 @@ -import { Meteor } from "meteor/meteor"; -import { check } from "meteor/check"; -import { Roles } from "meteor/alanning:roles"; -import { Revisions } from "/lib/collections"; -import { Reaction } from "/server/api"; - -/** - * products publication - * @param {Number} productScrollLimit - optional, defaults to 24 - * @param {Array} shops - array of shopId to retrieve product from. - * @return {Object} return product cursor - */ -Meteor.publish("ProductRevisions", function (productIds) { - check(productIds, Array); - - const shop = Reaction.getShopId(); - // Authorized content curators fo the shop get special publication of the product - // all all relevant revisions all is one package - if (Roles.userIsInRole(this.userId, ["owner", "admin", "createProduct"], shop._id)) { - return Revisions.find({ - "$or": [ - { - documentId: { - $in: productIds - } - }, - { - "documentData.ancestors": { - $in: productIds - } - }, - { - parentDocument: { - $in: productIds - } - } - ], - "workflow.status": { - $nin: [ - "revision/published" - ] - } - }); - } - - return this.ready(); -}); diff --git a/imports/plugins/core/ui/client/containers/edit.js b/imports/plugins/core/ui/client/containers/edit.js index 547b8294813..0d2357806c8 100644 --- a/imports/plugins/core/ui/client/containers/edit.js +++ b/imports/plugins/core/ui/client/containers/edit.js @@ -65,47 +65,8 @@ class EditContainer extends Component { renderEditButton() { let status; let tooltip; - let hasChange = false; - - if (this.props.data.__draft && this.props.field) { - const draft = this.props.data.__draft; - - if (Array.isArray(draft.diff)) { - for (const diff of draft.diff) { - let hasChangedField = false; - - if (Array.isArray(this.props.field)) { - if (this.props.field.indexOf(diff.path[0]) >= 0) { - hasChangedField = true; - } - } else if (typeof this.props.field === "string" && this.props.field === diff.path[0]) { - hasChangedField = true; - } - - if (hasChangedField) { - status = "warning"; - - tooltip = ( - - - - ); - - hasChange = true; - } - } - } - } else if (this.props.data.__draft) { - status = "warning"; - - tooltip = ( - - - - ); - } - if (this.props.autoHideEditButton && hasChange === false) { + if (this.props.autoHideEditButton) { return null; } diff --git a/imports/plugins/core/ui/client/containers/mediaGallery.js b/imports/plugins/core/ui/client/containers/mediaGallery.js index 4d093e83ffd..3bf40a84c27 100644 --- a/imports/plugins/core/ui/client/containers/mediaGallery.js +++ b/imports/plugins/core/ui/client/containers/mediaGallery.js @@ -10,8 +10,6 @@ import { Meteor } from "meteor/meteor"; import MediaGallery from "../components/media/mediaGallery"; import { Logger, Reaction } from "/client/api"; import { ReactionProduct } from "/lib/api"; -import { Revisions } from "/lib/collections"; -import { isRevisionControlEnabled } from "/imports/plugins/core/revisions/lib/api"; import { Media } from "/imports/plugins/core/files/client"; const wrapComponent = (Comp) => ( @@ -213,18 +211,6 @@ const wrapComponent = (Comp) => ( } ); -function fetchMediaRevisions() { - const productId = ReactionProduct.selectedProductId(); - const mediaRevisions = Revisions.find({ - "parentDocument": productId, - "documentType": "image", - "workflow.status": { - $nin: ["revision/published"] - } - }).fetch(); - return mediaRevisions; -} - // resort the media in function sortMedia(media) { const sortedMedia = _.sortBy(media, (m) => { @@ -237,35 +223,10 @@ function sortMedia(media) { return sortedMedia; } -// Search through revisions and if we find one for the image, stick it on the object -function appendRevisionsToMedia(props, media) { - if (!isRevisionControlEnabled() || !Reaction.hasPermission(props.permission || ["createProduct"])) { - return media; - } - const mediaRevisions = fetchMediaRevisions(); - const newMedia = []; - for (const image of media) { - image.revision = undefined; - for (const revision of mediaRevisions) { - if (revision.documentId === image._id) { - image.revision = revision; - image.metadata.priority = revision.documentData.priority; - } - } - newMedia.push(image); - } - return sortMedia(newMedia); -} - function composer(props, onData) { - let media; let editable; const viewAs = Reaction.getUserPreferences("reaction-dashboard", "viewAs", "administrator"); - if (props.media) { - media = appendRevisionsToMedia(props, props.media); - } - if (viewAs === "customer") { editable = false; } else { @@ -274,7 +235,7 @@ function composer(props, onData) { onData(null, { editable, - media + media: sortMedia(props.media) }); } diff --git a/imports/plugins/core/versions/server/migrations/25_remove_revision_control.js b/imports/plugins/core/versions/server/migrations/25_remove_revision_control.js new file mode 100644 index 00000000000..6f606421d2d --- /dev/null +++ b/imports/plugins/core/versions/server/migrations/25_remove_revision_control.js @@ -0,0 +1,12 @@ +import { Migrations } from "meteor/percolate:migrations"; +import { Packages } from "/lib/collections"; + +// Migration file created for removing the revision control package registry entry from all shops +Migrations.add({ + version: 25, + up() { + Packages.remove({ + name: "reaction-revisions" + }); + } +}); diff --git a/imports/plugins/core/versions/server/migrations/index.js b/imports/plugins/core/versions/server/migrations/index.js index 26658655eb5..4ba42c448a0 100644 --- a/imports/plugins/core/versions/server/migrations/index.js +++ b/imports/plugins/core/versions/server/migrations/index.js @@ -22,3 +22,4 @@ import "./21_clean_cart_shipment_method"; import "./22_register_verify_account"; import "./23_drop_tempstore_collections"; import "./24_publish_all_existing_visible_products"; +import "./25_remove_revision_control"; diff --git a/imports/plugins/included/inventory/server/methods/inventory.app-test.js b/imports/plugins/included/inventory/server/methods/inventory.app-test.js index 91f56bdb3bf..98cdde81a95 100644 --- a/imports/plugins/included/inventory/server/methods/inventory.app-test.js +++ b/imports/plugins/included/inventory/server/methods/inventory.app-test.js @@ -8,7 +8,6 @@ import { expect } from "meteor/practicalmeteor:chai"; import { sinon } from "meteor/practicalmeteor:sinon"; import { addProduct } from "/server/imports/fixtures/products"; import Fixtures from "/server/imports/fixtures"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; Fixtures(); @@ -41,7 +40,6 @@ describe("inventory method", function () { beforeEach(function () { sandbox = sinon.sandbox.create(); - sandbox.stub(RevisionApi, "isRevisionControlEnabled", () => true); // again hack. w/o this we can't remove products from previous spec. Inventory.remove({}); // Empty Inventory }); diff --git a/imports/plugins/included/inventory/server/methods/inventory.js b/imports/plugins/included/inventory/server/methods/inventory.js index 8c41e7209b4..bc049e059ac 100644 --- a/imports/plugins/included/inventory/server/methods/inventory.js +++ b/imports/plugins/included/inventory/server/methods/inventory.js @@ -2,7 +2,7 @@ import { Meteor } from "meteor/meteor"; import { Inventory, Products } from "/lib/collections"; import { Logger, Reaction } from "/server/api"; import rawCollections from "/imports/collections/rawCollections"; -import getVariants from "/imports/plugins/core/revisions/server/no-meteor/utils/getVariants"; +import getVariants from "/imports/plugins/core/catalog/server/no-meteor/utils/getVariants"; /** * @namespace Inventory/Methods diff --git a/imports/plugins/included/product-detail-simple/client/containers/publish.js b/imports/plugins/included/product-detail-simple/client/containers/publish.js index 07a10f6dd19..13d55f5fe3a 100644 --- a/imports/plugins/included/product-detail-simple/client/containers/publish.js +++ b/imports/plugins/included/product-detail-simple/client/containers/publish.js @@ -4,7 +4,7 @@ import { registerComponent, composeWithTracker } from "@reactioncommerce/reactio import { Meteor } from "meteor/meteor"; import { ReactionProduct } from "/lib/api"; import { Products } from "/lib/collections"; -import PublishContainer from "/imports/plugins/core/revisions/client/containers/publishContainer"; +import PublishContainer from "/imports/plugins/core/catalog/client/containers/publishContainer"; class ProductPublishContainer extends Component { handleMetaRemove = (productId, metafield) => { diff --git a/imports/plugins/included/product-variant/client/templates/products/productSettings/productSettings.js b/imports/plugins/included/product-variant/client/templates/products/productSettings/productSettings.js index 013dd3c6946..0abc5284dcc 100644 --- a/imports/plugins/included/product-variant/client/templates/products/productSettings/productSettings.js +++ b/imports/plugins/included/product-variant/client/templates/products/productSettings/productSettings.js @@ -6,8 +6,6 @@ import { Reaction } from "/client/api"; import Logger from "/client/modules/logger"; import { Catalog, getPrimaryMediaForItem, ReactionProduct } from "/lib/api"; import { Products } from "/lib/collections"; -import { isRevisionControlEnabled } from "/imports/plugins/core/revisions/lib/api"; -import { applyProductRevision } from "/lib/api/products"; function updateVariantProductField(variants, field, value) { return variants.map((variant) => Meteor.call("products/updateProductField", variant._id, field, value)); @@ -30,7 +28,7 @@ Template.productSettings.onCreated(function () { _id: { $in: productIds } - }).map((product) => applyProductRevision(product)); + }).fetch(); this.state.set("productIds", productIds); this.state.set("products", products); @@ -122,28 +120,18 @@ Template.productSettings.events({ const instance = Template.instance(); const products = instance.state.get("products") || []; - if (isRevisionControlEnabled()) { - for (const product of products) { - // Update the visibility using the first selected product to determine the proper - // visibility toggle. This is to ensure that all selected products will become visible or not visible - // at the same time so it's not confusing. - Meteor.call("products/updateProductField", product._id, "isVisible", !products[0].isVisible); - // update the variants visibility - const variants = Products.find({ - ancestors: { - $in: [product._id] - } - }); - updateVariantProductField(variants, "isVisible", !products[0].isVisible); - } - } else { - // The legacy behavior will bulk toggle visibilty of each product seperatly. - // - // Example: - // If you selected 10 products, and 5 were visible and 5 were not visible, and then - // clicked the visibility button, 5 products would switched from not visible to visible, and the other 5 - // would be swiched from visible to not visible. - ReactionProduct.publishProduct(products); + for (const product of products) { + // Update the visibility using the first selected product to determine the proper + // visibility toggle. This is to ensure that all selected products will become visible or not visible + // at the same time so it's not confusing. + Meteor.call("products/updateProductField", product._id, "isVisible", !products[0].isVisible); + // update the variants visibility + const variants = Products.find({ + ancestors: { + $in: [product._id] + } + }); + updateVariantProductField(variants, "isVisible", !products[0].isVisible); } }, "click [data-event-action=cloneProduct]"() { diff --git a/imports/plugins/included/product-variant/components/variantForm.js b/imports/plugins/included/product-variant/components/variantForm.js index 4e46afa9eb1..b1f1bf92471 100644 --- a/imports/plugins/included/product-variant/components/variantForm.js +++ b/imports/plugins/included/product-variant/components/variantForm.js @@ -173,13 +173,13 @@ class VariantForm extends Component { const inverseValue = !value; this.setState(({ variant }) => ({ + inventoryPolicy: inverseValue, variant: { ...variant, [field]: inverseValue } })); - this.handleFieldBlur(event, inverseValue, field); } diff --git a/imports/plugins/included/product-variant/containers/gridPublishContainer.js b/imports/plugins/included/product-variant/containers/gridPublishContainer.js index 52df62f8448..d18c46c2170 100644 --- a/imports/plugins/included/product-variant/containers/gridPublishContainer.js +++ b/imports/plugins/included/product-variant/containers/gridPublishContainer.js @@ -5,7 +5,7 @@ import { Meteor } from "meteor/meteor"; import { Session } from "meteor/session"; import { ReactionProduct } from "/lib/api"; import { Products } from "/lib/collections"; -import PublishContainer from "/imports/plugins/core/revisions/client/containers/publishContainer"; +import PublishContainer from "/imports/plugins/core/catalog/client/containers/publishContainer"; class GridProductPublishContainer extends Component { static propTypes = { diff --git a/imports/plugins/included/product-variant/containers/productsContainerAdmin.js b/imports/plugins/included/product-variant/containers/productsContainerAdmin.js index d8a6176e660..c2d2b90225e 100644 --- a/imports/plugins/included/product-variant/containers/productsContainerAdmin.js +++ b/imports/plugins/included/product-variant/containers/productsContainerAdmin.js @@ -9,7 +9,6 @@ import { Tracker } from "meteor/tracker"; import { Reaction } from "/client/api"; import { ITEMS_INCREMENT } from "/client/config/defaults"; import { ReactionProduct } from "/lib/api"; -import { applyProductRevision, resubscribeAfterCloning } from "/lib/api/products"; import { Products, Tags, Shops } from "/lib/collections"; import ProductsComponent from "../components/products"; @@ -157,10 +156,6 @@ function composer(props, onData) { const queryParams = Object.assign({}, tags, Reaction.Router.current().query, shopIds); const productsSubscription = Meteor.subscribe("Products", scrollLimit, queryParams, sort, editMode); - if (resubscribeAfterCloning.get()) { - resubscribeAfterCloning.set(false); - productsSubscription.stop(); - } if (productsSubscription.ready()) { window.prerenderReady = true; @@ -179,12 +174,8 @@ function composer(props, onData) { shopId: { $in: activeShopsIds } }); - const productIds = []; - const products = productCursor.map((product) => { - productIds.push(product._id); - - return applyProductRevision(product); - }); + const products = productCursor.fetch(); + const productIds = productCursor.map((product) => product._id); const sortedProducts = ReactionProduct.sortProducts(products, currentTagId); Session.set("productGrid/products", sortedProducts); diff --git a/imports/plugins/included/product-variant/containers/variantEditContainer.js b/imports/plugins/included/product-variant/containers/variantEditContainer.js index 6480ed5dd9f..ea27e327c70 100644 --- a/imports/plugins/included/product-variant/containers/variantEditContainer.js +++ b/imports/plugins/included/product-variant/containers/variantEditContainer.js @@ -7,7 +7,6 @@ import { ReactionProduct } from "/lib/api"; import { Products } from "/lib/collections"; import { Countries } from "/client/collections"; import { Reaction, i18next } from "/client/api"; -import { applyProductRevision } from "/lib/api/products"; import VariantEdit from "../components/variantEdit"; @@ -72,14 +71,13 @@ function composer(props, onData) { _id: ReactionProduct.selectedTopVariant()._id }); - const revisedVariant = applyProductRevision(variant); - const childVariants = ReactionProduct.getVariants(revisedVariant._id); + const childVariants = ReactionProduct.getVariants(variant._id); onData(null, { countries: Countries.find({}).fetch(), editFocus: Reaction.state.get("edit/focus"), childVariants, - variant: revisedVariant + variant }); } else { onData(null, { diff --git a/lib/api/catalog.js b/lib/api/catalog.js index 29e359151e5..30e2d0b126c 100644 --- a/lib/api/catalog.js +++ b/lib/api/catalog.js @@ -1,7 +1,6 @@ import _ from "lodash"; import { Products } from "/lib/collections"; import { ReactionProduct } from "/lib/api"; -import { applyProductRevision } from "/lib/api/products"; /** * @file Catalog methods @@ -43,7 +42,7 @@ export default { * @return {Object} range, min, max */ getProductPriceRange(productId) { - const product = applyProductRevision(Products.findOne(productId)); + const product = Products.findOne(productId); if (!product) { return { range: "0", @@ -115,7 +114,7 @@ export default { switch (visibleChildren.length) { case 0: { - const topVariant = applyProductRevision(Products.findOne(variantId)); + const topVariant = Products.findOne(variantId); // topVariant could be undefined when we removing last top variant return topVariant && topVariant.price; } @@ -162,14 +161,15 @@ export default { }, /** - * @method getPublishedOrRevision - * @memberof Catalog - * @description return top product revision if available - * @param {Object} product product or variant document - * @return {Object} product document - */ - getPublishedOrRevision(product) { - return applyProductRevision(product); + * @method getProduct + * @method + * @memberof ReactionProduct + * @summary Get product object. Could be useful for products and for top level variants + * @param {String} [id] - product _id + * @return {Object} Product data + */ + getProduct(id) { + return Products.findOne(id); }, /** @@ -185,7 +185,7 @@ export default { return Products.find({ ancestors: { $in: [id] }, type: type || "variant" - }).map(this.getPublishedOrRevision); + }).fetch(); }, /** @@ -197,11 +197,10 @@ export default { * @return {Array} Parent variant or empty */ getVariantParent(variant) { - const parent = Products.findOne({ + return Products.findOne({ _id: { $in: variant.ancestors }, type: "variant" }); - return this.getPublishedOrRevision(parent); }, /** @@ -218,7 +217,7 @@ export default { return Products.find({ ancestors: variant.ancestors, type: type || "variant" - }).map(this.getPublishedOrRevision); + }).fetch(); }, /** @@ -232,6 +231,6 @@ export default { return Products.find({ ancestors: [id], type: "variant" - }).map(this.getPublishedOrRevision); + }).fetch(); } }; diff --git a/lib/api/products.js b/lib/api/products.js index 941e0a23a7d..de74e899699 100644 --- a/lib/api/products.js +++ b/lib/api/products.js @@ -5,7 +5,7 @@ import { Meteor } from "meteor/meteor"; import { ReactiveDict } from "meteor/reactive-dict"; import { ReactiveVar } from "meteor/reactive-var"; import { Router } from "/imports/plugins/core/router/lib"; -import { Products, Revisions, Tags } from "/lib/collections"; +import { Products, Tags } from "/lib/collections"; import Catalog from "./catalog"; import { MetaData } from "/lib/api/router/metadata"; @@ -32,44 +32,6 @@ import { MetaData } from "/lib/api/router/metadata"; */ const ReactionProduct = new ReactiveDict("currentProduct"); -/** - * @name applyProductRevision - * @method - * @memberof ReactionProduct - * @summary Apply revision to product - * @example applyProductRevision(product) - * @param {Object} product product - * @return {Object|null} product or null, if no product found - */ -export function applyProductRevision(product) { - if (product) { - if (product.__revisions && product.__revisions.length) { - const cleanProduct = Object.assign({}, product); - delete cleanProduct.__revisions; - let revisedProduct; - // check for product revisions and set that as the current product - for (const revision of product.__revisions) { - if (!revision.parentDocument) { - revisedProduct = product.__revisions[0].documentData; - } - } - - // if there are no revision to product (image and/or tag only) just set the original product as the product - if (!revisedProduct) { - revisedProduct = cleanProduct; - } - - return Object.assign({}, revisedProduct, { - __published: cleanProduct, - __draft: product.__revisions[0] - }); - } - return product; - } - - return null; -} - /** * @name variantIsSelected * @method @@ -196,7 +158,7 @@ ReactionProduct.setProduct = (currentProductId, currentVariantId) => { // Update the meta data when a product is selected MetaData.init(Router.current()); - return applyProductRevision(product); + return product; }; /** @@ -241,7 +203,7 @@ ReactionProduct.selectedVariantId = () => { ReactionProduct.selectedVariant = function () { const id = ReactionProduct.selectedVariantId(); if (typeof id === "string") { - return applyProductRevision(Products.findOne(id)); + return Products.findOne(id); } return []; }; @@ -270,7 +232,7 @@ ReactionProduct.selectedTopVariant = function () { ReactionProduct.selectedProduct = function () { const id = ReactionProduct.selectedProductId(); if (typeof id === "string") { - return applyProductRevision(Products.findOne(id)); + return Products.findOne(id); } return undefined; }; @@ -633,14 +595,11 @@ ReactionProduct.isAncestorDeleted = function (product, includeSelf) { productIds.push(product._id); } - // Verify there are no deleted ancestors, + // Verify there are no deleted ancestors // Variants cannot be restored if their parent product / variant is deleted - const archivedCount = Revisions.find({ - "documentId": { $in: productIds }, - "documentData.isDeleted": true, - "workflow.status": { - $nin: ["revision/published"] - } + const archivedCount = Products.find({ + _id: { $in: productIds }, + isDeleted: true }).count(); if (archivedCount > 0) { diff --git a/lib/collections/collections.js b/lib/collections/collections.js index 3a7f2ae83e7..af4c8a0debe 100644 --- a/lib/collections/collections.js +++ b/lib/collections/collections.js @@ -141,15 +141,6 @@ export const Products = new Mongo.Collection("Products"); Products.attachSchema(Schemas.Product, { selector: { type: "simple" } }); Products.attachSchema(Schemas.ProductVariant, { selector: { type: "variant" } }); -/** - * @name Revisions - * @memberof Collections - * @type {MongoCollection} - */ -export const Revisions = new Mongo.Collection("Revisions"); - -Revisions.attachSchema(Schemas.Revisions); - /** * @name Shipping * @memberof Collections diff --git a/lib/collections/schemas/index.js b/lib/collections/schemas/index.js index 160941ba546..3d7b2328294 100644 --- a/lib/collections/schemas/index.js +++ b/lib/collections/schemas/index.js @@ -27,7 +27,6 @@ export * from "./orders"; export * from "./payments"; export * from "./products"; export * from "./registry"; -export * from "./revisions"; export * from "./shipping"; export * from "./shops"; export * from "./groups"; diff --git a/lib/collections/schemas/revisions.js b/lib/collections/schemas/revisions.js deleted file mode 100644 index db2498564fa..00000000000 --- a/lib/collections/schemas/revisions.js +++ /dev/null @@ -1,80 +0,0 @@ -import SimpleSchema from "simpl-schema"; -import { check } from "meteor/check"; -import { Tracker } from "meteor/tracker"; -import { registerSchema } from "@reactioncommerce/schemas"; -import { createdAtAutoValue, updatedAtAutoValue } from "./helpers"; -import { Workflow } from "./workflow"; - -/** - * @name Revisions - * @memberof Schemas - * @type {SimpleSchema} - * @property {String} _id Revision Id - * @property {Workflow} workflow required - * @property {String} documentId Reference Document Id - * @property {String} documentType Document Type, default value: `product`, allowed values: `product`, `image`, `tag` - * @property {String} parentDocument optional - * @property {"object"} documentData blackbox object - * @property {String} changeType optional, allowed values: `insert`, `update`, `remove` - * @property {Object[]} diff optional, blackbox - * @property {Date} createdAt required - * @property {Date} updatedAt optional - * @property {Date} publishAt optional - */ -export const Revisions = new SimpleSchema({ - "_id": { - type: String, - label: "Revision Id" - }, - "workflow": { - type: Workflow, - optional: false, - defaultValue: {} - }, - "documentId": { - type: String, - label: "Reference Document Id" - }, - "documentType": { - type: String, - label: "Document Type", - defaultValue: "product", - allowedValues: ["product", "image", "tag"] - }, - "parentDocument": { - type: String, - optional: true - }, - "documentData": { - type: "object", - blackbox: true - }, - "changeType": { - type: String, - optional: true, - allowedValues: ["insert", "update", "remove"] - }, - "diff": { - type: Array, - optional: true - }, - "diff.$": { - type: Object, - blackbox: true - }, - "createdAt": { - type: Date, - autoValue: createdAtAutoValue - }, - "updatedAt": { - type: Date, - autoValue: updatedAtAutoValue, - optional: true - }, - "publishAt": { - type: Date, - optional: true - } -}, { check, tracker: Tracker }); - -registerSchema("Revisions", Revisions); diff --git a/package-lock.json b/package-lock.json index a42baf20236..f69a91f86a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8839,9 +8839,9 @@ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, "public-encrypt": { "version": "4.0.0", @@ -8883,7 +8883,7 @@ "requires": { "inherits": "2.0.3", "isarray": "1.0.0", - "process-nextick-args": "2.0.0", + "process-nextick-args": "1.0.7", "safe-buffer": "5.1.1", "string_decoder": "1.0.3", "util-deprecate": "1.0.2" diff --git a/private/data/i18n/ar.json b/private/data/i18n/ar.json index b5997c3ef92..5720bf9a9e7 100644 --- a/private/data/i18n/ar.json +++ b/private/data/i18n/ar.json @@ -513,20 +513,6 @@ "isCommercial": "هذه عناوين تجارية" } }, - "revisions": { - "isDisabled": ".مراجعة المراقبة غير مفعل .أي تغيير سوف يتم نشره فوراً", - "isEnabled": "يتم تمكين مراجعة مراقبة للمنتجات. سوف يحتاج أي تغييرات على أن تنشر قبل أن يكون مرئيا للعملاء.", - "unpublishedChanges": "تغييرات غير منشورة", - "publishChanges": "تغييرات النشر", - "showChanges": "إظهار التغييرات", - "hideChanges": "إخفاء التغييرات", - "discardChanges": "التراجع عن التغييرات ", - "changedPublished": "تم نشر التغييرات بنجاح", - "changesDiscarded": "تم التراجع عن التغييرات بنجاح ", - "noChanges": "لا يوجد تغييرات", - "noChangesPublished": ".لا يوجد تغييرات لنشرها", - "noChangesDiscarded": "لايوجد تغييرات للتراجع عنها " - }, "address": { "country": "البلد", "fullName": "الأسم كامل", diff --git a/private/data/i18n/bg.json b/private/data/i18n/bg.json index e326d60c6ce..ac4566b42f8 100644 --- a/private/data/i18n/bg.json +++ b/private/data/i18n/bg.json @@ -513,20 +513,6 @@ "isCommercial": "Това е търговски адрес." } }, - "revisions": { - "isDisabled": "контрол на контрол е забранено. Всички промени ще бъдат публикувани веднага.", - "isEnabled": "контрол на контрол е активиран за продукти. ще се нуждае от никакви промени, за да бъдат публикувани преди да бъдат видими за клиентите.", - "unpublishedChanges": "Непубликувани промени", - "publishChanges": "Публикуване на промените", - "showChanges": "Показване на промените", - "hideChanges": "Скриване на промените", - "discardChanges": "Отхвърлите промените", - "changedPublished": "Промени публикувани успешно", - "changesDiscarded": "Промени изхвърли успешно", - "noChanges": "Няма промени", - "noChangesPublished": "Няма промени за публикуване", - "noChangesDiscarded": "Няма промени, за да изхвърли" - }, "address": { "country": "Държава", "fullName": "Пълно име", diff --git a/private/data/i18n/cs.json b/private/data/i18n/cs.json index 9635919d934..2e967bbcf81 100644 --- a/private/data/i18n/cs.json +++ b/private/data/i18n/cs.json @@ -513,20 +513,6 @@ "isCommercial": "Jedná se o obchodní adresu." } }, - "revisions": { - "isDisabled": "Ovládání revize je zakázáno. Jakékoli změny budou zveřejněny okamžitě.", - "isEnabled": "Ovládání revize je povoleno za produkty. bude muset případné změny, které mají být zveřejněny dříve, než jsou viditelné pro zákazníky.", - "unpublishedChanges": "nepublikované Změny", - "publishChanges": "Publish changes", - "showChanges": "Zobrazit změny", - "hideChanges": "skrýt Změny", - "discardChanges": "Zrušit změny", - "changedPublished": "Změny úspěšně publikován", - "changesDiscarded": "Změny úspěšně zlikvidovat", - "noChanges": "Žádné změny", - "noChangesPublished": "Nejsou žádné změny publikovat", - "noChangesDiscarded": "Nejsou žádné změny k vyhazování" - }, "address": { "country": "Země", "fullName": "Celé jméno", diff --git a/private/data/i18n/de.json b/private/data/i18n/de.json index 33320f8aabb..4d831df918d 100644 --- a/private/data/i18n/de.json +++ b/private/data/i18n/de.json @@ -513,20 +513,6 @@ "isCommercial": "This is a commercial address." } }, - "revisions": { - "isDisabled": "Die Revisionsverwaltung ist deaktiviert. Jegliche Änderungen werden sofort veröffentlicht.", - "isEnabled": "Revisionskontrolle für Produkte aktiviert. Alle Änderungen werden müssen, bevor sie an den Kunden sichtbar veröffentlicht.", - "unpublishedChanges": "Unveröffentlichte Änderungen", - "publishChanges": "Änderungen veröffentlichen", - "showChanges": "Änderungen anzeigen", - "hideChanges": "Änderungen verbergen", - "discardChanges": "Änderungen verwerfen", - "changedPublished": "Die Änderungen wurden erfolgreich veröffentlicht", - "changesDiscarded": "Änderungen erfolgreich verworfen", - "noChanges": "Keine Änderungen", - "noChangesPublished": "Es gibt keine Änderungen zu veröffentlichen", - "noChangesDiscarded": "Keine zu verwerfenden Änderungen gefunden" - }, "address": { "country": "Land", "fullName": "Vor- und Zuname", diff --git a/private/data/i18n/el.json b/private/data/i18n/el.json index 797072181fc..48bf2a57dd1 100644 --- a/private/data/i18n/el.json +++ b/private/data/i18n/el.json @@ -513,20 +513,6 @@ "isCommercial": "Είναι εμπορική διεύθυνση;" } }, - "revisions": { - "isDisabled": "έλεγχος αναθεώρηση είναι απενεργοποιημένη. Οποιεσδήποτε αλλαγές θα δημοσιεύονται αμέσως.", - "isEnabled": "ελέγχου αναθεώρησης είναι ενεργοποιημένη για Προϊόντα. Οποιεσδήποτε αλλαγές θα πρέπει να δημοσιεύονται πριν να είναι ορατή στους πελάτες.", - "unpublishedChanges": "Ανέκδοτες Αλλαγές", - "publishChanges": "Δημοσίευση αλλαγών", - "showChanges": "Εμφάνιση αλλαγών", - "hideChanges": "Απόκρυψη αλλαγών", - "discardChanges": "Απορρίψετε τις αλλαγές", - "changedPublished": "Αλλαγές που δημοσιεύθηκε με επιτυχία", - "changesDiscarded": "Αλλαγές απορρίφθηκε με επιτυχία", - "noChanges": "Χωρίς αλλαγές", - "noChangesPublished": "Δεν υπάρχουν αλλαγές να δημοσιεύσει", - "noChangesDiscarded": "Δεν υπάρχουν αλλαγές για να απορρίψετε" - }, "address": { "country": "Χώρα", "fullName": "Ονοματεπώνυμο", diff --git a/private/data/i18n/en.json b/private/data/i18n/en.json index 7e2dc283599..60848154620 100644 --- a/private/data/i18n/en.json +++ b/private/data/i18n/en.json @@ -526,20 +526,6 @@ "isCommercial": "This is a commercial address." } }, - "revisions": { - "isDisabled": "Revision control is disabled. Any changes will be published immediately.", - "isEnabled": "Revision control is enabled for products. Any changes will be need to be published before being visible to customers.", - "unpublishedChanges": "Unpublished changes", - "publishChanges": "Publish changes", - "showChanges": "Show changes", - "hideChanges": "Hide changes", - "discardChanges": "Discard changes", - "changedPublished": "Changes published successfully", - "changesDiscarded": "Changes discarded successfully", - "noChanges": "No changes", - "noChangesPublished": "There are no changes to publish", - "noChangesDiscarded": "There are no changes to discard" - }, "address": { "country": "Country", "fullName": "Full name", diff --git a/private/data/i18n/es.json b/private/data/i18n/es.json index 4e2812e6f77..20a05158a49 100644 --- a/private/data/i18n/es.json +++ b/private/data/i18n/es.json @@ -513,20 +513,6 @@ "isCommercial": "Esta es una dirección comercial" } }, - "revisions": { - "isDisabled": "El control de revisión está deshabilitado Todos los cambios se publicarán de imediato.", - "isEnabled": "Revisión de control está habilitada para los productos. Se necesitan cualquier cambio que se publicará antes de ser visibles para los clientes.", - "unpublishedChanges": "Cambios sin publicar", - "publishChanges": "Publicar cambios", - "showChanges": "Mostrar cambios", - "hideChanges": "Ocultar cambios", - "discardChanges": "Descartar cambios", - "changedPublished": "Cambios publicados correctamente", - "changesDiscarded": "Los cambios se han descartado", - "noChanges": "Sin cambios", - "noChangesPublished": "No hay cambios para publicar", - "noChangesDiscarded": "No hay cambios que descartar" - }, "address": { "country": "País", "fullName": "Nombre completo", diff --git a/private/data/i18n/fr.json b/private/data/i18n/fr.json index 8a871906296..2810b3c178a 100644 --- a/private/data/i18n/fr.json +++ b/private/data/i18n/fr.json @@ -513,20 +513,6 @@ "isCommercial": "Il s'agit d'une adresse commerciale." } }, - "revisions": { - "isDisabled": "La gestion de versions est désactivée. Toute modification sera immédiatement publiée.", - "isEnabled": "La gestion de versions est activée pour les produits. Toute modification devra être publiée avant d'être visible pour les clients.", - "unpublishedChanges": "Modifications non publiées", - "publishChanges": "Publier les modifications", - "showChanges": "Afficher les modifications", - "hideChanges": "Masquer les modifications", - "discardChanges": "Annuler les modifications", - "changedPublished": "Les modifications ont été publiées avec succès", - "changesDiscarded": "Modifications annulées avec succès", - "noChanges": "Aucune modification", - "noChangesPublished": "Aucune modification à publier", - "noChangesDiscarded": "Aucune modification à annuler" - }, "address": { "country": "Pays", "fullName": "Nom complet", diff --git a/private/data/i18n/he.json b/private/data/i18n/he.json index ff4fec442f1..6618b3fc71c 100644 --- a/private/data/i18n/he.json +++ b/private/data/i18n/he.json @@ -444,20 +444,6 @@ "isCommercial": "האם זו כתובת מסחרית?" } }, - "revisions": { - "isDisabled": "ניהול הגרסאות מושבת. כל השינויים יפורסמו באופן מידי.", - "isEnabled": "ניהול גרסאות מופעל עבור המוצרים. יש לפרסם את כל השינויים לפני שיהפכו להיות גלויים עבור הלקוחות.", - "unpublishedChanges": "שינויים שלא פורסמו", - "publishChanges": "פרסם שינויים", - "showChanges": "הצג שינויים", - "hideChanges": "הסתר שינויים", - "discardChanges": "בטל שינויים", - "changedPublished": "השינויים פורסמו בהצלחה", - "changesDiscarded": "השינויים בוטלו בהצלחה", - "noChanges": "אין שינויים", - "noChangesPublished": "אין שינויים לפרסום", - "noChangesDiscarded": "אין שינויים הניתנים לביטול" - }, "address": { "country": "ארץ", "fullName": "שם פרטי ומשפחה", diff --git a/private/data/i18n/hr.json b/private/data/i18n/hr.json index 43e29a01740..6a3d3d7c810 100644 --- a/private/data/i18n/hr.json +++ b/private/data/i18n/hr.json @@ -513,20 +513,6 @@ "isCommercial": "Ovo je adresa tvrtke." } }, - "revisions": { - "isDisabled": "kontrola Revizija je onemogućen. Sve promjene bit će odmah objavljena.", - "isEnabled": "kontrola Revizija je omogućena za proizvode. Sve promjene bit će morati biti objavljena prije nego što je vidljivo na kupce.", - "unpublishedChanges": "Neobjavljena Promjene", - "publishChanges": "Objavi promjene", - "showChanges": "Prikaži promjene", - "hideChanges": "Sakrij promjene", - "discardChanges": "Odbaciti promjene", - "changedPublished": "Promjene objavljeno uspješno", - "changesDiscarded": "Promjene uspješno odbačena", - "noChanges": "Nema promjena", - "noChangesPublished": "Nema promjena za objavu", - "noChangesDiscarded": "Nema promjena za odbacivanje" - }, "address": { "country": "Država", "fullName": "Puno ime", diff --git a/private/data/i18n/hu.json b/private/data/i18n/hu.json index c33995fadea..20a5be666e5 100644 --- a/private/data/i18n/hu.json +++ b/private/data/i18n/hu.json @@ -513,20 +513,6 @@ "isCommercial": "Céges cím." } }, - "revisions": { - "isDisabled": "Revision szabályozás ki van kapcsolva. A módosításokat haladéktalanul közzé kell tenni.", - "isEnabled": "Revision szabályozás engedélyezve a termékek. A módosításokat közzé kell tenni, mielőtt láthatóvá az ügyfelek számára.", - "unpublishedChanges": "közzétett módosítások", - "publishChanges": "Módosítások közzététele", - "showChanges": "Változások megjelenítése", - "hideChanges": "Változások elrejtése", - "discardChanges": "Módosítások elvetése", - "changedPublished": "Változások közzététele sikeresen", - "changesDiscarded": "Változások sikeresen elvetve", - "noChanges": "Nincs változás", - "noChangesPublished": "Nincs változás közzétételére", - "noChangesDiscarded": "Nincs változás megválni" - }, "address": { "country": "Ország", "fullName": "Teljes név", diff --git a/private/data/i18n/it.json b/private/data/i18n/it.json index 4c3ec615128..a4ff374dcec 100644 --- a/private/data/i18n/it.json +++ b/private/data/i18n/it.json @@ -513,20 +513,6 @@ "isCommercial": "Questo è un indirizzo commerciale." } }, - "revisions": { - "isDisabled": "controllo di revisione è disabilitato. Eventuali modifiche saranno pubblicate immediatamente.", - "isEnabled": "controllo di revisione è abilitato per i prodotti. saranno bisogno di alcuna modifica per essere pubblicato prima di essere visibile ai clienti.", - "unpublishedChanges": "Modifiche non pubblicati", - "publishChanges": "Pubblica modifiche", - "showChanges": "Mostra modifiche", - "hideChanges": "Nascondi le modifiche", - "discardChanges": "Non salvare le modifiche", - "changedPublished": "Modifiche pubblicato con successo", - "changesDiscarded": "Modifiche scartato con successo", - "noChanges": "Nessun cambiamento", - "noChangesPublished": "Non ci sono modifiche da pubblicare", - "noChangesDiscarded": "Non ci sono modifiche da scartare" - }, "address": { "country": "Nazione", "fullName": "Nome e cognome", diff --git a/private/data/i18n/my.json b/private/data/i18n/my.json index 436585cfb6a..d65f08776f6 100644 --- a/private/data/i18n/my.json +++ b/private/data/i18n/my.json @@ -513,20 +513,6 @@ "isCommercial": "ဒါကစီးပွားဖြစ်လိပ်စာဖြစ်ပါတယ်။" } }, - "revisions": { - "isDisabled": "တည်းဖြတ်မူထိန်းချုပ်မှုကိုပိတ်ထားသည်။ မဆိုအပြောင်းအလဲများချက်ချင်းပုံနှိပ်ထုတ်ဝေပါလိမ့်မည်။", - "isEnabled": "တည်းဖြတ်မူထိန်းချုပ်မှုထုတ်ကုန်များအတွက် enabled ဖြစ်ပါတယ်။ မဆိုစသောအပြောင်းအလဲဖောက်သည်မြင်နိုင်ဖြစ်ခြင်းမီကထုတ်ဝေသောခံရဖို့လိုအပ်ပါလိမ့်မည်။", - "unpublishedChanges": "အတည်မပြုရသေးသောအပြောင်းအလဲများ", - "publishChanges": "အပြောင်းအလဲများ Publish", - "showChanges": "အပြောင်းအလဲများ show ကို", - "hideChanges": "အပြောင်းအလဲများဝှက်ရန်", - "discardChanges": "စွန့်ပစ်အပြောင်းအလဲများ", - "changedPublished": "အောင်မြင်စွာပုံနှိပ်ထုတ်ဝေအပြောင်းအလဲများ", - "changesDiscarded": "အောင်မြင်စွာလွှင့်ပစ်အပြောင်းအလဲများ", - "noChanges": "အဘယ်သူမျှမအပြောင်းအလဲများ", - "noChangesPublished": "ထုတ်ဝေဖို့မအပြောင်းအလဲများရှိပါသည်", - "noChangesDiscarded": "ဖယ်ရန်မအပြောင်းအလဲများရှိပါသည်" - }, "address": { "country": "ပြည်", "fullName": "နာမည်အပြည့်အစုံ", diff --git a/private/data/i18n/nl.json b/private/data/i18n/nl.json index 3fc2d72ea49..2acae4c84f7 100644 --- a/private/data/i18n/nl.json +++ b/private/data/i18n/nl.json @@ -513,20 +513,6 @@ "isCommercial": "Dit is een zakelijk adres." } }, - "revisions": { - "isDisabled": "Revisie controle is uitgeschakeld. Eventuele wijzigingen zullen onmiddellijk worden gepubliceerd.", - "isEnabled": "Revisie controle is ingeschakeld voor producten. Eventuele wijzigingen zullen nodig hebben voordat ze zichtbaar zijn voor klanten om te worden gepubliceerd.", - "unpublishedChanges": "ongepubliceerde Wijzigingen", - "publishChanges": "Wijzigingen publiceren", - "showChanges": "Wijzigingen weergeven", - "hideChanges": "verbergen Wijzigingen", - "discardChanges": "Veranderingen ongedaan maken", - "changedPublished": "Wijzigingen met succes gepubliceerd", - "changesDiscarded": "Wijzigingen met succes verwijderd", - "noChanges": "Geen veranderingen", - "noChangesPublished": "Er zijn geen wijzigingen te publiceren", - "noChangesDiscarded": "Er zijn geen wijzigingen te ontdoen" - }, "address": { "country": "Land", "fullName": "Volledige naam", diff --git a/private/data/i18n/pl.json b/private/data/i18n/pl.json index 56f99b88f14..05ca212569f 100644 --- a/private/data/i18n/pl.json +++ b/private/data/i18n/pl.json @@ -513,20 +513,6 @@ "isCommercial": "Adres firmowy." } }, - "revisions": { - "isDisabled": "kontrola wersji jest wyłączona. Wszelkie zmiany będą publikowane natychmiast.", - "isEnabled": "kontrola wersji jest włączona dla Produktów. Wszelkie zmiany muszą być opublikowane, zanim będą widoczne dla klientów.", - "unpublishedChanges": "Niepublikowane Zmiany", - "publishChanges": "Opublikuj zmiany", - "showChanges": "Pokaż zmiany", - "hideChanges": "Schowaj Zmiany", - "discardChanges": "Odrzucać zmiany", - "changedPublished": "Zmiany opublikowane powodzeniem", - "changesDiscarded": "Zmiany odrzucone powodzeniem", - "noChanges": "Bez zmian", - "noChangesPublished": "Brak zmiany publikuj", - "noChangesDiscarded": "Brak zmiany, aby odrzucić" - }, "address": { "country": "Kraj", "fullName": "Pełne imię i nazwisko", diff --git a/private/data/i18n/pt.json b/private/data/i18n/pt.json index 5f699af6586..d461be7156f 100644 --- a/private/data/i18n/pt.json +++ b/private/data/i18n/pt.json @@ -513,20 +513,6 @@ "isCommercial": "Trata-se de um endereço comercial." } }, - "revisions": { - "isDisabled": "O controlo da revisão está desativado. As alterações serão publicadas de imediato.", - "isEnabled": "controle de revisão está habilitado para os produtos. será preciso qualquer alteração a ser publicado antes de ser visível para os clientes.", - "unpublishedChanges": "Alterações não publicadas", - "publishChanges": "Publicar alterações", - "showChanges": "Mostrar alterações", - "hideChanges": "Ocultar alterações", - "discardChanges": "Eliminar alterações", - "changedPublished": "Alterações publicadas com sucesso", - "changesDiscarded": "Alterações eliminadas com sucesso", - "noChanges": "Sem alterações", - "noChangesPublished": "Não há alterações para publicar", - "noChangesDiscarded": "Não há alterações para eliminar" - }, "address": { "country": "País", "fullName": "Nome completo", diff --git a/private/data/i18n/ro.json b/private/data/i18n/ro.json index 4f2b610f977..e853fedc10d 100644 --- a/private/data/i18n/ro.json +++ b/private/data/i18n/ro.json @@ -513,20 +513,6 @@ "isCommercial": "Aceasta este o adresă comercială." } }, - "revisions": { - "isDisabled": "Opțiunea „Revision control” este dezactivată. Orice schimbare va fi imediat publicată.", - "isEnabled": "Controlul revizie este activat pentru produse. Toate modificările vor fi trebui să fie publicate înainte de a fi vizibile pentru clienți.", - "unpublishedChanges": "Schimbări nepublicate", - "publishChanges": "Publicare schimbări", - "showChanges": "Expunere schimbări", - "hideChanges": "Ascundere modificări", - "discardChanges": "Eliminare schimbări", - "changedPublished": "Schimbările au fost publicate cu succes.", - "changesDiscarded": "Schimbări eliminate cu succes", - "noChanges": "Nicio schimbare", - "noChangesPublished": "Nu există modificări pentru publicare", - "noChangesDiscarded": "Nu există nicio schimbare de eliminat" - }, "address": { "country": "Țară", "fullName": "Numele complet", diff --git a/private/data/i18n/ru.json b/private/data/i18n/ru.json index c0894cdb71a..8cf085c8b1d 100644 --- a/private/data/i18n/ru.json +++ b/private/data/i18n/ru.json @@ -513,20 +513,6 @@ "isCommercial": "Это юридический адрес" } }, - "revisions": { - "isDisabled": "Управление версиями отключено. Любые изменения будут немедленно опубликованы.", - "isEnabled": "Контроль версий включена для продуктов. Любые изменения будут должны быть опубликованы до быть видимым для клиентов.", - "unpublishedChanges": "Неопубликованные изменения", - "publishChanges": "Опубликовать изменения", - "showChanges": "Отобразить изменения", - "hideChanges": "Скрыть изменения", - "discardChanges": "Отменить изменения", - "changedPublished": "Изменения успешно опубликованы", - "changesDiscarded": "Изменения успешно отменены", - "noChanges": "Изменений нет", - "noChangesPublished": "Нет изменений для публикации", - "noChangesDiscarded": "Отсутствуют изменения для отмены" - }, "address": { "country": "Страна", "fullName": "Ф.И.О.", diff --git a/private/data/i18n/sl.json b/private/data/i18n/sl.json index a8f36ddafb4..24c746d1a7c 100644 --- a/private/data/i18n/sl.json +++ b/private/data/i18n/sl.json @@ -513,20 +513,6 @@ "isCommercial": "To je komercialna naslov." } }, - "revisions": { - "isDisabled": "Nadzor Revizija je onemogočeno. Vse spremembe bodo objavljene takoj.", - "isEnabled": "Nadzor Revizija je omogočen za izdelke. Vse spremembe bo treba objaviti, preden se vidno strankam.", - "unpublishedChanges": "neobjavljene spremembe", - "publishChanges": "objavi spremembe", - "showChanges": "prikaži spremembe", - "hideChanges": "Skrij spremembe", - "discardChanges": "Zavreči spremembe", - "changedPublished": "Spremembe so bile uspešno objavljeno", - "changesDiscarded": "Spremembe uspešno zavreči", - "noChanges": "ni spremembe", - "noChangesPublished": "Ni spremembe objavijo", - "noChangesDiscarded": "Ni sprememb za zavržke" - }, "address": { "country": "Država", "fullName": "Polno ime", diff --git a/private/data/i18n/sv.json b/private/data/i18n/sv.json index 412408846fa..5b2c4680ab9 100644 --- a/private/data/i18n/sv.json +++ b/private/data/i18n/sv.json @@ -513,20 +513,6 @@ "isCommercial": "Detta är en kommersiell adress." } }, - "revisions": { - "isDisabled": "Versionskontroll är inaktiverad. Eventuella ändringar kommer att publiceras omedelbart.", - "isEnabled": "Versionskontroll har aktiverats för produkter. Eventuella ändringar kommer att behöva publiceras innan de blir synliga för kunderna.", - "unpublishedChanges": "Avpublicera ändringar", - "publishChanges": "publicera ändringar", - "showChanges": "visa ändringar", - "hideChanges": "Göm ändringar", - "discardChanges": "Ignorera ändringar", - "changedPublished": "Ändringarna publicerades", - "changesDiscarded": "Ändringarna kastades bort", - "noChanges": "Inga förändringar", - "noChangesPublished": "Det finns inga ändringar att publicera", - "noChangesDiscarded": "Det finns inga ändringar att avbryta" - }, "address": { "country": "Land", "fullName": "Fullständigt namn", diff --git a/private/data/i18n/tr.json b/private/data/i18n/tr.json index b7e4bd67307..57ab200c640 100644 --- a/private/data/i18n/tr.json +++ b/private/data/i18n/tr.json @@ -513,20 +513,6 @@ "isCommercial": "Bu ticari bir adres." } }, - "revisions": { - "isDisabled": "Revizyon kontrolü devre dışı bırakılır. Herhangi bir değişiklik derhal yayınlanacaktır.", - "isEnabled": "Revizyon kontrol Ürünleri etkindir. Herhangi bir değişiklik müşterilere görünür olmak önce yayınlanmış gerekir edilecektir.", - "unpublishedChanges": "yayınlanmamış değişiklikler", - "publishChanges": "Değişiklikleri Yayınla", - "showChanges": "Değişiklikleri göster", - "hideChanges": "gizle değişiklikler", - "discardChanges": "Değişiklikleri gözardı et", - "changedPublished": "Değişiklikler başarıyla yayınlandı", - "changesDiscarded": "Değişiklikler başarıyla atılır", - "noChanges": "Değişiklik yok", - "noChangesPublished": "yayımlamak için herhangi bir değişiklik bulunmamaktadır", - "noChangesDiscarded": "atmak için herhangi bir değişiklik bulunmamaktadır" - }, "address": { "country": "Ülke", "fullName": "Ad Saoyad", diff --git a/private/data/i18n/vi.json b/private/data/i18n/vi.json index 0e30397153b..6f9b9cadd46 100644 --- a/private/data/i18n/vi.json +++ b/private/data/i18n/vi.json @@ -513,20 +513,6 @@ "isCommercial": "Đây là địa chỉ thương mại." } }, - "revisions": { - "isDisabled": "kiểm soát sửa đổi được vô hiệu hóa. Mọi thay đổi sẽ được công bố ngay lập tức.", - "isEnabled": "kiểm soát sửa đổi được kích hoạt cho sản phẩm. Mọi thay đổi sẽ cần phải được công bố trước khi được rõ cho khách hàng.", - "unpublishedChanges": "Thay đổi chưa được công bố", - "publishChanges": "Xuất bản thay đổi", - "showChanges": "Hiện Thay đổi", - "hideChanges": "Ẩn Thay đổi", - "discardChanges": "Loại bỏ những thay đổi", - "changedPublished": "Thay đổi đăng thành công", - "changesDiscarded": "Thay đổi loại bỏ thành công", - "noChanges": "Không thay đổi", - "noChangesPublished": "Không có thay đổi để xuất bản", - "noChangesDiscarded": "Không có thay đổi để loại bỏ" - }, "address": { "country": "Quốc gia", "fullName": "Họ và tên", diff --git a/private/data/i18n/zh.json b/private/data/i18n/zh.json index d6e80393b39..54f6e8e96df 100644 --- a/private/data/i18n/zh.json +++ b/private/data/i18n/zh.json @@ -513,20 +513,6 @@ "isCommercial": "这是商业地址。" } }, - "revisions": { - "isDisabled": "版本控制已禁用。将立即发布任何更改。", - "isEnabled": "版本控制产品启用。任何更改都将是需要看到客户之前公布。", - "unpublishedChanges": "未发布更改", - "publishChanges": "发布更改", - "showChanges": "显示更改", - "hideChanges": "隐藏更改", - "discardChanges": "放弃更改", - "changedPublished": "更改已成功发布", - "changesDiscarded": "成功放弃更改", - "noChanges": "无更改", - "noChangesPublished": "没有要发布的更改", - "noChangesDiscarded": "没有需要放弃的更改" - }, "address": { "country": "国家", "fullName": "姓名", diff --git a/server/imports/fixtures/products.js b/server/imports/fixtures/products.js index fe591df674d..f25c863b12f 100755 --- a/server/imports/fixtures/products.js +++ b/server/imports/fixtures/products.js @@ -3,7 +3,6 @@ import _ from "lodash"; import { Factory } from "meteor/dburles:factory"; import { Products, Tags } from "/lib/collections"; import { getShop } from "./shops"; -import { Hooks } from "/server/api"; /** * @method metaField @@ -99,14 +98,10 @@ export function productVariant(options = {}) { */ export function addProduct(options = {}) { const product = Factory.create("product", options); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", product); // top level variant const variant = Factory.create("variant", Object.assign({}, productVariant(options), { ancestors: [product._id] })); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", variant); - const variant2 = Factory.create("variant", Object.assign({}, productVariant(options), { ancestors: [product._id, variant._id] })); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", variant2); - const variant3 = Factory.create("variant", Object.assign({}, productVariant(options), { ancestors: [product._id, variant._id] })); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", variant3); + Factory.create("variant", Object.assign({}, productVariant(options), { ancestors: [product._id, variant._id] })); + Factory.create("variant", Object.assign({}, productVariant(options), { ancestors: [product._id, variant._id] })); return product; } @@ -119,10 +114,8 @@ export function addProduct(options = {}) { */ export function addProductSingleVariant() { const product = Factory.create("product"); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", product); // top level variant const variant = Factory.create("variant", Object.assign({}, productVariant(), { ancestors: [product._id] })); - Hooks.Events.run("afterInsertCatalogProductInsertRevision", variant); return { product, variant }; } diff --git a/server/publications/collections/product-publications.app-test.js b/server/publications/collections/product-publications.app-test.js index 8321a8021b4..3a6780e0130 100644 --- a/server/publications/collections/product-publications.app-test.js +++ b/server/publications/collections/product-publications.app-test.js @@ -9,7 +9,6 @@ import { Reaction } from "/server/api"; import * as Collections from "/lib/collections"; import Fixtures from "/server/imports/fixtures"; import { PublicationCollector } from "meteor/johanbrook:publication-collector"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; Fixtures(); @@ -21,7 +20,6 @@ describe("Publication", function () { Collections.Shops.remove({}); createActiveShop({ _id: shopId }); sandbox = sinon.sandbox.create(); - sandbox.stub(RevisionApi, "isRevisionControlEnabled", () => true); }); afterEach(function () { diff --git a/server/publications/collections/product.js b/server/publications/collections/product.js index a557151a706..c6c0c68bb11 100644 --- a/server/publications/collections/product.js +++ b/server/publications/collections/product.js @@ -1,8 +1,7 @@ import { Meteor } from "meteor/meteor"; import { check, Match } from "meteor/check"; -import { Products, Revisions, Shops } from "/lib/collections"; +import { Products, Shops } from "/lib/collections"; import { Logger, Reaction } from "/server/api"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; /** * product detail publication @@ -65,54 +64,6 @@ Meteor.publish("Product", function (productIdOrHandle, shopIdOrSlug) { selector.isVisible = { $in: [true, false, undefined] }; - - if (RevisionApi.isRevisionControlEnabled()) { - const handle = Revisions.find({ - "workflow.status": { - $nin: [ - "revision/published" - ] - }, - "$or": [ - { "documentData._id": _id }, - { "documentData.ancestors": _id } - ] - }).observe({ - added: (revision) => { - this.added("Revisions", revision._id, revision); - if (revision.documentType === "product") { - // Check merge box (session collection view), if product is already in cache. - // If yes, we send a `changed`, otherwise `added`. I'm assuming - // that this._documents.Products is somewhat equivalent to the - // merge box Meteor.server.sessions[sessionId].getCollectionView("Products").documents - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [revision] }); - } else { - this.added("Products", revision.documentId, { __revisions: [revision] }); - } - } - }, - changed: (revision) => { - this.changed("Revisions", revision._id, revision); - if (revision.documentType === "product") { - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [revision] }); - } - } - }, - removed: (revision) => { - this.removed("Revisions", revision._id); - if (revision.documentType === "product") { - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [] }); - } - } - } - }); - this.onStop(() => { - handle.stop(); - }); - } } return Products.find(selector); diff --git a/server/publications/collections/products.js b/server/publications/collections/products.js index 4a6f18fa538..db8662f20a9 100644 --- a/server/publications/collections/products.js +++ b/server/publications/collections/products.js @@ -4,9 +4,8 @@ import { Meteor } from "meteor/meteor"; import { Tracker } from "meteor/tracker"; import { check, Match } from "meteor/check"; import { registerSchema } from "@reactioncommerce/schemas"; -import { Products, Shops, Revisions, Catalog } from "/lib/collections"; +import { Products, Shops, Catalog } from "/lib/collections"; import { Reaction, Logger } from "/server/api"; -import { RevisionApi } from "/imports/plugins/core/revisions/lib/api/revisions"; // // define search filters as a schema so we can validate @@ -88,62 +87,6 @@ const catalogProductFiltersSchema = new SimpleSchema({ } }); -/** - * Broadens an existing selector to include all variants of the given top-level productIds - * Additionally considers the tags product filter, if given - * Can operate on the "Revisions" and the "Products" collection - * @memberof Helpers - * @param collectionName {String} - "Revisions" or "Products" - * @param selector {object} - the selector that should be extended - * @param productFilters { object } - the product filter (e.g. orginating from query parameters) - * @param productIds {String[]} - the top-level productIds we want to get the variants of. - */ -function extendSelectorWithVariants(collectionName, selector, productFilters, productIds) { - let prefix = ""; - - if (collectionName.toLowerCase() === "revisions") { - prefix = "documentData."; - } else if (collectionName.toLowerCase() !== "products") { - throw new Error(`Can't extend selector for collection ${collectionName}.`); - } - - // Remove hashtag filter from selector (hashtags are not applied to variants, we need to get variants) - const newSelector = _.omit(selector, ["hashtags", "ancestors"]); - if (productFilters && productFilters.tags) { - // Re-configure selector to pick either Variants of one of the top-level products, or the top-level products in the filter - _.extend(newSelector, { - $or: [{ - [`${prefix}ancestors`]: { - $in: productIds - } - }, { - $and: [{ - [`${prefix}hashtags`]: { - $in: productFilters.tags - } - }, { - [`${prefix}_id`]: { - $in: productIds - } - }] - }] - }); - } else { - _.extend(newSelector, { - $or: [{ - [`${prefix}ancestors`]: { - $in: productIds - } - }, { - [`${prefix}_id`]: { - $in: productIds - } - }] - }); - } - return newSelector; -} - function filterProducts(productFilters) { // if there are filter/params that don't match the schema // validate, catch except but return no results @@ -353,8 +296,6 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so } // We publish an admin version of this publication to admins of products who are in "Edit Mode" - // Authorized content curators for shops get special publication of the product - // with all relevant revisions all is one package // userAdminShopIds is a list of shopIds that the user has createProduct or owner access for if (editMode && userAdminShopIds && Array.isArray(userAdminShopIds) && userAdminShopIds.length > 0) { selector.isVisible = { @@ -363,70 +304,6 @@ Meteor.publish("Products", function (productScrollLimit = 24, productFilters, so selector.shopId = { $in: activeShopsIds }; - - // Get _ids of top-level products - const productIds = Products.find(selector, { - sort, - limit: productScrollLimit - }).map((product) => product._id); - - - const productSelectorWithVariants = extendSelectorWithVariants("Products", selector, productFilters, productIds); - - if (RevisionApi.isRevisionControlEnabled()) { - const revisionSelector = { - "workflow.status": { - $nin: [ - "revision/published" - ] - } - }; - const revisionSelectorWithVariants = extendSelectorWithVariants("Revisions", revisionSelector, productFilters, productIds); - const handle = Revisions.find(revisionSelectorWithVariants).observe({ - added: (revision) => { - this.added("Revisions", revision._id, revision); - if (revision.documentType === "product") { - // Check merge box (session collection view), if product is already in cache. - // If yes, we send a `changed`, otherwise `added`. I'm assuming - // that this._documents.Products is somewhat equivalent to - // the merge box Meteor.server.sessions[sessionId].getCollectionView("Products").documents - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [revision] }); - } else { - this.added("Products", revision.documentId, { __revisions: [revision] }); - } - } - }, - changed: (revision) => { - this.changed("Revisions", revision._id, revision); - if (revision.documentType === "product") { - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [revision] }); - } - } - }, - removed: (revision) => { - this.removed("Revisions", revision._id); - if (revision.documentType === "product") { - if (this._documents.Products && this._documents.Products[revision.documentId]) { - this.changed("Products", revision.documentId, { __revisions: [] }); - } - } - } - }); - - this.onStop(() => { - handle.stop(); - }); - - return Products.find(productSelectorWithVariants); - } - - // Revision control is disabled, but is admin - return Products.find(productSelectorWithVariants, { - sort, - limit: productScrollLimit - }); } // This is where the publication begins for non-admin users diff --git a/server/publications/collections/revisions.js b/server/publications/collections/revisions.js deleted file mode 100644 index 011afb94943..00000000000 --- a/server/publications/collections/revisions.js +++ /dev/null @@ -1,42 +0,0 @@ -import { Meteor } from "meteor/meteor"; -import { check, Match } from "meteor/check"; -import { Roles } from "meteor/alanning:roles"; -import { Revisions } from "/lib/collections"; -import { Reaction } from "/server/api"; - -/** - * accounts - */ - -Meteor.publish("Revisions", function (documentIds) { - check(documentIds, Match.OneOf(String, Array)); - - // we could additionally make checks of useId defined, but this could lead to - // situation when user will may not have time to get an account - if (this.userId === null) { - return this.ready(); - } - const shopId = Reaction.getShopId(); - if (!shopId) { - return this.ready(); - } - - if (Roles.userIsInRole(this.userId, ["admin", "owner"])) { - if (Array.isArray(documentIds)) { - return Revisions.find({ - // shopId, - documentId: { - $in: documentIds - } - }); - } - - // global admin can get all accounts - return Revisions.find({ - // shopId, - documentId: documentIds - }); - } - // regular users should get just their account - return this.ready(); -});