Skip to content
Keep revision history for Meteor collection documents and provide restore functionality.
Branch: master
Clone or download
Pull request Compare This branch is 10 commits ahead of nicklozon:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitignore
HISTORY.md
README.md
collectionRevisions.js
package.js
restoreRevision.js

README.md

Collection Revisions for Meteor

The main purpose of this package is to retain revisions of collection documents and allow for the restore of those revisions.

Note: This package has been decaffeinated which makes it suitable for Meteor 1.6.1, where coffeescript got a major update.

Features

  • Saves a revision of the entire document when updates are made to it.
  • Revisions are stored within a field of the document, so no extra publications or subscriptions are needed to get the revisions.
  • Can specify how many revisions to keep per document, or keep unlimited
  • Can specify to not create revisions for updates made within a certain amount of time since the last revision (helpful for autoform with autosave triggering multiple updates)
  • Prune foregoing revisions upon restore
  • Callback function that provides two parameters - the revision and modifier objects before the collection update. Returning false in the callback will cancel the creation of a revision.

Installation

meteor add simonsimcity:collection-revisions

Usage

After you define your collection with something like:

Collection = new Mongo.Collection("collection");

You can use CollectionRevisions a few different ways: (Foo is our collection)

Package Default options

Foo.attachCollectionRevisions();

This will use all defaults, either by your code in CollectionRevisions.defaults or the package defaults.

Define Global default options

CollectionRevisions.defaults.keep = 10;
CollectionRevisions.defaults.debug = true;
... define whatever options you want to override the package defaults

These will be used for all collections you attach the CollectionRevisions to.

Include any collection specific options

CollectionRevisions.Foo = {
  keep:10
  ignoreWithin: 60000
  ... define whatever options you want to override the global or package defaults
}

Foo.attachCollectionRevisions(CollectionRevisions.Foo);

Options

Option Default Description
field 'revisions' name of the field which will hold the document's revisions within itself String
lastModifiedField 'lastModified' Name of the field storing the date / time the document was last modified String
ignoreWithin 0 If an update occurs within this amount of milliseconds since the last update, a new revision will not be created. Number
keep true Specify a number if you wish to only retain a limited number of revisions per document. True = retain all revisions. Number or Boolean
prune false Will delete the restored revision and all subsequent revisions. Boolean
debug false Turn to true to get console debug messages.
callback undefined Allows custom code to be executed against the revision and modifier objects before updating the collection.

Restoring a Revision

A revision can be restored by calling CollectionRevisions.restore from either the client, server, or both. It will follow the same allow / deny permissions for an update, or use your own permissions and call CollectionRevisions.restore within a method call. If you want the simulation to run correctly on the client, the document needs to be published and the revisions field present.

CollectionRevisions.restore(collection, documentId, revision, callback);
Parameter Type Description
collection Object This is the object of your collection
documentId String the _id of the document you want to restore
revision revisionId or Object Simplest form is to provide the revisionId stored within the revision, if you want to use specific data to restore, you can provide the revision object, overriding any fields you want to update the document to.
callback Function Callback function that is executed when the update is completed. Takes two parameters: function(error, result). Refer to the node mongodb driver documentation.

Showing a list of Revisions

Example template code: (bootstrap)

*Inside of Document Context*
<ul class="list-group">
  <li class='list-group-item'>
    <div class='col-xs-12 col-sm-6'>
      {{moment lastModified 'dddd, MMMM Do YYYY, h:mm:ss a'}}
    </div>
    <div class='col-xs-12 col-sm-6'>
      <button type="button" class="btn btn-default btn-success btn-xs pull-right">
        Current Revision
      </button>
    </div>
    <div class='clearfix'></div>
  </li>
  {{#each revisions}}
    <li class='list-group-item'>
      <div class='col-xs-12 col-sm-6'>
        {{moment lastModified 'dddd, MMMM Do YYYY, h:mm:ss a'}}
      </div>
      <div class='col-xs-12 col-sm-6'>
        <button type="button" class="btn btn-default btn-primary btn-xs pull-right revertFoo">
          <i class="fa fa-undo"></i> Revert
        </button>
      </div>
      <div class='clearfix'></div>
    </li>
  {{/each}}
</ul>

This is also using a global helper I have for moment:

Template.registerHelper('moment', function(date, format) {
  date = moment(date);
  return date.format(format);
});

Example template event code where Foo is the collection:

Template.fooRevisions.events({
  'click .revertFoo'(e,t) {
    // get the foo document _id
    const foo = Template.parentData();

    // restore the revision
    CollectionRevisions.restore(Foo, foo._id, this.revisionId);
  }
}); 

Callbacks

Callback format is function(revision, modifier).

This can be used to make changes to the revision before it is saved. Example:

CollectionRevisions.Employee = {
  callback: function(revision, modifier) {
    // This allows a custom field be inserted into the revision
    revision.dateTermination = modifier.$set.dateEffective;
  }
}

This can also be used to allow custom logic that negates creating a revision by returning false:

CollectionRevisions.Employee = {
  callback: function(revision, modifier) {
    // Do no create a revision if the effective date did not change 
    if(revision.dateEffective.getTime() === modifier.$set.dateEffective.getTime())
        return false;
  }
}

Updates Using multi=true

This package will not create revisions for Documents when an update is for multiple documents (multi=true)

You can’t perform that action at this time.