Compare commits

...

41 Commits

Author SHA1 Message Date
kgiszewski f541b50b82 Merge branch 'master' into pr/356
Conflicts:
	app/Umbraco/Umbraco.Archetype/Models/ArchetypePreValue.cs
	app/controllers/config.controller.js
	app/views/archetype.config.fieldset.dialog.html
2016-05-13 10:07:18 -04:00
Nicholas-Westby 5767ea97d1 Workaround for .sortable("instance") not working on older versions of Umbraco. 2016-05-11 21:19:09 -07:00
Nicholas-Westby e1fa7a3271 Fixed recursion error and tidied up validation logic. 2016-05-07 15:48:39 -07:00
Nicholas-Westby c2e97e28ce Disable sortables for Archetypes that can't have the currently dragged fieldset dropped onto them. 2016-05-07 14:54:30 -07:00
Nicholas-Westby eda9bab02f Ensure the property alias is unique when dragging between Archetypes. 2016-05-07 12:45:34 -07:00
Nicholas-Westby ce3cecbec9 Fix for incorrect validation errors. 2016-05-05 21:52:03 -07:00
Nicholas-Westby ef5faae254 Allow the last fieldset to be dropped back to its origin list. 2016-05-05 21:18:08 -07:00
Nicholas-Westby 547473e92c Account for the possibility that the "tinyMCE" variable may not exist. 2016-05-05 20:20:44 -07:00
Nicholas-Westby 9857059906 Ensured the temporary ID is unique when cloning fieldsets. 2016-05-05 20:05:20 -07:00
Nicholas-Westby 7f7b054c14 Clear errors on the target Archetype too. 2016-05-04 21:28:16 -07:00
Nicholas-Westby a40d95250e Clear out errors of nested Archetypes on drop. 2016-05-04 21:15:36 -07:00
Nicholas-Westby 1e6004d3fa Another RTE fix.
Improved handling of validations when moving fieldsets (recursively mark Archetypes as valid).
2016-05-04 20:58:26 -07:00
Nicholas-Westby 0671403732 Fix for rich text editors when dragging between Archetypes. 2016-05-03 21:59:04 -07:00
kjac 8e00914370 More code style fixes 2016-05-03 20:17:45 +02:00
kjac 104c369bcc Fix some code style issues 2016-05-03 20:15:23 +02:00
kjac 4c02cf2921 Merge remote-tracking branch 'refs/remotes/origin/master' into allowed-member-groups 2016-05-03 19:44:59 +02:00
kjac 74470b8e8f Merge remote-tracking branch 'refs/remotes/imulus/master' 2016-05-03 19:44:23 +02:00
Nicholas-Westby de92147ec0 WIP: Attempting to fix RTE when dragging between fieldsets (still needs some work). 2016-05-01 21:23:02 -07:00
Nicholas-Westby c2b7d2c89d Allowed rich text editors to be dragged between Archetypes. 2016-05-01 18:27:01 -07:00
Nicholas-Westby d0fe30f08b WIP: Better handling of validation errors when dragging fieldsets between Archetypes.
Added "jsonSnapshot" (utility function for troubleshooting).
Added "tolerance" (my hope is that will help drag operations act nicer with expanded fieldsets).
2016-05-01 17:33:32 -07:00
Nicholas-Westby d22ae1244e Merge remote-tracking branch 'origin/master' into feature/355-nested-dragging 2016-04-30 18:18:14 -07:00
Nicholas-Westby fa48d73630 Only allow cross-Archetype sorting of a single fieldset if it can be removed. 2016-04-28 20:32:07 -07:00
Nicholas-Westby be332d310f Add a top margin of 5px to empty Archetype drop zone. 2016-04-28 08:24:06 -07:00
kjac d97bbb59dc Merge remote-tracking branch 'refs/remotes/origin/master' into allowed-member-groups 2016-04-27 21:00:11 +02:00
kjac 3702ec484b Merge remote-tracking branch 'refs/remotes/imulus/master' 2016-04-27 20:59:42 +02:00
Nicholas-Westby d8d4ec1a73 Improved terminology (fieldsets are dragged across Archetypes, not across fieldsets). 2016-04-27 08:21:16 -07:00
Nicholas-Westby 32cb89bba1 Made cross-field dragging opt-in. 2016-04-26 21:40:19 -07:00
Nicholas-Westby 863f041fc9 Fixed issue when dragging newly created fieldset between Archetypes.
Ensured that fieldset aliases are compared when judging compatibility during drag.
2016-04-26 21:14:33 -07:00
Nicholas-Westby 5a83719dfc Performance optimization via short-circuiting. 2016-04-26 05:57:08 -07:00
Nicholas-Westby 1b1940fe6a Tamed a rogue comment. 2016-04-26 05:54:56 -07:00
Nicholas-Westby 7dd07b9f9c When dragging between Archetypes, only allow the operation when a configured fieldset matches the dragged fieldset. 2016-04-25 21:05:36 -07:00
Nicholas-Westby 29652bb880 Removed a parameter I never ended up using. 2016-04-24 21:54:05 -07:00
Nicholas-Westby 62b7420eaa Prevent dragging when can't add to or remove from list of fieldsets.
Allow dragging into an empty list of fieldsets.
2016-04-24 21:38:59 -07:00
Nicholas-Westby 333f17976b Enable nested dragging. 2016-04-24 17:57:36 -07:00
kjac 45981b0431 Handle lazy load of moment.js 2016-04-23 15:30:14 +02:00
kjac 531ff0aab4 Merge remote-tracking branch 'refs/remotes/origin/fix-publishing-for-collapsed-fieldsets' into allowed-member-groups
# Conflicts:
#	app/views/archetype.default.html
2016-04-23 15:25:33 +02:00
kjac ebf3fd1135 Clean up duplicate code after merge 2016-04-04 21:32:24 +02:00
kjac 85157612f4 Merge remote-tracking branch 'refs/remotes/origin/fix-publishing-for-collapsed-fieldsets' into allowed-member-groups 2016-04-04 21:31:38 +02:00
kjac f57205dab9 More member groups implementation 2016-04-04 21:21:02 +02:00
kjac b6ccab10b3 Merge remote-tracking branch 'refs/remotes/imulus/master' into allowed-member-groups
# Conflicts:
#	app/less/archetype.less
2016-04-04 20:37:15 +02:00
kjac b0f614c891 Allowed member groups PoC 2016-03-23 15:08:07 +01:00
17 changed files with 524 additions and 59 deletions
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Security;
using Newtonsoft.Json;
using Umbraco.Core;
using System;
@@ -29,6 +30,9 @@ namespace Archetype.Models
[JsonProperty("expireDate")]
public DateTime? ExpireDate { get; set; }
[JsonProperty("allowedMemberGroups")]
public string AllowedMemberGroups { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ArchetypeFieldsetModel"/> class.
/// </summary>
@@ -139,11 +143,26 @@ namespace Archetype.Models
/// Is this fieldset disabled, either explicitly or by other means?
/// </summary>
/// <returns>true if this fieldset is disabled, false otherwise</returns>
internal bool IsDisabled()
internal bool IsAvailable()
{
return Disabled
// explicitly disabled or implicitly disabled through publishing?
var disabled = Disabled
|| (ReleaseDate.HasValue && ReleaseDate > DateTime.UtcNow)
|| (ExpireDate.HasValue && DateTime.UtcNow > ExpireDate);
if(disabled)
{
// yes - the fieldset is not available
return false;
}
// limitation on member group access?
if(string.IsNullOrEmpty(AllowedMemberGroups))
{
// no - the fieldset is available
return true;
}
// maybe - the fieldset is available if the current member is a member of the configured member groups
var currentUserGroups = Roles.GetRolesForUser() ?? new string[0];
return currentUserGroups.ContainsAny(AllowedMemberGroups.Split(','));
}
#endregion
@@ -32,7 +32,7 @@ namespace Archetype.Models
/// </returns>
public IEnumerator<ArchetypeFieldsetModel> GetEnumerator()
{
return this.Fieldsets.Where(f => f.IsDisabled() == false).GetEnumerator();
return this.Fieldsets.Where(f => f.IsAvailable()).GetEnumerator();
}
//possibly obsolete?
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
@@ -34,6 +34,12 @@ namespace Archetype.Models
[JsonProperty("enablePublishing")]
public bool EnablePublishing { get; set; }
[JsonProperty("enableCrossDragging")]
public bool EnableCrossDragging { get; set; }
[JsonProperty("enableMemberGroups")]
public bool EnableMemberGroups { get; set; }
[JsonProperty("hideFieldsetControls")]
public bool HideFieldsetControls { get; set; }
@@ -98,7 +98,7 @@ namespace Archetype.Models
public bool IsDraft
{
get { return _fieldset.IsDisabled(); }
get { return _fieldset.IsAvailable() == false; }
}
public PublishedItemType ItemType
@@ -19,7 +19,7 @@ namespace Archetype.Models
var count = archetype.Fieldsets.Count();
_items = archetype.Fieldsets
.Where(x => x.IsDisabled() == false)
.Where(x => x.IsAvailable())
.Select(x => new ArchetypePublishedContent(x, this))
.ToArray();
}
+1 -1
View File
@@ -6,7 +6,7 @@ angular.module("umbraco").controller("Imulus.ArchetypeConfigController", functio
//define empty items
var newPropertyModel = '{"alias": "", "remove": false, "collapse": false, "label": "", "helpText": "", "dataTypeGuid": "0cc0eba1-9960-42c9-bf9b-60e150b429ae", "value": ""}';
var newFieldsetModel = '{"alias": "", "remove": false, "collapse": false, "labelTemplate": "", "icon": "", "label": "", "properties": [' + newPropertyModel + '], "group": null}';
var defaultFieldsetConfigModel = JSON.parse('{"showAdvancedOptions": false, "startWithAddButton": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "maxFieldsets": null, "enableCollapsing": true, "enableCloning": false, "enableDisabling": true, "enableDeepDatatypeRequests": false, "enablePublishing": false, "fieldsets": [' + newFieldsetModel + '], "fieldsetGroups": []}');
var defaultFieldsetConfigModel = JSON.parse('{"showAdvancedOptions": false, "startWithAddButton": false, "hideFieldsetToolbar": false, "enableMultipleFieldsets": false, "hideFieldsetControls": false, "hidePropertyLabel": false, "maxFieldsets": null, "enableCollapsing": true, "enableCloning": false, "enableDisabling": true, "enableDeepDatatypeRequests": false, "enablePublishing": false, "enableMemberGroups": false, "enableCrossDragging": false, "fieldsets": [' + newFieldsetModel + '], "fieldsetGroups": []}');
//ini the model
$scope.model.value = $scope.model.value || defaultFieldsetConfigModel;
+300 -37
View File
@@ -1,6 +1,9 @@
{{VERSION}}
angular.module("umbraco").controller("Imulus.ArchetypeController", function ($scope, $http, $filter, assetsService, angularHelper, notificationsService, $timeout, fileManager, entityResource, archetypeService, archetypeLabelService, archetypeCacheService, archetypePropertyEditorResource) {
// Variables.
var draggedParent;
//$scope.model.value = "";
$scope.model.hideLabel = $scope.model.config.hideLabel == 1;
@@ -43,36 +46,201 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
return archetypeLabelService.getFieldsetTitle($scope, fieldsetConfigModel, fieldsetIndex);
}
var draggedRteSettings;
var rteClass = ".umb-rte textarea";
//sort config
$scope.sortableOptions = {
axis: 'y',
cursor: "move",
handle: ".handle",
tolerance: "pointer",
activate: function(ev, ui) {
// Variables.
var parentItem = ui.item.parent();
var thisElement = parentItem[0];
var targetElement = ev.target;
var targetItem = angular.element(targetElement);
// The source of the drag can always be dropped back onto.
if (targetElement === thisElement) {
return;
}
// Variables.
var sourceScope = ui.item.scope();
var targetScope = targetItem.scope();
var valid = canMove(sourceScope, targetScope);
// If the sortable can't be moved to the target Archetype, disable
// the target Archetype's sortable temporarily.
if (!valid) {
var targetSortable = getSortableWidgetInstance(targetItem);
targetSortable.disable();
parentItem.sortable("refresh");
archetypeService.rememberDisabledSortable(targetSortable);
}
},
start: function(ev, ui) {
draggedRteSettings = {};
$(rteClass, ui.item.parent()).each(function () {
var id = $(this).attr("id");
draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings;
tinymce.execCommand('mceRemoveEditor', false, id);
$(this).css("visibility", "hidden");
archetypeService.storeEditors(ui.item.parent());
$scope.$apply(function() {
draggedParent = ui.item.parent();
draggedParent.scope().doingSort = true;
});
},
update: function (ev, ui) {
// Variables.
var targetScope = ui.item.sortable.droptarget.scope();
var sourceScope = ui.item.scope();
var sameScope = sourceScope === targetScope;
var sourceIndex = ui.item.sortable.index;
// Special constraints for when moving between Archetypes.
// If sourceScope is populated, we are in the first of the two updates (when
// moving between lists, ui-sortable calls the update function twice).
if (sourceScope && !sameScope) {
// Variables.
var valid = canMove(sourceScope, targetScope);
// If update isn't allowed, cancel the drag operation.
if (!valid) {
ui.item.sortable.cancel();
return;
}
// Clear the validations for this item.
clearValidations(ui.item.parent());
clearValidations(ui.item.sortable.droptarget);
// Reset "isValid" on the properties and fieldsets.
var fieldsetGroups = [
$scope.model.value.fieldsets,
targetScope.model.value.fieldsets
];
_.each(fieldsetGroups, function(fieldsets) {
recurseProperties(function(property, fieldset) {
property.isValid = true;
fieldset.isValid = true;
}, fieldsets);
});
// Move the activated fieldset to the target Archetype.
var movedFieldset = $scope.model.value.fieldsets[sourceIndex];
var loadedIndex = $scope.loadedFieldsets.indexOf(movedFieldset);
if (loadedIndex >= 0) {
$scope.loadedFieldsets.splice(loadedIndex, 1);
if (targetScope.loadedFieldsets.indexOf(movedFieldset) < 0) {
targetScope.loadedFieldsets.push(movedFieldset);
}
}
}
// Set scope dirty.
$scope.setDirty();
},
stop: function (ev, ui) {
$(rteClass, ui.item.parent()).each(function () {
var id = $(this).attr("id");
draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings;
tinyMCE.execCommand("mceRemoveEditor", false, id);
tinyMCE.init(draggedRteSettings[id]);
});
// Done sorting.
draggedParent.scope().doingSort = false;
// Enable disabled sortables.
archetypeService.enableSortables();
// Restore rich text editors.
var parent = null;
var target = ui.item.sortable.droptarget;
if (target) {
parent = target.parent();
}
archetypeService.restoreEditors(parent);
}
};
// Clears the Angular validations in an Archetype.
function clearValidations(el) {
var combined = $(".archetypeSortable", el).add(el);
combined.each(function(index, item) {
var $item = angular.element(item);
var cont = $item.controller("ngModel");
var err = cont.$error;
var keys = Object.keys(err);
for (var i = 0; i < keys.length; i++) {
cont.$setValidity(keys[i], true);
}
});
}
// Enable cross-archetype dragging?
if ($scope.model.config.enableCrossDragging) {
$scope.sortableOptions.connectWith = ".archetypeSortable:not(.invalid)";
}
// Checks if the specified model's properties match all of the properties in any of
// the specified fieldsets, while also checking if the fieldset aliases match.
// This is an indicator of the compatibility of a fieldset model with a collection
// of fieldset configurations.
function modelMatchesAnyFieldset(model, fieldsets) {
// Loop through fieldsets to find a match.
for (var i = 0; i < fieldsets.length; i++) {
var fieldset = fieldsets[i];
// Confirm that this configured fielset contains exactly
// the same properties as those that were supplied.
var valid =
// Does the alias match?
model.alias === fieldset.alias &&
// Does this fieldset have all the properties?
arePropertiesSubset(model.properties, fieldset.properties) &&
// Does the property array have all the fieldset properties?
arePropertiesSubset(fieldset.properties, model.properties);
// Match found?
if (valid) {
return true;
}
}
// No match found.
return false;
}
// Confirms that an array of properties is a subset of another array of properties.
function arePropertiesSubset(subset, superset) {
// Loop through the subset of properties.
for (var j = 0; j < subset.length; j++) {
var subProperty = subset[j];
var matchedProp = false;
// Loop through the superset to find a matching property from the subset.
for (var k = 0; k < superset.length; k++) {
var superProperty = superset[k];
if (superProperty.alias === subProperty.alias &&
superProperty.dataTypeGuid === subProperty.dataTypeGuid) {
matchedProp = true;
break;
}
}
// If no matching property could be found, the array is not a subset.
if (!matchedProp) {
return false;
}
}
// The array is a subset.
return true;
}
//handles a fieldset add
$scope.openFieldsetPicker = function ($index, event) {
if ($scope.canAdd() == false) {
@@ -176,6 +344,13 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
if(newFieldset) {
// Regenerate the temporary ID on each nested fieldset.
// This is done because no two fieldsets should have the same
// temporary ID.
recurseProperties(function(property) {
archetypeService.ensureTemporaryId(property, true);
}, [newFieldset]);
$scope.model.value.fieldsets.splice($index + 1, 0, newFieldset);
$scope.setDirty();
@@ -196,18 +371,20 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
$scope.canAdd = function () {
if ($scope.model.config.maxFieldsets)
{
return countVisible() < $scope.model.config.maxFieldsets;
var visibleCount = countVisible();
var maxFieldsets = $scope.model.config.maxFieldsets;
return visibleCount < maxFieldsets;
}
return true;
}
};
//helper that returns if an item can be removed
$scope.canRemove = function () {
return countVisible() > 1
|| ($scope.model.config.maxFieldsets == 1 && $scope.model.config.fieldsets.length > 1)
|| $scope.model.config.startWithAddButton;
}
};
$scope.canClone = function () {
@@ -226,8 +403,16 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
//helper that returns if an item can be sorted
$scope.canSort = function ()
{
return countVisible() > 1;
}
// Sorting can occur if there are multiple fieldsets, or if there is only one
// fieldset that can be removed (in which case it can be sorted into an entirely
// different Archetype).
return countVisible() > 1 || $scope.canRemove();
};
//helper that returns if an item is the last and it's being sorted.
$scope.sortingLastItem = function() {
return $scope.doingSort && $scope.model.value.fieldsets.length <= 1;
};
//helper that returns if an item can be disabled
$scope.canDisable = function () {
@@ -246,10 +431,14 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
return $scope.model.config.enablePublishing;
}
$scope.canUseMemberGroups = function() {
return $scope.model.config.enableMemberGroups;
}
//helper that returns if the "misc fieldset configuration" section should be visible
$scope.canConfigure = function () {
// currently the only thing in the "misc fieldset configuration" section is the publishing setup
return $scope.canPublish();
// currently the "misc fieldset configuration" section contains the publishing and the member groups setup
return $scope.canPublish() || $scope.canUseMemberGroups();
}
$scope.showDisableIcon = function (fieldset) {
@@ -338,6 +527,12 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
view: "datepicker",
value: fromUtc(fieldset.expireDate)
};
// create model for allowed member groups
fieldset.allowedMemberGroupsModel = {
alias: _.uniqueId("archetypeAllowedMemberGroups_"),
view: "membergrouppicker",
value: fieldset.allowedMemberGroups
};
}
//helper to get the correct fieldset from config
@@ -521,24 +716,21 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
//helper to lookup validity when given a fieldset
$scope.getFieldsetValidity = function (fieldset) {
if (fieldset.isValid == false) {
return false;
}
// recursive validation of nested fieldsets
var nestedFieldsetsValid = true;
_.each(fieldset.properties, function (property) {
if (property != null && property.value != null && property.propertyEditorAlias == "Imulus.Archetype") {
_.each(property.value.fieldsets, function (inner) {
if ($scope.getFieldsetValidity(inner) == false) {
nestedFieldsetsValid = false;
}
});
// Variables.
var valid = true;
// Recursive validation of nested fieldsets.
recurseFieldsets(function(item) {
if (item.isValid === false) {
valid = false;
}
});
}, [fieldset]);
return nestedFieldsetsValid;
}
// Were all the nested fieldsets valid?
return valid;
};
// helper to force the current form into the dirty state
$scope.setDirty = function () {
@@ -581,6 +773,8 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
// extract the publish configuration from the fieldsets (and convert local datetimes to UTC)
fieldset.releaseDate = toUtc(fieldset.releaseDateModel.value);
fieldset.expireDate = toUtc(fieldset.expireDateModel.value);
// extract the allowed member groups
fieldset.allowedMemberGroups = fieldset.allowedMemberGroupsModel.value;
});
});
@@ -597,4 +791,73 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
}
return moment(moment.utc(date).toDate()).format("YYYY-MM-DD HH:mm:ss")
}
// Serializes and deserializes an item to return a snapshot of that item (e.g., so it is not
// changed before being inspected). Useful when troubleshooting.
// Modified from: http://stackoverflow.com/a/11616993/2052963
function jsonSnapshot(item) {
var cache = [];
var stringItem = JSON.stringify(item, function(key, value) {
if (typeof value === "object" && value !== null) {
if (cache.indexOf(value) !== -1) {
return "[Removed Circular Reference Item]";
}
cache.push(value);
}
return value;
});
return JSON.parse(stringItem);
}
// Recursively processes each Archetype fieldset.
function recurseFieldsets(fn, fieldsets) {
if (!fieldsets || !fieldsets.length) {
return;
}
_.each(fieldsets, function(fieldset) {
fn(fieldset);
_.each(fieldset.properties, function (property) {
if (property != null && property.value != null && property.propertyEditorAlias === "Imulus.Archetype") {
recurseFieldsets(fn, property.value.fieldsets);
}
});
});
}
// Recursively processes each Archetype fieldset property.
function recurseProperties(fn, fieldsets) {
recurseFieldsets(function(fieldset) {
_.each(fieldset.properties, function (property) {
fn(property, fieldset);
});
}, fieldsets);
}
// Indicates whether or not the fieldset can be moved between the source and target scope.
// The source scope is the scope of the fieldset being dragged.
// The target scope is the scope of the Archetype to drop into.
function canMove(sourceScope, targetScope) {
var targetFieldsets = targetScope.model.config.fieldsets;
var model = sourceScope.fieldsetConfigModel;
var canRemove = sourceScope.canRemove();
var canAdd = targetScope.canAdd();
var valid = canRemove && canAdd;
valid = valid && modelMatchesAnyFieldset(model, targetFieldsets);
return valid;
}
// This is an alternative to: element.sortable("instance")
// Workaround for this issue: https://github.com/imulus/Archetype/pull/356#issuecomment-218527910
// Snagged from this pull request: https://github.com/angular-ui/ui-sortable/pull/319
// More technical details here: https://github.com/angular-ui/ui-sortable/issues/316
function getSortableWidgetInstance(element) {
// this is a fix to support jquery-ui prior to v1.11.x
// otherwise we should be using `element.sortable('instance')`
var data = element.data('ui-sortable');
if (data && typeof data === 'object' && data.widgetFullName === 'ui-sortable') {
return data;
}
return null;
}
});
+8 -2
View File
@@ -72,8 +72,14 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
scope.renderModel = {};
scope.model.value = archetypeService.getFieldsetProperty(scope).value;
//init the property editor state
archetypeService.getFieldsetProperty(scope).editorState = {};
// init the property editor state (while ensuring the temporary ID is retained
// from any prior initializations).
var fieldsetProperty = archetypeService.getFieldsetProperty(scope);
var oldState = fieldsetProperty.editorState;
archetypeService.ensureTemporaryId(fieldsetProperty);
if (oldState) {
fieldsetProperty.editorState.temporaryId = oldState.temporaryId;
}
//set the config from the prevalues
scope.model.config = config;
+5 -1
View File
@@ -51,5 +51,9 @@
"publishHelpText": "Konfigurér udgivelses- og udløbsdato for dette element",
"publishReleaseDate": "Udgivelsesdato",
"publishExpireDate": "Udløbsdato",
"settings": "Indstillinger"
"settings": "Indstillinger",
"enableMemberGroups": "Aktivér medlems-grupper",
"enableMemerGroupsDescription": "Tillad at fieldsets kun vises for specifikke medlems-grupper",
"allowedMemberGroups": "Tilladte medlems-grupper",
"allowedMemberGroupsHelpText": "Vælg de medlems-grupper, der skal kunne se dette element. Hvis der ikke er valgt nogen, kan alle se elementet."
}
+5 -1
View File
@@ -51,5 +51,9 @@
"publishHelpText": "Konfigurér udgivelses- og udløbsdato for dette element",
"publishReleaseDate": "Udgivelsesdato",
"publishExpireDate": "Udløbsdato",
"settings": "Indstillinger"
"settings": "Indstillinger",
"enableMemberGroups": "Aktivér medlems-grupper",
"enableMemerGroupsDescription": "Tillad at fieldsets kun vises for specifikke medlems-grupper",
"allowedMemberGroups": "Tilladte medlems-grupper",
"allowedMemberGroupsHelpText": "Vælg de medlems-grupper, der skal kunne se dette element. Hvis der ikke er valgt nogen, kan alle se elementet."
}
+2
View File
@@ -41,6 +41,8 @@
"overrideDefaultConverter":"Override Default Property Value Converter?",
"enableCloning":"Enable Cloning?",
"enableCloningDescription":"Enable Fieldset Cloning.",
"enableCrossDragging":"Enable Cross-Archetype Dragging?",
"enableCrossDraggingDescription":"Allows fieldsets to be dragged into other Archetypes.",
"fieldsetGroups":"Fieldset groups",
"fieldsetGroupsDescription":"If you have a lot of fieldsets to choose from, you may want to consider grouping them in the fieldset picker. Once you have created your groups here, a group picker will be displayed in the fieldset editor and you can assign your fieldsets to their respective groups.",
"fieldsetGroupName":"Name",
+2
View File
@@ -41,6 +41,8 @@
"overrideDefaultConverter":"Override Default Property Value Converter?",
"enableCloning":"Enable Cloning?",
"enableCloningDescription":"Enable Fieldset Cloning.",
"enableCrossDragging":"Enable Cross-Archetype Dragging?",
"enableCrossDraggingDescription":"Allows fieldsets to be dragged into other Archetypes.",
"fieldsetGroups":"Fieldset groups",
"fieldsetGroupsDescription":"If you have a lot of fieldsets to choose from, you may want to consider grouping them in the fieldset picker. Once you have created your groups here, a group picker will be displayed in the fieldset editor and you can assign your fieldsets to their respective groups.",
"fieldsetGroupName":"Name",
+2
View File
@@ -41,6 +41,8 @@
"overrideDefaultConverter":"Override Default Property Value Converter?",
"enableCloning":"Enable Cloning?",
"enableCloningDescription":"Enable Fieldset Cloning.",
"enableCrossDragging":"Enable Cross-Archetype Dragging?",
"enableCrossDraggingDescription":"Allows fieldsets to be dragged into other Archetypes.",
"fieldsetGroups":"Fieldset groups",
"fieldsetGroupsDescription":"If you have a lot of fieldsets to choose from, you may want to consider grouping them in the fieldset picker. Once you have created your groups here, a group picker will be displayed in the fieldset editor and you can assign your fieldsets to their respective groups.",
"fieldsetGroupName":"Name",
+20
View File
@@ -337,8 +337,28 @@
padding: 14px;
background-color: #f8f8f8;
overflow: hidden;
.allowedGroups {
.umb-membergrouppicker a {
i {
padding-right: 0;
font-size: inherit;
}
&:hover i {
color: #333;
}
}
}
}
}
// Necessary to ensure empty lists can be dropped onto when sorting nested fieldsets.
.archetypeSortable.archetypeEmpty {
min-height: 42px;
border: 1px dashed #dddddd;
margin-top: 5px;
}
}
.archetypeEditor, .archetypeConfig {
+115 -3
View File
@@ -1,4 +1,11 @@
angular.module('umbraco.services').factory('archetypeService', function () {
// Variables.
var draggedRteArchetype;
var rteClass = ".umb-rte textarea";
var editorSettings = {};
var disabledSortables = [];
//public
return {
//helper that returns a JS ojbect from 'value' string or the original string
@@ -44,7 +51,7 @@ angular.module('umbraco.services').factory('archetypeService', function () {
return property.alias == alias;
});
},
getUniquePropertyAlias: function (currentScope, propertyAliasParts) {
getUniquePropertyAlias: function (currentScope, propertyAliasParts, excludeUniqueId) {
if (currentScope.hasOwnProperty('fieldsetIndex') && currentScope.hasOwnProperty('property') && currentScope.hasOwnProperty('propertyConfigIndex'))
{
var currentPropertyAlias = "f" + currentScope.fieldsetIndex + "-" + currentScope.property.alias + "-p" + currentScope.propertyConfigIndex;
@@ -57,9 +64,15 @@ angular.module('umbraco.services').factory('archetypeService', function () {
}
if (currentScope.$parent)
this.getUniquePropertyAlias(currentScope.$parent, propertyAliasParts);
this.getUniquePropertyAlias(currentScope.$parent, propertyAliasParts, true);
return _.unique(propertyAliasParts).reverse().join("-");
var reversed = _.unique(propertyAliasParts).reverse();
if (!excludeUniqueId) {
reversed.push(_.uniqueId("u-"));
}
return reversed.join("-");
},
getFieldset: function(scope) {
return scope.archetypeRenderModel.fieldsets[scope.fieldsetIndex];
@@ -99,6 +112,105 @@ angular.module('umbraco.services').factory('archetypeService', function () {
// it's the Umbraco way to hide the invalid state when altering an invalid property, even if the new value isn't valid either
property.isValid = true;
this.setFieldsetValidity(fieldset);
},
// This stores the rich text editors in all Archetypes (e.g., during a drag operation).
// Typically, the editors will be restored after the drag completes.
storeEditors: function (element) {
// If there are not tinyMCE editors, this may be undefined.
if (typeof tinyMCE === "undefined") {
return;
}
// Variables.
var self = this;
draggedRteArchetype = element;
// Empty the stored settings.
editorSettings = {};
// For fast lookups, store each editor by the element ID.
var editorsById = {};
for (var i = 0; i < tinyMCE.editors.length; i++) {
var tempEditor = tinyMCE.editors[i];
editorsById[tempEditor.id] = tempEditor;
}
// Process each rich text editor.
$(rteClass).each(function() {
// Variables.
var id = $(this).attr("id");
var editor = editorsById[id];
// Get the property's temporary ID.
var scope = angular.element(this).scope().$parent;
var property = self.getFieldsetProperty(scope);
var tempId = property.editorState.temporaryId;
// Store the editor settings by the temporary ID?
if (editor && editor.settings) {
editorSettings[tempId] = editor.settings;
}
});
},
// This restores the rich text editors in the specified Archetype
// (e.g., after a drop drop operation).
restoreEditors: function(element) {
// If there are not tinyMCE editors, this may be undefined.
if (typeof tinyMCE === "undefined") {
return;
}
// Variables.
var bothElements = element
? element.add(draggedRteArchetype)
: draggedRteArchetype;
var self = this;
// Process each RTE in both Archetypes.
$(rteClass, bothElements).each(function () {
// Variables.
var id = $(this).attr("id");
// Get the stored editor settings.
var scope = angular.element(this).scope().$parent;
var property = self.getFieldsetProperty(scope);
var tempId = property.editorState.temporaryId;
var settings = editorSettings[tempId];
// Remove and reinitialize the editor.
tinyMCE.execCommand("mceRemoveEditor", false, id);
tinyMCE.init(settings);
});
},
// Ensures the specified property has a temporary ID in its editor state,
// optionally forcing one to be regenerated (if specified).
ensureTemporaryId: function(property, regenerateId) {
if (!property.editorState) {
property.editorState = {};
}
var editorState = property.editorState;
if (!editorState.temporaryId || regenerateId) {
editorState.temporaryId = _.uniqueId("property-temp-id-");
}
},
// Remembers a sortable that has been disabled (so it can be enabled later).
rememberDisabledSortable: function(sortable) {
disabledSortables.push(sortable);
},
// Enables all of the sortables that were disabled.
enableSortables: function() {
_.each(disabledSortables, function(sortable) {
sortable.enable();
});
disabledSortables = [];
}
}
});
@@ -30,6 +30,12 @@
<div>
<label for="archetypeAdvancedOptionsPublishing"><input type="checkbox" id="archetypeAdvancedOptionsPublishing" ng-model="dialogData.model.enablePublishing" /><archetype-localize key="enablePublishing">Enable Fieldset Publishing?</archetype-localize><small><archetype-localize key="enablePublishingDescription">Allows fieldsets to be automatically published and/or unpublished.</archetype-localize></small></label>
</div>
<div>
<label for="archetypeAdvancedOptionsCrossDragging"><input type="checkbox" id="archetypeAdvancedOptionsCrossDragging" ng-model="dialogData.model.enableCrossDragging" /><archetype-localize key="enableCrossDragging">Enable Cross-Archetype Dragging?</archetype-localize><small><archetype-localize key="enableCrossDraggingDescription">Allows fieldsets to be dragged into other Archetypes.</archetype-localize></small></label>
</div>
<div>
<label for="archetypeAdvancedOptionsMemberGroups"><input type="checkbox" id="archetypeAdvancedOptionsMemberGroups" ng-model="dialogData.model.enableMemberGroups" /><archetype-localize key="enableMemberGroups">Enable Member Group Access?</archetype-localize><small><archetype-localize key="enableMemerGroupsDescription">Allows fieldsets to be displayed for specified member groups only.</archetype-localize></small></label>
</div>
<h4><archetype-localize key="multipleFieldsets">Multiple fieldsets</archetype-localize></h4>
<div>
+27 -8
View File
@@ -1,7 +1,17 @@
{{VERSION_HTML}}
<div class="archetypeEditor ng-class:model.config.customCssClass">
<textarea class="archetypeDeveloperModel" ng-show="model.config.developerMode" ng-model="model.value"></textarea>
<ul ui-sortable="sortableOptions" ng-model="model.value.fieldsets" ng-show="!showAddButton()">
<!-- "Add" button. -->
<div ng-show="showAddButton()">
<a class="archetypeAddButton" ng-click="openFieldsetPicker(0, $event)" prevent-default>
<i class="icon icon-add"></i>
<archetype-localize key="addFieldset">Add an item</archetype-localize>
</a>
</div>
<!-- Fieldsets. -->
<ul ui-sortable="sortableOptions" class="archetypeSortable" ng-class="{archetypeEmpty: model.value.fieldsets.length === 0 || sortingLastItem()}" ng-model="model.value.fieldsets">
<li ng-repeat="fieldset in model.value.fieldsets">
<fieldset ng-class="['archetype-fieldset-' + fieldset.alias, (getFieldsetValidity(fieldset) == false ? 'archetypeFieldsetError' : '')]" ng-init="fieldsetConfigModel = getConfigFieldsetByAlias(fieldset.alias)">
<div class="archetypeFieldsetLabel" ng-class="{enableCollapsing: model.config.enableCollapsing}">
@@ -46,7 +56,7 @@
</div>
<div class="settings" ng-show="showSettings">
<!-- misc fieldset settings go here -->
<div ng-if="fieldset.releaseDateModel" ng-show="canPublish()">
<div ng-if="fieldset.releaseDateModel" ng-show="canPublish()" class="control-group">
<label ng-hide="model.config.hidePropertyLabels == '1'" class="control-label">
<span><archetype-localize key="publish">Publishing</archetype-localize></span>
<div class="archetypeFieldsetHelpText">
@@ -70,6 +80,19 @@
<archetype-submit-watcher active-submit-watcher="activeSubmitWatcher" load-callback="submitWatcherOnLoad" submit-callback="submitWatcherOnSubmit"></archetype-submit-watcher>
</div>
</div>
<div ng-if="fieldset.allowedMemberGroupsModel" ng-show="canUseMemberGroups()" class="control-group allowedGroups">
<label ng-hide="model.config.hidePropertyLabels == '1'" class="control-label">
<span><archetype-localize key="allowedMemberGroups">Allowed member groups</archetype-localize></span>
<div class="archetypeFieldsetHelpText">
<small><archetype-localize key="allowedMemberGroupsHelpText">Choose the member groups that should be allowed to see this item. Leave empty to allow all member groups.</archetype-localize></small>
</div>
</label>
<div ng-class="[(model.config.hidePropertyLabels == '1' ? 'controls-no-label' : 'controls')]">
<umb-editor model="fieldset.allowedMemberGroupsModel"></umb-editor>
</div>
<!-- we need a submit watcher here to make sure the allowed member groups are saved back to the model -->
<archetype-submit-watcher active-submit-watcher="activeSubmitWatcher" load-callback="submitWatcherOnLoad" submit-callback="submitWatcherOnSubmit"></archetype-submit-watcher>
</div>
</div>
</div>
</form>
@@ -77,12 +100,8 @@
</fieldset>
</li>
</ul>
<div ng-show="showAddButton()">
<a class="archetypeAddButton" ng-click="openFieldsetPicker(0, $event)" prevent-default>
<i class="icon icon-add"></i>
<archetype-localize key="addFieldset">Add an item</archetype-localize>
</a>
</div>
<!-- Fieldset picker. -->
<div class="usky-grid fieldsetPicker" ng-class="{withGroups: overlayMenu.fieldsetGroups.length > 1}" ng-if="overlayMenu.show">
<div class="cell-tools-menu" ng-style="overlayMenu.style" delayed-mouseleave="closeFieldsetPicker()" archetype-click-outside="closeFieldsetPicker()">
<h5>