mirror of
https://github.com/documize/community.git
synced 2025-07-24 23:59:47 +02:00
revision history UI
This commit is contained in:
parent
5c8db2a873
commit
0d9400965d
10 changed files with 106 additions and 268 deletions
|
@ -12,7 +12,7 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
revision: {},
|
revision: null,
|
||||||
hasDiff: Ember.computed('diff', function () {
|
hasDiff: Ember.computed('diff', function () {
|
||||||
return this.get('diff').length > 0;
|
return this.get('diff').length > 0;
|
||||||
}),
|
}),
|
||||||
|
@ -20,38 +20,28 @@ export default Ember.Component.extend({
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
let revisions = this.get('revisions');
|
let revisions = this.get('revisions');
|
||||||
|
|
||||||
revisions.forEach((revision) => {
|
revisions.forEach((r) => {
|
||||||
Ember.set(revision, 'deleted', revision.revisions === 0);
|
Ember.set(r, 'deleted', r.revisions === 0);
|
||||||
|
Ember.set(r, 'label', `${r.created} - ${r.firstname} ${r.lastname} - ${r.title}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (revisions.length > 0 && is.null(this.get('revision'))) {
|
||||||
|
this.send('onSelectRevision', revisions[0]);
|
||||||
|
}
|
||||||
|
|
||||||
this.set('revisions', revisions);
|
this.set('revisions', revisions);
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
this.eventBus.subscribe('resized', this, 'sizeSidebar');
|
|
||||||
this.sizeSidebar();
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this.eventBus.unsubscribe('resized');
|
|
||||||
},
|
|
||||||
|
|
||||||
sizeSidebar() {
|
|
||||||
let size = $(window).height() - 200;
|
|
||||||
this.$('.document-history > .sidebar').css('height', size + "px");
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
getDiff(revision) {
|
onSelectRevision(revision) {
|
||||||
this.set('revision', revision);
|
this.set('revision', revision);
|
||||||
|
|
||||||
if (!revision.deleted) {
|
if (!revision.deleted) {
|
||||||
this.attrs.onFetchDiff(revision.pageId, revision.id);
|
this.attrs.onFetchDiff(revision.pageId, revision.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
rollback() {
|
onRollback() {
|
||||||
let revision = this.get('revision');
|
let revision = this.get('revision');
|
||||||
this.attrs.onRollback(revision.pageId, revision.id);
|
this.attrs.onRollback(revision.pageId, revision.id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
|
||||||
//
|
|
||||||
// This software (Documize Community Edition) is licensed under
|
|
||||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
|
||||||
//
|
|
||||||
// You can operate outside the AGPL restrictions by purchasing
|
|
||||||
// Documize Enterprise Edition and obtaining a commercial license
|
|
||||||
// by contacting <sales@documize.com>.
|
|
||||||
//
|
|
||||||
// https://documize.com
|
|
||||||
|
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
documentService: Ember.inject.service('document'),
|
|
||||||
|
|
||||||
revisions: [],
|
|
||||||
diffReport: "",
|
|
||||||
busy: false,
|
|
||||||
currentRevisionId: "",
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
if (is.undefined(this.get('model'))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
this.get('documentService').getPageRevisions(this.get('model.documentId'), this.get('model.pageId')).then(function(response) {
|
|
||||||
if (is.array(response)) {
|
|
||||||
self.set('revisions', response);
|
|
||||||
if (response.length > 0) {
|
|
||||||
self.send('produceReport', response[0].id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
didRender() {
|
|
||||||
let self = this;
|
|
||||||
Ember.run.schedule('afterRender', function() {
|
|
||||||
Mousetrap.bind('esc', function() {
|
|
||||||
self.send('cancelAction');
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
produceReport(revisionId) {
|
|
||||||
this.set('busy', true);
|
|
||||||
this.set('diffReport', "");
|
|
||||||
this.set('currentRevisionId', revisionId);
|
|
||||||
|
|
||||||
// visually mark active revision
|
|
||||||
let revisions = this.get('revisions');
|
|
||||||
|
|
||||||
revisions.forEach(function(revision) {
|
|
||||||
Ember.set(revision, 'selected', false);
|
|
||||||
});
|
|
||||||
|
|
||||||
let revision = _.findWhere(revisions, {
|
|
||||||
id: revisionId
|
|
||||||
});
|
|
||||||
Ember.set(revision, 'selected', true);
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
this.get('documentService').getPageRevisionDiff(this.get('model.documentId'),
|
|
||||||
this.get('model.pageId'), revisionId).then(function(response) {
|
|
||||||
self.set('busy', false);
|
|
||||||
self.set('diffReport', Ember.String.htmlSafe(response));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelAction() {
|
|
||||||
this.attrs.editorClose();
|
|
||||||
},
|
|
||||||
|
|
||||||
primaryAction() {
|
|
||||||
if (this.session.isEditor) {
|
|
||||||
this.attrs.editorAction(this.get('model.pageId'), this.get('currentRevisionId'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -51,18 +51,6 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, {
|
||||||
this.set('pinState.newName', this.get('document.name').substring(0,3).toUpperCase());
|
this.set('pinState.newName', this.get('document.name').substring(0,3).toUpperCase());
|
||||||
},
|
},
|
||||||
|
|
||||||
didRender() {
|
|
||||||
this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onChangeTab(tab) {
|
onChangeTab(tab) {
|
||||||
this.set('tab', tab);
|
this.set('tab', tab);
|
||||||
|
|
|
@ -1,2 +1,21 @@
|
||||||
{{document/document-history document=model.document folder=model.folder pages=model.pages revisions=model.revisions diff=model.diff
|
<div class="zone-document-history">
|
||||||
onFetchDiff=(action 'onFetchDiff') onRollback=(action 'onRollback')}}
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
|
||||||
|
<div id="zone-document-content" class="zone-document-content">
|
||||||
|
<div class="back-to-space">
|
||||||
|
{{#link-to 'document.index' model.folder.id model.folder.slug model.document.id model.document.slug}}
|
||||||
|
<i class="material-icons">arrow_back</i> {{model.document.name}}
|
||||||
|
{{/link-to}}
|
||||||
|
</div>
|
||||||
|
{{document/document-heading document=model.document isEditor=false}}
|
||||||
|
{{document/document-history document=model.document folder=model.folder pages=model.pages
|
||||||
|
revisions=model.revisions diff=model.diff onFetchDiff=(action 'onFetchDiff') onRollback=(action 'onRollback')}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,9 @@ export default Router.map(function () {
|
||||||
this.route('block', {
|
this.route('block', {
|
||||||
path: 'block/:block_id'
|
path: 'block/:block_id'
|
||||||
});
|
});
|
||||||
|
this.route('history', {
|
||||||
|
path: 'history'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route('customize', {
|
this.route('customize', {
|
||||||
|
|
|
@ -1,72 +1,26 @@
|
||||||
.document-history {
|
.zone-document-history {
|
||||||
position: relative;
|
margin-left: 60px;
|
||||||
|
padding: 20px 60px;
|
||||||
|
z-index: 777;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
> .sidebar {
|
.diff-zone {
|
||||||
|
@extend .transition-all;
|
||||||
|
@include border-radius(2px);
|
||||||
|
@include ease-in();
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-y: scroll;
|
padding: 25px 50px;
|
||||||
|
box-shadow: 0 0 0 0.75pt $color-stroke,0 0 3pt 0.75pt $color-stroke;
|
||||||
|
background-color: $color-white;
|
||||||
|
|
||||||
> .heading {
|
> .canvas {
|
||||||
font-size: 1.2rem;
|
padding: 0;
|
||||||
font-family: $font-light;
|
|
||||||
color: $color-off-black;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .line {
|
.revision-picker {
|
||||||
width: 2px;
|
width: 300px;
|
||||||
background-color: $color-stroke;
|
float: left;
|
||||||
height: 100%;
|
margin-bottom: 30px;
|
||||||
position: absolute;
|
|
||||||
left: 17px;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .items {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px 0 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
> .item {
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
color: $color-off-black;
|
|
||||||
@include ease-in();
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
> .avatar-box {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .date {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-family: $font-light;
|
|
||||||
margin-left: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> .detail {
|
|
||||||
display: block;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-family: $font-semibold;
|
|
||||||
margin-left: 65px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $color-link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
> .deleted {
|
|
||||||
color: $color-red;
|
|
||||||
cursor: not-allowed;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $color-red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
<div class="container width-100">
|
<div class="diff-zone">
|
||||||
<div class="row">
|
{{ui-select tagName="div" class="revision-picker" content=revisions action=(action 'onSelectRevision') optionValuePath="id" optionLabelPath="label"}}
|
||||||
|
{{#if hasDiff}}
|
||||||
<div class="col-xs-9 col-sm-9 col-md-9 col-lg-9">
|
<div id="restore-history-button" class="regular-button button-green pull-right">Restore</div>
|
||||||
{{#if hasDiff}}
|
{{#dropdown-dialog target="restore-history-button" position="bottom right" button="Restore" color="flat-green" onAction=(action 'onRollback')}}
|
||||||
<div id="restore-history-button" class="regular-button button-green pull-right margin-right-40">
|
<p>Are you sure you want to roll back to this version?</p>
|
||||||
Restore
|
{{/dropdown-dialog}}
|
||||||
</div>
|
<div class="clearfix" />
|
||||||
{{#dropdown-dialog target="restore-history-button" position="bottom right" button="Restore" color="flat-green" onAction=(action 'rollback')}}
|
<div class="is-a-page wysiwyg">
|
||||||
<p>Are you sure you want to roll back to this version?</p>
|
{{{diff}}}
|
||||||
{{/dropdown-dialog}}
|
|
||||||
<div class="clearfix" />
|
|
||||||
<div class="doc-layout">
|
|
||||||
{{{diff}}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
<div class="col-xs-3 col-sm-3 col-md-3 col-lg-3">
|
<div class="clearfix" />
|
||||||
<div class="document-history">
|
|
||||||
<div class="sidebar">
|
|
||||||
<p class="heading">{{revisions.length}} changes</p>
|
|
||||||
<div class="line" />
|
|
||||||
<ul class="items">
|
|
||||||
{{#each revisions as |r|}}
|
|
||||||
<li class="item {{if r.deleted 'deleted'}}" {{action 'getDiff' r}}>
|
|
||||||
<div class="avatar-box">
|
|
||||||
{{ui/ui-avatar refId=r.id firstname=r.firstname lastname=r.lastname}}
|
|
||||||
</div>
|
|
||||||
<div class="date">{{time-ago r.created}}</div>
|
|
||||||
<div class="detail">{{r.title}}</div>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
<div class="editor-button-zone z-depth-1">
|
|
||||||
<div class="left">
|
|
||||||
<h2>Revision History</h2>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<button class="" {{ action 'cancelAction' }}>Cancel</button>
|
|
||||||
<button class="" {{ action 'primaryAction' }}>Revert</button>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="editor-editing-zone">
|
|
||||||
<div class="page-history">
|
|
||||||
<ul>
|
|
||||||
{{#each revisions key="id" as |revision index|}}
|
|
||||||
<li {{action 'produceReport' revision.id}} class="{{if revision.selected 'active'}}">
|
|
||||||
<div class="avatar-large" title="{{revision.firstname}} {{revision.lastname}}">{{revision.initials}}</div>
|
|
||||||
<div class="who">
|
|
||||||
{{revision.firstname}} {{revision.lastname}}
|
|
||||||
</div>
|
|
||||||
<div class="time-ago" title="{{revision.created}}">
|
|
||||||
{{time-ago revision.created}}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
<div class="clearfix"></div>
|
|
||||||
<div class="diff-report">
|
|
||||||
<div class="legend">
|
|
||||||
<div class="add-key">Added</div>
|
|
||||||
<div class="removed-key">Removed</div>
|
|
||||||
<div class="changed-key">Formatted</div>
|
|
||||||
</div>
|
|
||||||
{{#if busy}}
|
|
||||||
<div class="processing">
|
|
||||||
<img src="/assets/img/processing.gif" />
|
|
||||||
<div class="caption">Preparing</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{diffReport}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -15,6 +15,18 @@
|
||||||
<div class="round-button-mono {{if (is-equal tab 'activity') 'selected'}}" {{action 'onChangeTab' 'activity'}}>
|
<div class="round-button-mono {{if (is-equal tab 'activity') 'selected'}}" {{action 'onChangeTab' 'activity'}}>
|
||||||
<i class="material-icons">timeline</i>
|
<i class="material-icons">timeline</i>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="margin-top-20"></div>
|
||||||
|
<div class="round-button-mono {{if (is-equal tab 'feedback') 'feedback'}}" {{action 'onChangeTab' 'feedback'}}>
|
||||||
|
<i class="material-icons">chat_bubble</i>
|
||||||
|
</div>
|
||||||
|
<div class="margin-top-20"></div>
|
||||||
|
<div class="round-button-mono {{if (is-equal tab 'actions') 'selected'}}" {{action 'onChangeTab' 'actions'}}>
|
||||||
|
<i class="material-icons">person</i>
|
||||||
|
</div>
|
||||||
|
<div class="margin-top-20"></div>
|
||||||
|
<div class="round-button-mono {{if (is-equal tab 'share') 'selected'}}" {{action 'onChangeTab' 'share'}}>
|
||||||
|
<i class="material-icons">share</i>
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -38,6 +50,18 @@
|
||||||
{{#if (is-equal tab 'activity')}}
|
{{#if (is-equal tab 'activity')}}
|
||||||
{{document/sidebar-view-activity document=document pages=pages isEditor=isEditor}}
|
{{document/sidebar-view-activity document=document pages=pages isEditor=isEditor}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (is-equal tab 'feedback')}}
|
||||||
|
{{enterprise/sidebar-view-feedback document=document isEditor=isEditor}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (is-equal tab 'actions')}}
|
||||||
|
{{enterprise/sidebar-view-actions document=document folder=folder isEditor=isEditor}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if (is-equal tab 'share')}}
|
||||||
|
{{enterprise/sidebar-view-share document=document isEditor=isEditor}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#dropdown-menu target="sidebar-zone-more-button" position="bottom right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
|
{{#dropdown-menu target="sidebar-zone-more-button" position="bottom right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
|
||||||
|
@ -48,13 +72,20 @@
|
||||||
{{else}}
|
{{else}}
|
||||||
<li class="item" id="pin-document-button">Pin</li>
|
<li class="item" id="pin-document-button">Pin</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<li class="item" id="pin-document-button">
|
||||||
|
{{#link-to 'document.history'}}History{{/link-to}}
|
||||||
|
</li>
|
||||||
|
<li class="divider"></li>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if isEditor}}
|
||||||
|
<li class="item" id="save-template-button">Template</li>
|
||||||
|
<li class="divider"></li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<li class="item" id="print-document-button" {{action 'onPrintDocument'}}>Print</li>
|
<li class="item" id="print-document-button" {{action 'onPrintDocument'}}>Print</li>
|
||||||
|
|
||||||
{{#if isEditor}}
|
{{#if isEditor}}
|
||||||
<li class="divider"></li>
|
|
||||||
<li class="item" id="save-template-button">Template</li>
|
|
||||||
<li class="divider"></li>
|
<li class="divider"></li>
|
||||||
<li class="item danger" id="delete-document-button">Delete</li>
|
<li class="item danger" id="delete-document-button">Delete</li>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -790,6 +790,10 @@ func GetDocumentRevisions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
revisions, _ := p.GetDocumentRevisions(documentID)
|
revisions, _ := p.GetDocumentRevisions(documentID)
|
||||||
|
|
||||||
|
if len(revisions) == 0 {
|
||||||
|
revisions = []entity.Revision{}
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := json.Marshal(revisions)
|
payload, err := json.Marshal(revisions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -832,6 +836,10 @@ func GetDocumentPageRevisions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
revisions, _ := p.GetPageRevisions(pageID)
|
revisions, _ := p.GetPageRevisions(pageID)
|
||||||
|
|
||||||
|
if len(revisions) == 0 {
|
||||||
|
revisions = []entity.Revision{}
|
||||||
|
}
|
||||||
|
|
||||||
payload, err := json.Marshal(revisions)
|
payload, err := json.Marshal(revisions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue