Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f541b50b82 | |||
| 5767ea97d1 | |||
| e1fa7a3271 | |||
| c2e97e28ce | |||
| eda9bab02f | |||
| ce3cecbec9 | |||
| ef5faae254 | |||
| 547473e92c | |||
| 9857059906 | |||
| 7f7b054c14 | |||
| a40d95250e | |||
| 1e6004d3fa | |||
| 0671403732 | |||
| 8e00914370 | |||
| 104c369bcc | |||
| 4c02cf2921 | |||
| 74470b8e8f | |||
| de92147ec0 | |||
| c2b7d2c89d | |||
| d0fe30f08b | |||
| d22ae1244e | |||
| fa48d73630 | |||
| be332d310f | |||
| d97bbb59dc | |||
| 3702ec484b | |||
| d8d4ec1a73 | |||
| 32cb89bba1 | |||
| 863f041fc9 | |||
| 5a83719dfc | |||
| 1b1940fe6a | |||
| 7dd07b9f9c | |||
| 29652bb880 | |||
| 62b7420eaa | |||
| 333f17976b | |||
| 45981b0431 | |||
| 531ff0aab4 | |||
| ebf3fd1135 | |||
| 85157612f4 | |||
| f57205dab9 | |||
| b6ccab10b3 | |||
| b0f614c891 |
@@ -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();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Archetype.Extensions;
|
||||
using Archetype.Models;
|
||||
using ClientDependency.Core;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
@@ -95,7 +96,7 @@ namespace Archetype.PropertyEditors
|
||||
if(propDef == null || propDef.DataTypeGuid == null) continue;
|
||||
var dtd = ArchetypeHelper.Instance.GetDataTypeByGuid(Guid.Parse(propDef.DataTypeGuid));
|
||||
var propType = new PropertyType(dtd) {Alias = propDef.Alias};
|
||||
var prop = new Property(propType, (propDef.Value != null) ? propDef.Value.ToString() : null);
|
||||
var prop = new Property(propType, propDef.Value);
|
||||
var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias);
|
||||
propDef.Value = propEditor.ValueEditor.ConvertDbToString(prop, propType, dataTypeService);
|
||||
}
|
||||
@@ -135,7 +136,7 @@ namespace Archetype.PropertyEditors
|
||||
{
|
||||
var dtd = ArchetypeHelper.Instance.GetDataTypeByGuid(Guid.Parse(propDef.DataTypeGuid));
|
||||
var propType = new PropertyType(dtd) {Alias = propDef.Alias};
|
||||
var prop = new Property(propType, (propDef.Value != null) ? propDef.Value.ToString() : null);
|
||||
var prop = new Property(propType, propDef.Value);
|
||||
var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias);
|
||||
propDef.Value = propEditor.ValueEditor.ConvertDbToEditor(prop, propType, dataTypeService);
|
||||
}
|
||||
@@ -215,7 +216,7 @@ namespace Archetype.PropertyEditors
|
||||
var propData = new ContentPropertyData(propDef.Value, preValues, additionalData);
|
||||
var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias);
|
||||
// make sure to send the current property value (if any) to the PE ValueEditor
|
||||
propDef.Value = propEditor.ValueEditor.ConvertEditorToDb(propData, currentProperty != null ? (currentProperty.Value != null) ? currentProperty.Value.ToString() : null : null);
|
||||
propDef.Value = propEditor.ValueEditor.ConvertEditorToDb(propData, currentProperty != null ? currentProperty.Value : null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -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
@@ -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
@@ -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."
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user