Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revision control for images #1555

Merged
merged 26 commits into from
Nov 10, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
746356f
Add media hook. Rename file to "hooks'"
brent-hoover Oct 25, 2016
537791c
Only return published images for non-admin users
brent-hoover Oct 25, 2016
0875ca4
Merge branch 'development' into brent-fix-issue-1456
brent-hoover Oct 26, 2016
25cfd6d
Only set product images as unpublished
brent-hoover Oct 26, 2016
a98db1d
Capture revision record and show Publish button when image changes ar…
brent-hoover Oct 26, 2016
ee26da7
Adding images deferred until published
brent-hoover Oct 26, 2016
bf6a808
Merge branch 'release-0.18.0' into brent-fix-issue-1456
brent-hoover Oct 26, 2016
0f97295
Removing images deferred until published
brent-hoover Oct 27, 2016
86dc4d2
Capturing and deferring other changes to metadata (priority really)
brent-hoover Oct 28, 2016
ba67b6f
Combine revisions when making multiple changes before publishing
brent-hoover Oct 28, 2016
37e3d52
Properly handle non-product revisions in applyProductRevision so that…
brent-hoover Oct 31, 2016
8f322de
Find the product correctly based on revision type
brent-hoover Oct 31, 2016
0703e5b
Fix regression where images weren't being published because revision …
brent-hoover Oct 31, 2016
357ff12
Setting priceRange should be in an `else` statement
brent-hoover Oct 31, 2016
b77af92
If you add an image and then delete it before publishing it, just rem…
brent-hoover Nov 1, 2016
1dad9b7
Update revisions with media
brent-hoover Nov 2, 2016
f0d11c8
Show visual indicator on image if there are pending revisions
brent-hoover Nov 2, 2016
13aa8b9
Discard drafts of image revisions
brent-hoover Nov 3, 2016
35ea5fd
Sometimes rendering the "edited" marker
brent-hoover Nov 3, 2016
7d919ef
Reset revisions so that removals take effect
brent-hoover Nov 3, 2016
6f4cc56
Set priority to revision priority
brent-hoover Nov 3, 2016
cd1d059
Fix issue where changing order wouldn't stick
brent-hoover Nov 4, 2016
61f6883
Remove logging
brent-hoover Nov 7, 2016
4fa49a3
Merge branch 'release-0.18.0' into brent-fix-issue-1456
brent-hoover Nov 7, 2016
0299448
Change lost in merge
brent-hoover Nov 7, 2016
013627b
Another change from merge
brent-hoover Nov 7, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ class PublishControls extends Component {
get hasChanges() {
// Verify we even have any revision at all
if (this.hasRevisions) {
// Loop through all revisions to determin if they have changes
// Loop through all revisions to determine if they have changes
const diffHasActualChanges = this.props.revisions.map((revision) => {
// We probably do have chnages to publish
// Note: Sometimes "updatedAt" will cause false positives, but just incase, lets
// enable the publish button anyway.
if (Array.isArray(revision.diff) && revision.diff.length) {
if (Array.isArray(revision.diff) && revision.diff.length || revision.documentType !== "product") {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ import { i18next } from "/client/api";
class PublishContainer extends Component {
handlePublishClick = (revisions) => {
if (Array.isArray(revisions)) {
const documentIds = revisions.map((revision) => {
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 === true) {
const message = i18next.t("revisions.changedPublished", {
Expand Down Expand Up @@ -105,6 +110,11 @@ function composer(props, onData) {
"documentData.ancestors": {
$in: props.documentIds
}
},
{
parentDocument: {
$in: props.documentIds
}
}
],
"workflow.status": {
Expand All @@ -113,7 +123,6 @@ function composer(props, onData) {
]
}
}).fetch();

onData(null, {
isEnabled: isRevisionControlEnabled(),
documentIds: props.documentIds,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,127 @@
import { Products, Revisions, Tags } from "/lib/collections";
import { Logger } from "/server/api";
import _ from "lodash";
import { diff } from "deep-diff";
import { Products, Revisions, Tags, Media } from "/lib/collections";
import { Logger } from "/server/api";
import { RevisionApi } from "../lib/api";


function convertMetadata(modifierObject) {
const metadata = {};
for (const prop in modifierObject) {
if (modifierObject.hasOwnProperty(prop)) {
if (prop.indexOf("metadata") !== -1) {
const splitName = _.split(prop, ".")[1];
metadata[splitName] = modifierObject[prop];
}
}
}
return metadata;
}

Media.files.before.insert((userid, media) => {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
}
if (media.metadata.productId) {
const revisionMetadata = Object.assign({}, media.metadata);
revisionMetadata.workflow = "published";
Revisions.insert({
documentId: media._id,
documentData: revisionMetadata,
documentType: "image",
parentDocument: media.metadata.productId,
changeType: "insert",
workflow: {
status: "revision/update"
}
});
media.metadata.workflow = "unpublished";
} else {
media.metadata.workflow = "published";
}
return true;
});

Media.files.before.update((userId, media, fieldNames, modifier) => {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
}
// if it's not metadata ignore it, as LOTS of othing things change on this record
if (!_.includes(fieldNames, "metadata")) {
return true;
}

if (media.metadata.productId) {
const convertedModifier = convertMetadata(modifier.$set);
const convertedMetadata = Object.assign({}, media.metadata, convertedModifier);
const existingRevision = Revisions.findOne({
"documentId": media._id,
"workflow.status": {
$nin: [
"revision/published"
]
}
});
if (existingRevision) {
const updatedMetadata = Object.assign({}, existingRevision.documentData, convertedMetadata);
// 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
}
});
} else {
Revisions.insert({
documentId: media._id,
documentData: convertedMetadata,
documentType: "image",
parentDocument: media.metadata.productId,
changeType: "update",
workflow: {
status: "revision/update"
}
});
}

return false; // prevent actual update of image. This also stops other hooks from running :/
}
// for non-product images, just ignore and keep on moving
return true;
});

Media.files.before.remove((userId, media) => {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
}

// if the media is unpublished, then go ahead and just delete it
if (media.metadata.workflow && media.metadata.workflow === "unpublished") {
Revisions.remove({
documentId: media._id
});
return true;
}
if (media.metadata.productId) {
Revisions.insert({
documentId: media._id,
documentData: media.metadata,
documentType: "image",
parentDocument: media.metadata.productId,
changeType: "remove",
workflow: {
status: "revision/update"
}
});
return false; // prevent actual deletion of image. This also stops other hooks from running :/
}
return true;
});


Products.before.insert((userId, product) => {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
Expand Down Expand Up @@ -243,13 +362,21 @@ Revisions.after.update(function (userId, revision) {
if (RevisionApi.isRevisionControlEnabled() === false) {
return true;
}
let differences;

// Make diff
const product = Products.findOne({
_id: revision.documentId
});

const differences = diff(product, revision.documentData);
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 = Media.findOne(revision.documentId);
differences = diff(image.metadata, revision.documentData);
}

Revisions.direct.update({
_id: revision._id
Expand Down
2 changes: 1 addition & 1 deletion imports/plugins/core/revisions/server/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import "./startup";
import "./hooks";
import "./methods";
70 changes: 60 additions & 10 deletions imports/plugins/core/revisions/server/methods.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Products, Revisions, Packages } from "/lib/collections";
import { Meteor } from "meteor/meteor";
import { check, Match } from "meteor/check";
import { Products, Media, Revisions, Packages } from "/lib/collections";
import { Logger } from "/server/api";

export function updateSettings(settings) {
check(settings, Object);
Expand Down Expand Up @@ -41,6 +42,11 @@ export function discardDrafts(documentIds) {
"documentData.ancestors": {
$in: documentIdArray
}
},
{
parentDocument: {
$in: documentIds
}
}
]
};
Expand Down Expand Up @@ -76,6 +82,11 @@ Meteor.methods({
"documentData.ancestors": {
$in: documentIds
}
},
{
parentDocument: {
$in: documentIds
}
}
]
}).fetch();
Expand All @@ -101,15 +112,54 @@ Meteor.methods({

if (revisions) {
for (const revision of revisions) {
const res = Products.update({
_id: revision.documentId
}, {
$set: revision.documentData
}, {
publish: true
});

updatedDocuments += res;
if (!revision.documentType || revision.documentType === "product") {
const res = Products.update({
_id: revision.documentId
}, {
$set: revision.documentData
}, {
publish: true
});
updatedDocuments += res;
} else if (revision.documentType === "image") {
if (revision.changeType === "insert") {
const res = Media.files.direct.update({
_id: revision.documentId
}, {
$set: {
metadata: revision.documentData
}
});
updatedDocuments += res;
} else if (revision.changeType === "remove") {
const res = Media.files.direct.update({
_id: revision.documentId
}, {
$set: {
"metadata.workflow": "archived"
}
});
updatedDocuments += res;
} else if (revision.changeType === "update") {
const res = Media.files.direct.update({
_id: revision.documentId
}, {
$set: {
metadata: revision.documentData
}
});
updatedDocuments += res;
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.direct.update({
_id: revision._id
}, {
$set: {
"workflow.status": "revision/published"
}
});
}
}
}

Expand Down
16 changes: 16 additions & 0 deletions imports/plugins/core/ui/client/components/media/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ class MediaItem extends Component {
}
}

renderRevision() {
if (this.props.revision) {
if (this.props.revision.changeType === "remove") {
return (
<IconButton icon="fa fa-pencil-remove" />
);
}
return (
<IconButton icon="fa fa-pencil-square-o" />
);
}
}

renderControls() {
if (this.props.editable) {
return (
Expand All @@ -33,6 +46,7 @@ class MediaItem extends Component {
icon="fa fa-times"
onClick={this.handleRemoveMedia}
/>
{this.renderRevision()}
</div>
);
}
Expand Down Expand Up @@ -93,9 +107,11 @@ MediaItem.propTypes = {
connectDropTarget: PropTypes.func,
defaultSource: PropTypes.string,
editable: PropTypes.bool,
metadata: PropTypes.object,
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onRemoveMedia: PropTypes.func,
revision: PropTypes.object,
source: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class MediaGallery extends Component {
editable={this.props.editable}
index={index}
key={index}
revision={this.featuredMedia.revision}
metadata={this.featuredMedia.metadata}
onMouseEnter={this.props.onMouseEnterMedia}
onMouseLeave={this.props.onMouseLeaveMedia}
Expand All @@ -66,6 +67,7 @@ class MediaGallery extends Component {
editable={this.props.editable}
index={index}
key={index}
revision={media.revision}
metadata={media.metadata}
onMouseEnter={this.props.onMouseEnterMedia}
onMouseLeave={this.props.onMouseLeaveMedia}
Expand Down
Loading