Merge pull request #3312 from umbraco/temp8-3298-move-rollback
Temp8 3298 move rollback
This commit is contained in:
@@ -19,6 +19,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <remarks>Current version is first, and then versions are ordered with most recent first.</remarks>
|
||||
IEnumerable<TEntity> GetAllVersions(int nodeId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets versions.
|
||||
/// </summary>
|
||||
/// <remarks>Current version is first, and then versions are ordered with most recent first.</remarks>
|
||||
IEnumerable<TEntity> GetAllVersionsSlim(int nodeId, int skip, int take);
|
||||
|
||||
/// <summary>
|
||||
/// Gets version identifiers.
|
||||
/// </summary>
|
||||
|
||||
@@ -53,6 +53,10 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
// gets all versions, current first
|
||||
public abstract IEnumerable<TEntity> GetAllVersions(int nodeId);
|
||||
|
||||
// gets all versions, current first
|
||||
public virtual IEnumerable<TEntity> GetAllVersionsSlim(int nodeId, int skip, int take)
|
||||
=> GetAllVersions(nodeId).Skip(skip).Take(take);
|
||||
|
||||
// gets all version ids, current first
|
||||
public virtual IEnumerable<int> GetVersionIds(int nodeId, int maxRows)
|
||||
{
|
||||
|
||||
@@ -220,6 +220,16 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true);
|
||||
}
|
||||
|
||||
public override IEnumerable<IContent> GetAllVersionsSlim(int nodeId, int skip, int take)
|
||||
{
|
||||
var sql = GetBaseQuery(QueryType.Many, false)
|
||||
.Where<NodeDto>(x => x.NodeId == nodeId)
|
||||
.OrderByDescending<ContentVersionDto>(x => x.Current)
|
||||
.AndByDescending<ContentVersionDto>(x => x.VersionDate);
|
||||
|
||||
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true);
|
||||
}
|
||||
|
||||
public override IContent GetVersion(int versionId)
|
||||
{
|
||||
var sql = GetBaseQuery(QueryType.Single, false)
|
||||
@@ -911,7 +921,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
return base.ApplySystemOrdering(ref sql, ordering);
|
||||
}
|
||||
|
||||
private IEnumerable<IContent> MapDtosToContent(List<DocumentDto> dtos, bool withCache = false)
|
||||
private IEnumerable<IContent> MapDtosToContent(List<DocumentDto> dtos, bool withCache = false, bool slim = false)
|
||||
{
|
||||
var temps = new List<TempContent<Content>>();
|
||||
var contentTypes = new Dictionary<int, IContentType>();
|
||||
@@ -944,18 +954,21 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
var c = content[i] = ContentBaseFactory.BuildEntity(dto, contentType);
|
||||
|
||||
// need templates
|
||||
var templateId = dto.DocumentVersionDto.TemplateId;
|
||||
if (templateId.HasValue && templateId.Value > 0)
|
||||
templateIds.Add(templateId.Value);
|
||||
if (dto.Published)
|
||||
if (!slim)
|
||||
{
|
||||
templateId = dto.PublishedVersionDto.TemplateId;
|
||||
// need templates
|
||||
var templateId = dto.DocumentVersionDto.TemplateId;
|
||||
if (templateId.HasValue && templateId.Value > 0)
|
||||
templateIds.Add(templateId.Value);
|
||||
if (dto.Published)
|
||||
{
|
||||
templateId = dto.PublishedVersionDto.TemplateId;
|
||||
if (templateId.HasValue && templateId.Value > 0)
|
||||
templateIds.Add(templateId.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// need properties
|
||||
// need temps, for properties, templates and variations
|
||||
var versionId = dto.DocumentVersionDto.Id;
|
||||
var publishedVersionId = dto.Published ? dto.PublishedVersionDto.Id : 0;
|
||||
var temp = new TempContent<Content>(dto.NodeId, versionId, publishedVersionId, contentType, c)
|
||||
@@ -966,25 +979,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
temps.Add(temp);
|
||||
}
|
||||
|
||||
// load all required templates in 1 query, and index
|
||||
var templates = _templateRepository.GetMany(templateIds.ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// load all properties for all documents from database in 1 query - indexed by version id
|
||||
var properties = GetPropertyCollections(temps);
|
||||
|
||||
// assign templates and properties
|
||||
foreach (var temp in temps)
|
||||
if (!slim)
|
||||
{
|
||||
// complete the item
|
||||
if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template))
|
||||
temp.Content.Template = template;
|
||||
if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template))
|
||||
temp.Content.PublishTemplate = template;
|
||||
temp.Content.Properties = properties[temp.VersionId];
|
||||
// load all required templates in 1 query, and index
|
||||
var templates = _templateRepository.GetMany(templateIds.ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
temp.Content.ResetDirtyProperties(false);
|
||||
// load all properties for all documents from database in 1 query - indexed by version id
|
||||
var properties = GetPropertyCollections(temps);
|
||||
|
||||
// assign templates and properties
|
||||
foreach (var temp in temps)
|
||||
{
|
||||
// complete the item
|
||||
if (temp.Template1Id.HasValue && templates.TryGetValue(temp.Template1Id.Value, out var template))
|
||||
temp.Content.Template = template;
|
||||
if (temp.Template2Id.HasValue && templates.TryGetValue(temp.Template2Id.Value, out template))
|
||||
temp.Content.PublishTemplate = template;
|
||||
temp.Content.Properties = properties[temp.VersionId];
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
temp.Content.ResetDirtyProperties(false);
|
||||
}
|
||||
}
|
||||
|
||||
// set variations, if varying
|
||||
|
||||
@@ -134,6 +134,12 @@ namespace Umbraco.Core.Services
|
||||
/// <remarks>Versions are ordered with current first, then most recent first.</remarks>
|
||||
IEnumerable<IContent> GetVersions(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all versions of a document.
|
||||
/// </summary>
|
||||
/// <remarks>Versions are ordered with current first, then most recent first.</remarks>
|
||||
IEnumerable<IContent> GetVersionsSlim(int id, int skip, int take);
|
||||
|
||||
/// <summary>
|
||||
/// Gets top versions of a document.
|
||||
/// </summary>
|
||||
@@ -461,5 +467,21 @@ namespace Umbraco.Core.Services
|
||||
IContent CreateAndSave(string name, IContent parent, string contentTypeAlias, int userId = 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rollback
|
||||
|
||||
/// <summary>
|
||||
/// Rolls back the content to a specific version.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the content node.</param>
|
||||
/// <param name="versionId">The version id to roll back to.</param>
|
||||
/// <param name="culture">An optional culture to roll back.</param>
|
||||
/// <param name="userId">The identifier of the user who is performing the roll back.</param>
|
||||
/// <remarks>
|
||||
/// <para>When no culture is specified, all cultures are rolled back.</para>
|
||||
/// </remarks>
|
||||
OperationResult Rollback(int id, int versionId, string culture = "*", int userId = 0);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,6 +474,19 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of an <see cref="IContent"/> objects versions by Id
|
||||
/// </summary>
|
||||
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
|
||||
public IEnumerable<IContent> GetVersionsSlim(int id, int skip, int take)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.ContentTree);
|
||||
return _documentRepository.GetAllVersionsSlim(id, skip, take);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all version Ids for the given content item ordered so latest is first
|
||||
/// </summary>
|
||||
@@ -2490,5 +2503,68 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rollback
|
||||
|
||||
public OperationResult Rollback(int id, int versionId, string culture = "*", int userId = 0)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
//Get the current copy of the node
|
||||
var content = GetById(id);
|
||||
|
||||
//Get the version
|
||||
var version = GetVersion(versionId);
|
||||
|
||||
//Good ole null checks
|
||||
if (content == null || version == null)
|
||||
{
|
||||
return new OperationResult(OperationResultType.FailedCannot, evtMsgs);
|
||||
}
|
||||
|
||||
//Store the result of doing the save of content for the rollback
|
||||
OperationResult rollbackSaveResult;
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var rollbackEventArgs = new RollbackEventArgs<IContent>(content);
|
||||
|
||||
//Emit RollingBack event aka before
|
||||
if (scope.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
|
||||
//Copy the changes from the version
|
||||
content.CopyFrom(version, culture);
|
||||
|
||||
//Save the content for the rollback
|
||||
rollbackSaveResult = Save(content, userId);
|
||||
|
||||
//Depending on the save result - is what we log & audit along with what we return
|
||||
if (rollbackSaveResult.Success == false)
|
||||
{
|
||||
//Log the error/warning
|
||||
Logger.Error<ContentService>("User '{UserId}' was unable to rollback content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Emit RolledBack event aka after
|
||||
rollbackEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(RolledBack, this, rollbackEventArgs);
|
||||
|
||||
//Logging & Audit message
|
||||
Logger.Info<ContentService>("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
|
||||
Audit(AuditType.RollBack, $"Content '{content.Name}' was rolled back to version '{versionId}'", userId, id);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return rollbackSaveResult;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2330,6 +2330,22 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
Console.WriteLine("-");
|
||||
|
||||
var versionsSlim = ServiceContext.ContentService.GetVersionsSlim(page.Id, 0, 50).ToArray();
|
||||
Assert.AreEqual(5, versionsSlim.Length);
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
Console.Write("[{0}] ", i);
|
||||
Console.WriteLine(versionsSlim[i].UpdateDate.ToString("O").Substring(11));
|
||||
Console.WriteLine(" fr: {0}", versionsSlim[i].GetUpdateDate("fr")?.ToString("O").Substring(11));
|
||||
Console.WriteLine(" da: {0}", versionsSlim[i].GetUpdateDate("da")?.ToString("O").Substring(11));
|
||||
}
|
||||
Console.WriteLine("-");
|
||||
|
||||
// what we do in the controller to get rollback versions
|
||||
var versionsSlimFr = versionsSlim.Where(x => x.UpdateDate == x.GetUpdateDate("fr")).ToArray();
|
||||
Assert.AreEqual(3, versionsSlimFr.Length);
|
||||
|
||||
// alas, at the moment we do *not* properly track 'dirty' for cultures, meaning
|
||||
// that we cannot synchronize dates the way we do with publish dates - and so this
|
||||
// would fail - the version UpdateDate is greater than the cultures'.
|
||||
|
||||
@@ -103,6 +103,17 @@
|
||||
loadContent();
|
||||
}
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("editors.content.reload", function (name, args) {
|
||||
// if this content item uses the updated doc type we need to reload the content item
|
||||
if(args && args.node && args.node.key === $scope.content.key) {
|
||||
$scope.page.loading = true;
|
||||
loadContent().then(function() {
|
||||
$scope.page.loading = false;
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+16
@@ -101,6 +101,22 @@
|
||||
scope.node.template = templateAlias;
|
||||
};
|
||||
|
||||
scope.openRollback = function() {
|
||||
|
||||
var rollback = {
|
||||
node: scope.node,
|
||||
submit: function(model) {
|
||||
const args = { node: scope.node };
|
||||
eventsService.emit("editors.content.reload", args);
|
||||
editorService.close();
|
||||
},
|
||||
close: function() {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
editorService.rollback(rollback);
|
||||
};
|
||||
|
||||
function loadAuditTrail() {
|
||||
|
||||
scope.loadingAuditTrail = true;
|
||||
|
||||
@@ -808,8 +808,106 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
),
|
||||
"Failed to create blueprint from content with id " + contentId
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentResource#getRollbackVersions
|
||||
* @methodOf umbraco.resources.contentResource
|
||||
*
|
||||
* @description
|
||||
* Returns an array of previous version id's, given a node id and a culture
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* contentResource.getRollbackVersions(id, culture)
|
||||
* .then(function(versions) {
|
||||
* alert('its here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {Int} id Id of node
|
||||
* @param {Int} culture if provided, the results will be for this specific culture/variant
|
||||
* @returns {Promise} resourcePromise object containing the versions
|
||||
*
|
||||
*/
|
||||
getRollbackVersions: function (contentId, culture) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetRollbackVersions", {
|
||||
contentId: contentId,
|
||||
culture: culture
|
||||
})
|
||||
),
|
||||
"Failed to get rollback versions for content item with id " + contentId
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentResource#getRollbackVersion
|
||||
* @methodOf umbraco.resources.contentResource
|
||||
*
|
||||
* @description
|
||||
* Returns a previous version of a content item
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* contentResource.getRollbackVersion(versionId, culture)
|
||||
* .then(function(version) {
|
||||
* alert('its here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {Int} versionId The version Id
|
||||
* @param {Int} culture if provided, the results will be for this specific culture/variant
|
||||
* @returns {Promise} resourcePromise object containing the version
|
||||
*
|
||||
*/
|
||||
getRollbackVersion: function (versionId, culture) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetRollbackVersion", {
|
||||
versionId: versionId,
|
||||
culture: culture
|
||||
})
|
||||
),
|
||||
"Failed to get version for content item with id " + versionId
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentResource#rollback
|
||||
* @methodOf umbraco.resources.contentResource
|
||||
*
|
||||
* @description
|
||||
* Roll backs a content item to a previous version
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* contentResource.rollback(contentId, versionId, culture)
|
||||
* .then(function() {
|
||||
* alert('its here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {Int} id Id of node
|
||||
* @param {Int} versionId The version Id
|
||||
* @param {Int} culture if provided, the results will be for this specific culture/variant
|
||||
* @returns {Promise} resourcePromise object
|
||||
*
|
||||
*/
|
||||
rollback: function (contentId, versionId, culture) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostRollbackContent", {
|
||||
contentId: contentId, versionId:versionId, culture:culture
|
||||
})
|
||||
),
|
||||
"Failed to roll back content item with id " + contentId
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -180,6 +180,25 @@
|
||||
open(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.editorService#rollback
|
||||
* @methodOf umbraco.services.editorService
|
||||
*
|
||||
* @description
|
||||
* Opens a rollback editor in infinite editing.
|
||||
* @param {String} editor.node The node to rollback
|
||||
* @param {Callback} editor.submit Saves, submits, and closes the editor
|
||||
* @param {Callback} editor.close Closes the editor
|
||||
* @returns {Object} editor object
|
||||
*/
|
||||
|
||||
function rollback(editor) {
|
||||
editor.view = "views/common/infiniteeditors/rollback/rollback.html";
|
||||
editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.services.editorService#linkPicker
|
||||
@@ -481,6 +500,7 @@
|
||||
copy: copy,
|
||||
move: move,
|
||||
embed: embed,
|
||||
rollback: rollback,
|
||||
linkPicker: linkPicker,
|
||||
mediaPicker: mediaPicker,
|
||||
iconPicker: iconPicker,
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
}
|
||||
|
||||
.umb-button--xs {
|
||||
padding: 5px 16px;
|
||||
padding: 5px 13px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
.umb-box-header {
|
||||
padding: 10px 20px;
|
||||
border-bottom: 1px solid @gray-9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.umb-box-header-title {
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
.ws-normal { white-space: normal; }
|
||||
.nowrap { white-space: nowrap; }
|
||||
.pre { white-space: pre; }
|
||||
.pre-wrap { white-space: pre-wrap; }
|
||||
.pre-line { white-space: pre-line; }
|
||||
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function RollbackController($scope, contentResource, localizationService, assetsService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.rollback = rollback;
|
||||
vm.changeLanguage = changeLanguage;
|
||||
vm.changeVersion = changeVersion;
|
||||
vm.submit = submit;
|
||||
vm.close = close;
|
||||
|
||||
//////////
|
||||
|
||||
function onInit() {
|
||||
|
||||
vm.loading = true;
|
||||
vm.variantVersions = [];
|
||||
vm.diff = null;
|
||||
vm.currentVersion = null;
|
||||
vm.rollbackButtonDisabled = true;
|
||||
|
||||
// find the current version for invariant nodes
|
||||
if($scope.model.node.variants.length === 1) {
|
||||
vm.currentVersion = $scope.model.node.variants[0];
|
||||
}
|
||||
|
||||
// find the current version for nodes with variants
|
||||
if($scope.model.node.variants.length > 1) {
|
||||
var active = _.find($scope.model.node.variants, function (v) {
|
||||
return v.active;
|
||||
});
|
||||
|
||||
// preselect the language in the dropdown
|
||||
if(active) {
|
||||
vm.selectedLanguage = active;
|
||||
vm.currentVersion = active;
|
||||
}
|
||||
}
|
||||
|
||||
// set default title
|
||||
if(!$scope.model.title) {
|
||||
localizationService.localize("actions_rollback").then(function(value){
|
||||
$scope.model.title = value;
|
||||
});
|
||||
}
|
||||
|
||||
// Load in diff library
|
||||
assetsService.loadJs('lib/jsdiff/diff.min.js', $scope).then(function () {
|
||||
|
||||
getVersions().then(function(){
|
||||
vm.loading = false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function changeLanguage(language) {
|
||||
vm.currentVersion = language;
|
||||
getVersions();
|
||||
}
|
||||
|
||||
function changeVersion(version) {
|
||||
|
||||
if(version && version.versionId) {
|
||||
|
||||
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
|
||||
|
||||
contentResource.getRollbackVersion(version.versionId, culture)
|
||||
.then(function(data){
|
||||
vm.previousVersion = data;
|
||||
vm.previousVersion.versionId = version.versionId;
|
||||
createDiff(vm.currentVersion, vm.previousVersion);
|
||||
vm.rollbackButtonDisabled = false;
|
||||
});
|
||||
|
||||
} else {
|
||||
vm.diff = null;
|
||||
vm.rollbackButtonDisabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function getVersions() {
|
||||
|
||||
const nodeId = $scope.model.node.id;
|
||||
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
|
||||
|
||||
return contentResource.getRollbackVersions(nodeId, culture)
|
||||
.then(function(data){
|
||||
vm.previousVersions = data.map(version => {
|
||||
version.displayValue = version.versionDate + " - " + version.versionAuthorName;
|
||||
return version;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This will load in a new version
|
||||
*/
|
||||
function createDiff(currentVersion, previousVersion) {
|
||||
|
||||
vm.diff = {};
|
||||
vm.diff.properties = [];
|
||||
|
||||
// find diff in name
|
||||
vm.diff.name = JsDiff.diffWords(currentVersion.name, previousVersion.name);
|
||||
|
||||
// extract all properties from the tabs and create new object for the diff
|
||||
currentVersion.tabs.forEach((tab, tabIndex) => {
|
||||
tab.properties.forEach((property, propertyIndex) => {
|
||||
var oldProperty = previousVersion.tabs[tabIndex].properties[propertyIndex];
|
||||
|
||||
// we have to make properties storing values as object into strings (Grid, nested content, etc.)
|
||||
if(property.value instanceof Object) {
|
||||
property.value = JSON.stringify(property.value, null, 1);
|
||||
property.isObject = true;
|
||||
}
|
||||
|
||||
if(oldProperty.value instanceof Object) {
|
||||
oldProperty.value = JSON.stringify(oldProperty.value, null, 1);
|
||||
oldProperty.isObject = true;
|
||||
}
|
||||
|
||||
// create new property object used in the diff table
|
||||
var diffProperty = {
|
||||
"alias": property.alias,
|
||||
"label": property.label,
|
||||
"diff": (property.value || oldProperty.value) ? JsDiff.diffWords(property.value, oldProperty.value) : "",
|
||||
"isObject": (property.isObject || oldProperty.isObject) ? true : false
|
||||
};
|
||||
|
||||
vm.diff.properties.push(diffProperty);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
|
||||
vm.rollbackButtonState = "busy";
|
||||
|
||||
const nodeId = $scope.model.node.id;
|
||||
const versionId = vm.previousVersion.versionId;
|
||||
const culture = $scope.model.node.variants.length > 1 ? vm.currentVersion.language.culture : null;
|
||||
|
||||
return contentResource.rollback(nodeId, versionId, culture)
|
||||
.then(data => {
|
||||
vm.rollbackButtonState = "success";
|
||||
submit();
|
||||
}, error => {
|
||||
vm.rollbackButtonState = "error";
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if($scope.model.submit) {
|
||||
$scope.model.submit($scope.model.submit);
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
if($scope.model.close) {
|
||||
$scope.model.close();
|
||||
}
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.RollbackController", RollbackController);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,104 @@
|
||||
<div ng-controller="Umbraco.Editors.RollbackController as vm">
|
||||
|
||||
<umb-editor-view>
|
||||
|
||||
<umb-editor-header
|
||||
name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
|
||||
<umb-load-indicator
|
||||
ng-if="vm.loading">
|
||||
</umb-load-indicator>
|
||||
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
|
||||
<div ng-if="model.node.variants.length > 1">
|
||||
<h5><localize key="general_language"></localize></h5>
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="vm.selectedLanguage"
|
||||
ng-options="variant as variant.language.name for variant in model.node.variants track by variant.language.culture"
|
||||
ng-change="vm.changeLanguage(vm.selectedLanguage)">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
<h5><localize key="rollback_currentVersion"></localize></h5>
|
||||
<p>{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})</p>
|
||||
|
||||
<h5><localize key="rollback_rollbackTo"></localize></h5>
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="vm.selectedVersion"
|
||||
ng-options="version.displayValue for version in vm.previousVersions track by version.versionId"
|
||||
ng-change="vm.changeVersion(vm.selectedVersion)">
|
||||
<option value=""><localize key="general_choose">Choose</localize>...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.diff" class="diff" style="border-top: 1px solid #e9e9eb; margin-top: 30px;">
|
||||
|
||||
<h5>Changes</h5>
|
||||
<small style="margin-bottom: 15px; display: block;"><localize key="rollback_diffHelp"></localize></small>
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="bold">Name</td>
|
||||
<td>
|
||||
<span ng-repeat="part in vm.diff.name">
|
||||
<ins ng-if="part.added">{{part.value}}</ins>
|
||||
<del ng-if="part.removed">{{part.value}}</del>
|
||||
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="property in vm.diff.properties track by property.alias">
|
||||
<td class="bold">{{property.label}}</td>
|
||||
<td ng-class="{'pre-line': property.isObject}">
|
||||
<span ng-repeat="part in property.diff">
|
||||
<ins ng-if="part.added">{{part.value}}</ins>
|
||||
<del ng-if="part.removed">{{part.value}}</del>
|
||||
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
action="vm.close()">
|
||||
</umb-button>
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="success"
|
||||
state="vm.rollbackButtonState"
|
||||
label-key="actions_rollback"
|
||||
disabled="vm.rollbackButtonDisabled"
|
||||
action="vm.rollback()">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</div>
|
||||
@@ -41,7 +41,19 @@
|
||||
</umb-box>
|
||||
|
||||
<umb-box data-element="node-info-history">
|
||||
<umb-box-header title-key="general_history"></umb-box-header>
|
||||
|
||||
<umb-box-header
|
||||
title-key="general_history">
|
||||
<umb-button
|
||||
type="button"
|
||||
button-style="outline"
|
||||
action="openRollback()"
|
||||
label-key="actions_rollback"
|
||||
size="xs"
|
||||
add-ellipsis="true">
|
||||
</umb-button>
|
||||
</umb-box-header>
|
||||
|
||||
<umb-box-content class="block-form">
|
||||
|
||||
<div style="position: relative;">
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
<div class="umb-box-header" ng-transclude>
|
||||
<div class="umb-box-header-title" ng-if="title || titleKey">
|
||||
<localize ng-if="titleKey" key="{{titleKey}}"></localize>
|
||||
<span ng-if="title">{{title}}</span>
|
||||
</div>
|
||||
<div class="umb-box-header-description" ng-if="description || descriptionKey">
|
||||
<localize ng-if="descriptionKey" key="{{descriptionKey}}"></localize>
|
||||
<span ng-if="description">{{description}}</span>
|
||||
<div class="umb-box-header">
|
||||
<div>
|
||||
<div class="umb-box-header-title" ng-if="title || titleKey">
|
||||
<localize ng-if="titleKey" key="{{titleKey}}"></localize>
|
||||
<span ng-if="title">{{title}}</span>
|
||||
</div>
|
||||
<div class="umb-box-header-description" ng-if="description || descriptionKey">
|
||||
<localize ng-if="descriptionKey" key="{{descriptionKey}}"></localize>
|
||||
<span ng-if="description">{{description}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ng-transclude></ng-transclude>
|
||||
</div>
|
||||
@@ -1,117 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function ContentRollbackController($scope, assetsService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
vm.rollback = rollback;
|
||||
vm.loadVersion = loadVersion;
|
||||
vm.closeDialog = closeDialog;
|
||||
|
||||
function onInit() {
|
||||
|
||||
vm.loading = true;
|
||||
vm.variantVersions = [];
|
||||
vm.diff = null;
|
||||
|
||||
// Load in diff library
|
||||
assetsService.loadJs('lib/jsdiff/diff.min.js', $scope).then(function () {
|
||||
|
||||
var currentLanguage = $scope.currentNode.metaData.culture;
|
||||
|
||||
vm.currentVersion = {
|
||||
"id": 1,
|
||||
"createDate": "22/08/2018 13.32",
|
||||
"name": "Forside",
|
||||
"properties": [
|
||||
{
|
||||
"alias": "headline",
|
||||
"label": "Headline",
|
||||
"value": "Velkommen"
|
||||
},
|
||||
{
|
||||
"alias": "text",
|
||||
"label": "Text",
|
||||
"value": "This is my danish Content"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
vm.previousVersions = [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Forside",
|
||||
"createDate": "21/08/2018 19.25"
|
||||
}
|
||||
];
|
||||
|
||||
vm.loading = false;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
console.log("rollback");
|
||||
}
|
||||
|
||||
/**
|
||||
* This will load in a new version
|
||||
*/
|
||||
function loadVersion(id) {
|
||||
|
||||
// fake load version
|
||||
var currentLanguage = $scope.currentNode.metaData.culture;
|
||||
|
||||
vm.diff = {};
|
||||
vm.diff.properties = [];
|
||||
|
||||
var oldVersion = {
|
||||
"id": 2,
|
||||
"name": "Foride",
|
||||
"properties": [
|
||||
{
|
||||
"alias": "headline",
|
||||
"label": "Headline",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"alias": "text",
|
||||
"label": "Text",
|
||||
"value": "This is my danish Content Test"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// find diff in name
|
||||
vm.diff.name = JsDiff.diffWords(vm.currentVersion.name, oldVersion.name);
|
||||
|
||||
// find diff in properties
|
||||
angular.forEach(vm.currentVersion.properties,
|
||||
function(newProperty, index){
|
||||
var oldProperty = oldVersion.properties[index];
|
||||
var diffProperty = {
|
||||
"alias": newProperty.alias,
|
||||
"label": newProperty.label,
|
||||
"diff": JsDiff.diffWords(newProperty.value, oldProperty.value)
|
||||
};
|
||||
vm.diff.properties.push(diffProperty);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This will close the dialog
|
||||
*/
|
||||
function closeDialog() {
|
||||
$scope.nav.hideDialog();
|
||||
}
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.RollbackController", ContentRollbackController);
|
||||
|
||||
})();
|
||||
@@ -1,80 +0,0 @@
|
||||
<div ng-controller="Umbraco.Editors.Content.RollbackController as vm">
|
||||
|
||||
<div class="umb-dialog-body" ng-cloak>
|
||||
|
||||
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
|
||||
|
||||
<div class="umb-pane" ng-show="!vm.loading">
|
||||
|
||||
<div>
|
||||
<h5>Rollback {{ currentNode.name }}</h5>
|
||||
|
||||
<h5><localize key="rollback_currentVersion"></localize></h5>
|
||||
<p>{{vm.currentVersion.name}} (Created: {{vm.currentVersion.createDate}})</p>
|
||||
|
||||
<h5><localize key="rollback_rollbackTo"></localize></h5>
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="vm.selectedVersion"
|
||||
ng-options="version.createDate for version in vm.previousVersions track by version.id"
|
||||
ng-change="vm.loadVersion(vm.selectedVersion.id)">
|
||||
<option value=""><localize key="general_choose">Choose</localize>...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.diff" class="diff" style="border-top: 1px solid #e9e9eb; margin-top: 30px;">
|
||||
|
||||
<h5>Changes</h5>
|
||||
<small style="margin-bottom: 15px; display: block;"><localize key="rollback_diffHelp"></localize></small>
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="bold">Name</td>
|
||||
<td>
|
||||
<span ng-repeat="part in vm.diff.name">
|
||||
<ins ng-if="part.added">{{part.value}}</ins>
|
||||
<del ng-if="part.removed">{{part.value}}</del>
|
||||
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="property in vm.diff.properties track by property.alias">
|
||||
<td class="bold">{{property.label}}</td>
|
||||
<td>
|
||||
<span ng-repeat="part in property.diff">
|
||||
<ins ng-if="part.added">{{part.value}}</ins>
|
||||
<del ng-if="part.removed">{{part.value}}</del>
|
||||
<span ng-if="!part.added && !part.removed">{{part.value}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
|
||||
<umb-button
|
||||
label-key="general_close"
|
||||
disabled="vm.saveState === 'busy'"
|
||||
action="vm.closeDialog()"
|
||||
type="button"
|
||||
button-style="link">
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
label-key="buttons_rollback"
|
||||
disabled="vm.saveState === 'busy'"
|
||||
state="vm.saveState"
|
||||
action="vm.rollback()"
|
||||
type="button"
|
||||
button-style="success">
|
||||
</umb-button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -348,7 +348,6 @@
|
||||
<Content Include="Umbraco\Developer\Packages\directoryBrowser.aspx" />
|
||||
<Content Include="Umbraco\Developer\Packages\editPackage.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\protectPage.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\rollBack.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\sendToTranslation.aspx" />
|
||||
<Content Include="Umbraco\Config\Create\UI.xml" />
|
||||
<Content Include="Umbraco\Config\Lang\en.xml">
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master"AutoEventWireup="True" Inherits="umbraco.presentation.dialogs.rollBack" %>
|
||||
|
||||
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
|
||||
<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="head" runat="server">
|
||||
<script type="text/javascript">
|
||||
function doSubmit() { document.Form1["ok"].click() }
|
||||
|
||||
var functionsFrame = this;
|
||||
var tabFrame = this;
|
||||
var isDialog = true;
|
||||
var submitOnEnter = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
.propertyItemheader {
|
||||
width: 140px !Important;
|
||||
}
|
||||
|
||||
.diff {
|
||||
margin-top: 10px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
border-top: 1px solid #efefef;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.diff table td {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.diff del {
|
||||
background: rgb(255, 230, 230) none repeat scroll 0%;
|
||||
-moz-background-clip: -moz-initial;
|
||||
-moz-background-origin: -moz-initial;
|
||||
-moz-background-inline-policy: -moz-initial;
|
||||
}
|
||||
|
||||
.diff ins {
|
||||
background: rgb(230, 255, 230) none repeat scroll 0%;
|
||||
-moz-background-clip: -moz-initial;
|
||||
-moz-background-origin: -moz-initial;
|
||||
-moz-background-inline-policy: -moz-initial;
|
||||
}
|
||||
|
||||
.diff .diffnotice {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="body" runat="server">
|
||||
<div class="umb-dialog-body">
|
||||
<umb:JsInclude ID="JsInclude1" runat="server" FilePath="js/umbracoCheckKeys.js" PathNameAlias="UmbracoRoot" />
|
||||
|
||||
<cc1:Feedback ID="feedBackMsg" runat="server" />
|
||||
|
||||
<cc1:Pane ID="pp_selectVersion" runat="server" Text="Select a version to compare with the current version">
|
||||
<cc1:PropertyPanel id="pp_currentVersion" Text="Current version" runat="server">
|
||||
<asp:Literal ID="currentVersionTitle" runat="server" />
|
||||
<small>(<asp:Literal ID="currentVersionMeta" runat="server" />)</small></cc1:PropertyPanel>
|
||||
<cc1:PropertyPanel ID="pp_rollBackTo" Text="Rollback to" runat="server">
|
||||
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" Width="400px" AutoPostBack="True" CssClass="guiInputTextTiny" />
|
||||
</cc1:PropertyPanel>
|
||||
|
||||
<cc1:PropertyPanel id="pp_view" Text="View" runat="server">
|
||||
<small>
|
||||
<asp:RadioButtonList ID="rbl_mode" runat="server" OnSelectedIndexChanged="version_load" RepeatDirection="Horizontal">
|
||||
<asp:ListItem Selected="True" Value="diff">Diff</asp:ListItem>
|
||||
<asp:ListItem Value="html">Html</asp:ListItem>
|
||||
</asp:RadioButtonList>
|
||||
</small>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<asp:Panel ID="diffPanel" Visible="false" runat="server" Height="300px">
|
||||
<div class="diff">
|
||||
<div class="diffnotice">
|
||||
<p>
|
||||
<asp:Literal ID="lt_notice" runat="server" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table border="0" style="width: 95%;">
|
||||
<asp:Literal ID="propertiesCompare" runat="server"></asp:Literal>
|
||||
</table>
|
||||
</div>
|
||||
</asp:Panel>
|
||||
</div>
|
||||
|
||||
<div runat="server" id="pl_buttons" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("general/cancel")%></a>
|
||||
<asp:Button ID="Button1" runat="server" visible="false" CssClass="btn btn-primary" OnClick="doRollback_Click"></asp:Button>
|
||||
</div>
|
||||
</asp:Content>
|
||||
@@ -1702,5 +1702,93 @@ namespace Umbraco.Web.Editors
|
||||
Services.NotificationService.SetNotifications(Security.CurrentUser, content, notifyOptions);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<RollbackVersion> GetRollbackVersions(int contentId, string culture = null)
|
||||
{
|
||||
var rollbackVersions = new List<RollbackVersion>();
|
||||
var writerIds = new HashSet<int>();
|
||||
|
||||
//Return a list of all versions of a specific content node
|
||||
// fixme - cap at 50 versions for now?
|
||||
var versions = Services.ContentService.GetVersionsSlim(contentId, 0, 50);
|
||||
|
||||
//Not all nodes are variants & thus culture can be null
|
||||
if (culture != null)
|
||||
{
|
||||
//Get cultures that were published with the version = their update date is equal to the version's
|
||||
versions = versions.Where(x => x.UpdateDate == x.GetUpdateDate(culture));
|
||||
}
|
||||
|
||||
//First item is our current item/state (cant rollback to ourselves)
|
||||
versions = versions.Skip(1);
|
||||
|
||||
foreach (var version in versions)
|
||||
{
|
||||
var rollbackVersion = new RollbackVersion
|
||||
{
|
||||
VersionId = version.VersionId,
|
||||
VersionDate = version.UpdateDate,
|
||||
VersionAuthorId = version.WriterId
|
||||
};
|
||||
|
||||
rollbackVersions.Add(rollbackVersion);
|
||||
|
||||
writerIds.Add(version.WriterId);
|
||||
}
|
||||
|
||||
var users = Services.UserService
|
||||
.GetUsersById(writerIds.ToArray())
|
||||
.ToDictionary(x => x.Id, x => x.Name);
|
||||
|
||||
foreach (var rollbackVersion in rollbackVersions)
|
||||
{
|
||||
if (users.TryGetValue(rollbackVersion.VersionAuthorId, out var userName))
|
||||
rollbackVersion.VersionAuthorName = userName;
|
||||
}
|
||||
|
||||
return rollbackVersions;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ContentVariantDisplay GetRollbackVersion(int versionId, string culture = null)
|
||||
{
|
||||
var version = Services.ContentService.GetVersion(versionId);
|
||||
var content = MapToDisplay(version);
|
||||
|
||||
return culture == null
|
||||
? content.Variants.FirstOrDefault() //No culture set - so this is an invariant node - so just list me the first item in here
|
||||
: content.Variants.FirstOrDefault(x => x.Language.IsoCode == culture);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public HttpResponseMessage PostRollbackContent(int contentId, int versionId, string culture = "*")
|
||||
{
|
||||
var rollbackResult = Services.ContentService.Rollback(contentId, versionId, culture, Security.GetUserId().ResultOr(0));
|
||||
|
||||
if (rollbackResult.Success)
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
|
||||
switch (rollbackResult.Result)
|
||||
{
|
||||
case OperationResultType.Failed:
|
||||
case OperationResultType.FailedCannot:
|
||||
case OperationResultType.FailedExceptionThrown:
|
||||
case OperationResultType.NoOperation:
|
||||
default:
|
||||
notificationModel.AddErrorNotification(
|
||||
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
|
||||
null); //TODO: There is no specific failed to save error message AFAIK
|
||||
break;
|
||||
case OperationResultType.FailedCancelledByEvent:
|
||||
notificationModel.AddErrorNotification(
|
||||
Services.TextService.Localize("speechBubbles/operationCancelledHeader"),
|
||||
Services.TextService.Localize("speechBubbles/operationCancelledText"));
|
||||
break;
|
||||
}
|
||||
|
||||
return Request.CreateValidationErrorResponse(notificationModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "rollbackVersion", Namespace = "")]
|
||||
public class RollbackVersion
|
||||
{
|
||||
[DataMember(Name = "versionId")]
|
||||
public int VersionId { get; set; }
|
||||
|
||||
[DataMember(Name = "versionDate")]
|
||||
public DateTime VersionDate { get; set; }
|
||||
|
||||
[DataMember(Name = "versionAuthorId")]
|
||||
public int VersionAuthorId { get; set; }
|
||||
|
||||
[DataMember(Name = "versionAuthorName")]
|
||||
public string VersionAuthorName { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -238,7 +238,6 @@ namespace Umbraco.Web.Trees
|
||||
|
||||
AddActionNode<ActionSort>(item, menu, true);
|
||||
|
||||
AddActionNode<ActionRollback>(item, menu);
|
||||
AddActionNode<ActionToPublish>(item, menu, convert: true);
|
||||
AddActionNode<ActionAssignDomain>(item, menu);
|
||||
AddActionNode<ActionRights>(item, menu, convert: true);
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
<Compile Include="Media\TypeDetector\SvgDetector.cs" />
|
||||
<Compile Include="Media\TypeDetector\TIFFDetector.cs" />
|
||||
<Compile Include="Media\UploadAutoFillProperties.cs" />
|
||||
<Compile Include="Models\ContentEditing\RollbackVersion.cs" />
|
||||
<Compile Include="Models\ContentEditing\UnpublishContent.cs" />
|
||||
<Compile Include="Models\Mapping\MappingOperationOptionsExtensions.cs" />
|
||||
<Compile Include="ContentApps\ContentAppDefinitionCollection.cs" />
|
||||
@@ -1311,12 +1312,6 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx.designer.cs">
|
||||
<DependentUpon>editPackage.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.cs">
|
||||
<DependentUpon>rollBack.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx.designer.cs">
|
||||
<DependentUpon>rollBack.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\DisableEventValidation.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\Item.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\ItemRenderer.cs" />
|
||||
@@ -1401,9 +1396,6 @@
|
||||
<Content Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Content>
|
||||
<Content Include="umbraco.presentation\umbraco\dialogs\rollBack.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Web References\org.umbraco.update\checkforupgrade.disco" />
|
||||
@@ -1457,10 +1449,7 @@
|
||||
<CachedSettingsPropName>umbraco_org_umbraco_update_CheckForUpgrade</CachedSettingsPropName>
|
||||
</WebReferenceUrl>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="umbraco.presentation\umbraco\settings\" />
|
||||
<Folder Include="umbraco.presentation\umbraco\translation\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!--
|
||||
copied from Microsoft.CSharp.targets
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
<%@ Page Language="c#" CodeBehind="rollBack.aspx.cs" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.presentation.dialogs.rollBack" %>
|
||||
|
||||
<%@ Register TagPrefix="umb" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
|
||||
<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" %>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="head" runat="server">
|
||||
<script type="text/javascript">
|
||||
function doSubmit() { document.Form1["ok"].click() }
|
||||
|
||||
var functionsFrame = this;
|
||||
var tabFrame = this;
|
||||
var isDialog = true;
|
||||
var submitOnEnter = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
.propertyItemheader {
|
||||
width: 140px !Important;
|
||||
}
|
||||
|
||||
.diff {
|
||||
margin-top: 10px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
border-top: 1px solid #ccc;
|
||||
border-top: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.diff table td {
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.diff del {
|
||||
background: rgb(255, 230, 230) none repeat scroll 0%;
|
||||
-moz-background-clip: -moz-initial;
|
||||
-moz-background-origin: -moz-initial;
|
||||
-moz-background-inline-policy: -moz-initial;
|
||||
}
|
||||
|
||||
.diff ins {
|
||||
background: rgb(230, 255, 230) none repeat scroll 0%;
|
||||
-moz-background-clip: -moz-initial;
|
||||
-moz-background-origin: -moz-initial;
|
||||
-moz-background-inline-policy: -moz-initial;
|
||||
}
|
||||
|
||||
.diff .diffnotice {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
</asp:Content>
|
||||
|
||||
<asp:Content ContentPlaceHolderID="body" runat="server">
|
||||
<div class="umb-dialog-body">
|
||||
<umb:JsInclude ID="JsInclude1" runat="server" FilePath="js/umbracoCheckKeys.js" PathNameAlias="UmbracoRoot" />
|
||||
|
||||
<cc1:Feedback ID="feedBackMsg" runat="server" />
|
||||
|
||||
<cc1:Pane ID="pp_selectVersion" runat="server" Text="Select a version to compare with the current version">
|
||||
<cc1:PropertyPanel id="pp_currentVersion" Text="Current version" runat="server">
|
||||
<asp:Literal ID="currentVersionTitle" runat="server" />
|
||||
<small>(<asp:Literal ID="currentVersionMeta" runat="server" />)</small></cc1:PropertyPanel>
|
||||
<cc1:PropertyPanel ID="pp_rollBackTo" Text="Rollback to" runat="server">
|
||||
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" Width="400px" AutoPostBack="True" CssClass="guiInputTextTiny" />
|
||||
</cc1:PropertyPanel>
|
||||
|
||||
<cc1:PropertyPanel id="pp_view" Text="View" runat="server">
|
||||
<small>
|
||||
<asp:RadioButtonList ID="rbl_mode" runat="server" OnSelectedIndexChanged="version_load" RepeatDirection="Horizontal">
|
||||
<asp:ListItem Selected="True" Value="diff">Diff</asp:ListItem>
|
||||
<asp:ListItem Value="html">Html</asp:ListItem>
|
||||
</asp:RadioButtonList>
|
||||
</small>
|
||||
</cc1:PropertyPanel>
|
||||
</cc1:Pane>
|
||||
|
||||
<asp:Panel ID="diffPanel" Visible="false" runat="server" Height="300px">
|
||||
<div class="diff">
|
||||
<div class="diffnotice">
|
||||
<p>
|
||||
<asp:Literal ID="lt_notice" runat="server" />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<table border="0" style="width: 95%;">
|
||||
<asp:Literal ID="propertiesCompare" runat="server"></asp:Literal>
|
||||
</table>
|
||||
</div>
|
||||
</asp:Panel>
|
||||
</div>
|
||||
|
||||
<div runat="server" id="pl_buttons" class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
|
||||
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("general/cancel")%></a>
|
||||
<asp:Button ID="Button1" runat="server" Visible="false" CssClass="btn btn-primary" OnClick="doRollback_Click"></asp:Button>
|
||||
</div>
|
||||
</asp:Content>
|
||||
@@ -1,156 +0,0 @@
|
||||
//TODO: REbuild this in angular and then remove
|
||||
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.ComponentModel;
|
||||
//using System.Data;
|
||||
//using System.Drawing;
|
||||
//using System.Linq;
|
||||
//using System.Web;
|
||||
//using System.Web.SessionState;
|
||||
//using System.Web.UI;
|
||||
//using System.Web.UI.WebControls;
|
||||
//using System.Web.UI.HtmlControls;
|
||||
//
|
||||
//using umbraco.cms.businesslogic.web;
|
||||
//using umbraco.cms.businesslogic.property;
|
||||
|
||||
//namespace umbraco.presentation.dialogs
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// Summary description for rollBack.
|
||||
// /// </summary>
|
||||
// public partial class rollBack : UmbracoEnsuredPage
|
||||
// {
|
||||
// public rollBack()
|
||||
// {
|
||||
// CurrentApp = Constants.Applications.Content.ToString();
|
||||
|
||||
// }
|
||||
// private Document currentDoc = new Document(int.Parse(helper.Request("nodeId")));
|
||||
|
||||
// protected void version_load(object sender, EventArgs e) {
|
||||
|
||||
// if (allVersions.SelectedValue != "")
|
||||
// {
|
||||
// diffPanel.Visible = true;
|
||||
// Document rollback = new Document(currentDoc.Id, new Guid(allVersions.SelectedValue));
|
||||
|
||||
// propertiesCompare.Text = "<tr><th style='width: 25%;' valign='top'>" + Services.TextService.Localize("general/name") + ":</th><td>" + rollback.Text + "</td></tr>";
|
||||
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + Services.TextService.Localize("content/createDate") + ":</th><td>" + rollback.VersionDate.ToLongDateString() + " " + rollback.VersionDate.ToLongTimeString() + " " + Services.TextService.Localize("general/by") + ": " + rollback.User.Name + "</td></tr>";
|
||||
|
||||
// if (rbl_mode.SelectedValue == "diff")
|
||||
// lt_notice.Text = Services.TextService.Localize("rollback/diffHelp");
|
||||
// else
|
||||
// lt_notice.Text = Services.TextService.Localize("rollback/htmlHelp");
|
||||
|
||||
|
||||
// var props = rollback.GenericProperties;
|
||||
// foreach (Property p in props)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
|
||||
// if (p.Value != null)
|
||||
// {
|
||||
|
||||
// //new property value...
|
||||
// string thevalue = p.Value.ToString();
|
||||
|
||||
// if (rbl_mode.SelectedValue == "diff")
|
||||
// {
|
||||
|
||||
// //if display mode is set to diff...
|
||||
// thevalue = library.StripHtml(p.Value.ToString());
|
||||
// Property cP = currentDoc.getProperty(p.PropertyType);
|
||||
// if (cP != null && cP.Value != null)
|
||||
// {
|
||||
|
||||
// string cThevalue = library.StripHtml(cP.Value.ToString());
|
||||
|
||||
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + library.ReplaceLineBreaks(cms.businesslogic.utilities.Diff.Diff2Html(cThevalue, thevalue)) + "</td></tr>";
|
||||
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //If no current version of the value... display with no diff.
|
||||
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + thevalue + "</td></tr>";
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //If display mode is html
|
||||
// propertiesCompare.Text += "<tr><th style='width: 25%;' valign='top'>" + p.PropertyType.Name + ":</th><td>" + thevalue + "</td></tr>";
|
||||
// }
|
||||
|
||||
// //previewVersionContent.Controls.Add(new LiteralControl("<div style=\"margin-top: 4px; border: 1px solid #DEDEDE; padding: 4px;\"><p style=\"padding: 0px; margin: 0px;\" class=\"guiDialogNormal\"><b>" + p.PropertyType.Name + "</b><br/>"));
|
||||
// //previewVersionContent.Controls.Add(new LiteralControl(thevalue));
|
||||
|
||||
// }
|
||||
// //previewVersionContent.Controls.Add(new LiteralControl("</p></div>"));
|
||||
// }
|
||||
// catch { }
|
||||
// }
|
||||
|
||||
// Button1.Visible = true;
|
||||
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// diffPanel.Visible = false;
|
||||
// Button1.Visible = false;
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// protected void Page_Load(object sender, System.EventArgs e)
|
||||
// {
|
||||
|
||||
// if (String.IsNullOrEmpty(allVersions.SelectedValue))
|
||||
// rbl_mode.AutoPostBack = false;
|
||||
// else
|
||||
// rbl_mode.AutoPostBack = true;
|
||||
|
||||
// currentVersionTitle.Text = currentDoc.Text;
|
||||
// currentVersionMeta.Text = Services.TextService.Localize("content/createDate") + ": " + currentDoc.VersionDate.ToShortDateString() + " " + currentDoc.VersionDate.ToShortTimeString();
|
||||
|
||||
// if (!IsPostBack) {
|
||||
// allVersions.Items.Add(new ListItem(Services.TextService.Localize("rollback/selectVersion")+ "...", ""));
|
||||
|
||||
// foreach (DocumentVersionList dl in currentDoc.GetVersions())
|
||||
// {
|
||||
// //we don't need to show the current version
|
||||
// if (dl.Version == currentDoc.Version)
|
||||
// continue;
|
||||
//
|
||||
// allVersions.Items.Add(new ListItem(dl.Text + " (" + Services.TextService.Localize("content/createDate") + ": " + dl.Date.ToShortDateString() + " " + dl.Date.ToShortTimeString() + ")", dl.Version.ToString()));
|
||||
// }
|
||||
// Button1.Text = Services.TextService.Localize("actions/rollback");
|
||||
// }
|
||||
// }
|
||||
// protected void doRollback_Click(object sender, System.EventArgs e)
|
||||
// {
|
||||
// if (allVersions.SelectedValue.Trim() != "")
|
||||
// {
|
||||
// Document d = new Document(int.Parse(helper.Request("nodeId")));
|
||||
// d.RollBack(new Guid(allVersions.SelectedValue), Security.CurrentUser);
|
||||
|
||||
// BusinessLogic.Log.Add(BusinessLogic.LogTypes.RollBack, Security.CurrentUser, d.Id, "Version rolled back to revision '" + allVersions.SelectedValue + "'");
|
||||
|
||||
// Document rollback = new Document(d.Id, new Guid(allVersions.SelectedValue));
|
||||
// feedBackMsg.type = global::Umbraco.Web._Legacy.Controls.Feedback.feedbacktype.success;
|
||||
// string[] vars = {rollback.Text, rollback.VersionDate.ToLongDateString()};
|
||||
|
||||
// feedBackMsg.Text = ui.Text("rollback", "documentRolledBack", vars, new global::umbraco.BusinessLogic.User(0)) + "</p><p><a href='#' onclick='" + ClientTools.Scripts.CloseModalWindow() + "'>" + Services.TextService.Localize("closeThisWindow") + "</a>";
|
||||
// diffPanel.Visible = false;
|
||||
// pl_buttons.Visible = false;
|
||||
//
|
||||
// ClientTools.ReloadLocationIfMatched(string.Format("/content/content/edit/{0}", d.Id));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace umbraco.presentation.dialogs {
|
||||
|
||||
|
||||
public partial class rollBack {
|
||||
|
||||
/// <summary>
|
||||
/// JsInclude1 control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::ClientDependency.Core.Controls.JsInclude JsInclude1;
|
||||
|
||||
/// <summary>
|
||||
/// feedBackMsg control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web._Legacy.Controls.Feedback feedBackMsg;
|
||||
|
||||
/// <summary>
|
||||
/// pp_selectVersion control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web._Legacy.Controls.Pane pp_selectVersion;
|
||||
|
||||
/// <summary>
|
||||
/// pp_currentVersion control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_currentVersion;
|
||||
|
||||
/// <summary>
|
||||
/// currentVersionTitle control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Literal currentVersionTitle;
|
||||
|
||||
/// <summary>
|
||||
/// currentVersionMeta control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Literal currentVersionMeta;
|
||||
|
||||
/// <summary>
|
||||
/// pp_rollBackTo control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_rollBackTo;
|
||||
|
||||
/// <summary>
|
||||
/// allVersions control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.DropDownList allVersions;
|
||||
|
||||
/// <summary>
|
||||
/// pp_view control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_view;
|
||||
|
||||
/// <summary>
|
||||
/// rbl_mode control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.RadioButtonList rbl_mode;
|
||||
|
||||
/// <summary>
|
||||
/// diffPanel control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Panel diffPanel;
|
||||
|
||||
/// <summary>
|
||||
/// lt_notice control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Literal lt_notice;
|
||||
|
||||
/// <summary>
|
||||
/// propertiesCompare control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Literal propertiesCompare;
|
||||
|
||||
/// <summary>
|
||||
/// pl_buttons control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.HtmlControls.HtmlGenericControl pl_buttons;
|
||||
|
||||
/// <summary>
|
||||
/// Button1 control.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Auto-generated field.
|
||||
/// To modify move field declaration from designer file to code-behind file.
|
||||
/// </remarks>
|
||||
protected global::System.Web.UI.WebControls.Button Button1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user