Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abf6917b1c | |||
| 40ad701f7f | |||
| a8113f926c | |||
| 3c527396de | |||
| 59bf66dcd1 | |||
| c125042a86 | |||
| 85ea472129 | |||
| f541b50b82 | |||
| 5767ea97d1 | |||
| e1fa7a3271 | |||
| c2e97e28ce | |||
| eda9bab02f | |||
| ce3cecbec9 | |||
| ef5faae254 | |||
| 547473e92c | |||
| 9857059906 | |||
| 7f7b054c14 | |||
| a40d95250e | |||
| 1e6004d3fa | |||
| 0671403732 | |||
| 8e00914370 | |||
| 104c369bcc | |||
| 4c02cf2921 | |||
| 74470b8e8f | |||
| de92147ec0 | |||
| c2b7d2c89d | |||
| d0fe30f08b | |||
| d22ae1244e | |||
| fa48d73630 | |||
| fb305c9d0e | |||
| be332d310f | |||
| d97bbb59dc | |||
| 3702ec484b | |||
| d8d4ec1a73 | |||
| 32cb89bba1 | |||
| 863f041fc9 | |||
| 5a83719dfc | |||
| 1b1940fe6a | |||
| b71564bfb5 | |||
| ba8a6cf65c | |||
| 7dd07b9f9c | |||
| 29652bb880 | |||
| 62b7420eaa | |||
| 333f17976b | |||
| 45981b0431 | |||
| 531ff0aab4 | |||
| 394572a8fc | |||
| c7114b63f5 | |||
| 9704a8b591 | |||
| 0b6af3b4e1 | |||
| 4b874dc5b5 | |||
| a081d97546 | |||
| ebf3fd1135 | |||
| 85157612f4 | |||
| 6b79a508bb | |||
| f57205dab9 | |||
| b6ccab10b3 | |||
| e1f67a738b | |||
| 4410f1bb81 | |||
| 5c2a99987a | |||
| 715a2bf6d5 | |||
| d9ffb4b097 | |||
| c409922469 | |||
| e33eb863e6 | |||
| b0f614c891 | |||
| a424a7e4f3 | |||
| 75fa2bb1bb | |||
| 5585244d32 | |||
| 9336f4508a | |||
| 8f7024c32a | |||
| dea307ff8e | |||
| d69a10de48 | |||
| b93df7a4c0 | |||
| 48dbdebec5 | |||
| 65837fa74c | |||
| 29a73336e3 | |||
| 0584c4eda5 |
@@ -10,6 +10,8 @@ Install the selected <a href='https://github.com/imulus/Archetype/releases'>rele
|
||||
## Official Docs ##
|
||||
https://github.com/kgiszewski/ArchetypeManual
|
||||
|
||||
|
||||
##News and Updates##
|
||||
Follow us on Twitter https://twitter.com/ArchetypeKit
|
||||
|
||||
##Core Team##
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Security;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using System;
|
||||
@@ -23,6 +24,15 @@ namespace Archetype.Models
|
||||
[JsonProperty("id")]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[JsonProperty("releaseDate")]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[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>
|
||||
@@ -129,6 +139,32 @@ namespace Archetype.Models
|
||||
return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(propertyAlias));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is this fieldset disabled, either explicitly or by other means?
|
||||
/// </summary>
|
||||
/// <returns>true if this fieldset is disabled, false otherwise</returns>
|
||||
internal bool IsAvailable()
|
||||
{
|
||||
// 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.Disabled == 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;
|
||||
|
||||
@@ -31,6 +31,15 @@ namespace Archetype.Models
|
||||
[JsonProperty("enableDisabling")]
|
||||
public bool EnableDisabling { get; set; }
|
||||
|
||||
[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.Disabled; }
|
||||
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.Disabled == false)
|
||||
.Where(x => x.IsAvailable())
|
||||
.Select(x => new ArchetypePublishedContent(x, this))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.12.4")]
|
||||
[assembly: AssemblyFileVersion("1.12.4")]
|
||||
[assembly: AssemblyVersion("1.13.1")]
|
||||
[assembly: AssemblyFileVersion("1.13.1")]
|
||||
|
||||
@@ -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;
|
||||
@@ -175,6 +176,10 @@ namespace Archetype.PropertyEditors
|
||||
var uploadedFiles = editorValue.AdditionalData.ContainsKey("files") ? editorValue.AdditionalData["files"] as IEnumerable<ContentItemFile> : null;
|
||||
foreach (var fieldset in archetype.Fieldsets)
|
||||
{
|
||||
// make sure the publishing dates are in UTC
|
||||
fieldset.ReleaseDate = EnsureUtcDate(fieldset.ReleaseDate);
|
||||
fieldset.ExpireDate = EnsureUtcDate(fieldset.ExpireDate);
|
||||
|
||||
// assign an id to the fieldset if it has none (e.g. newly created fieldset)
|
||||
fieldset.Id = fieldset.Id == Guid.Empty ? Guid.NewGuid() : fieldset.Id;
|
||||
// find the corresponding fieldset in the current Archetype value (if any)
|
||||
@@ -237,6 +242,20 @@ namespace Archetype.PropertyEditors
|
||||
? new ArchetypePropertyEditor()
|
||||
: (PropertyEditor) new TextboxPropertyEditor();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a datetime is in UTC
|
||||
/// </summary>
|
||||
/// <param name="dateTime">The datetime</param>
|
||||
/// <returns>The datetime in UTC (or null if it's not set)</returns>
|
||||
private DateTime? EnsureUtcDate(DateTime? dateTime)
|
||||
{
|
||||
if(dateTime.HasValue == false || dateTime.Value.Kind == DateTimeKind.Utc)
|
||||
{
|
||||
return dateTime;
|
||||
}
|
||||
return new DateTime(dateTime.Value.Ticks, DateTimeKind.Utc);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -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, "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;
|
||||
|
||||
+443
-36
@@ -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) {
|
||||
@@ -148,11 +316,18 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
}
|
||||
}
|
||||
|
||||
addCustomPropertiesToFieldset(newFieldset);
|
||||
|
||||
$scope.setDirty();
|
||||
|
||||
$scope.$broadcast("archetypeAddFieldset", {index: $index, visible: countVisible()});
|
||||
|
||||
newFieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
|
||||
|
||||
// If the fieldset is not collapsed, it should be instantly loaded.
|
||||
if (!newFieldset.collapse) {
|
||||
$scope.loadedFieldsets.push(newFieldset);
|
||||
}
|
||||
|
||||
$scope.focusFieldset(newFieldset);
|
||||
}
|
||||
@@ -174,11 +349,24 @@ 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();
|
||||
|
||||
newFieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
|
||||
|
||||
// If the fieldset is not collapsed, it should be instantly loaded.
|
||||
if (!newFieldset.collapse) {
|
||||
$scope.loadedFieldsets.push(newFieldset);
|
||||
}
|
||||
|
||||
$scope.focusFieldset(newFieldset);
|
||||
}
|
||||
}
|
||||
@@ -194,18 +382,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 () {
|
||||
|
||||
@@ -224,8 +414,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 () {
|
||||
@@ -239,6 +437,70 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
///&& $scope.model.config.fieldsets.length == 1;
|
||||
}
|
||||
|
||||
//helper that returns if an item can use publishing
|
||||
$scope.canPublish = function () {
|
||||
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 "misc fieldset configuration" section contains the publishing and the member groups setup
|
||||
return $scope.canPublish() || $scope.canUseMemberGroups();
|
||||
}
|
||||
|
||||
$scope.showDisableIcon = function (fieldset) {
|
||||
if ($scope.canDisable() == false) {
|
||||
return false;
|
||||
}
|
||||
// disabled state takes precedence over publishing
|
||||
if (fieldset.disabled) {
|
||||
return true;
|
||||
}
|
||||
return $scope.isDisabledByPublishing(fieldset) == false;
|
||||
}
|
||||
|
||||
$scope.showPublishingIcon = function (fieldset) {
|
||||
if ($scope.canPublish() == false) {
|
||||
return false;
|
||||
}
|
||||
if ($scope.canDisable()) {
|
||||
// disabled state takes precedence over publishing
|
||||
if (fieldset.disabled) {
|
||||
return false;
|
||||
}
|
||||
return $scope.isDisabledByPublishing(fieldset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope.isDisabledByPublishing = function(fieldset) {
|
||||
if ($scope.canPublish() === false) {
|
||||
return false;
|
||||
}
|
||||
// NOTE: all comparison is done in local datetime
|
||||
// - that's fine because the selected local datetimes will be converted to UTC datetimes when submitted
|
||||
if (fieldset.expireDateModel && fieldset.expireDateModel.value && (moment() > moment(fieldset.expireDateModel.value))) {
|
||||
// an expired release affects the fieldset
|
||||
return true;
|
||||
}
|
||||
if (fieldset.releaseDateModel && fieldset.releaseDateModel.value && (moment(fieldset.releaseDateModel.value) > moment())) {
|
||||
// a pending release affects the fieldset
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$scope.isDisabled = function(fieldset) {
|
||||
if (fieldset.disabled) {
|
||||
return true;
|
||||
}
|
||||
return $scope.isDisabledByPublishing(fieldset);
|
||||
}
|
||||
|
||||
//helper, ini the render model from the server (model.value)
|
||||
function init() {
|
||||
$scope.model.value = removeNulls($scope.model.value);
|
||||
@@ -254,6 +516,36 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
});
|
||||
}
|
||||
|
||||
function addCustomProperties(fieldsets) {
|
||||
// make sure we have loaded moment.js before using it
|
||||
assetsService.loadJs("lib/moment/moment-with-locales.js").then(function() {
|
||||
_.each(fieldsets, function(fieldset) {
|
||||
addCustomPropertiesToFieldset(fieldset);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addCustomPropertiesToFieldset(fieldset) {
|
||||
// create models for publish configuration (utilizing the built-in datepicker data type)
|
||||
// NOTE: all datetimes must be converted from UTC to local
|
||||
fieldset.releaseDateModel = {
|
||||
alias: _.uniqueId("archetypeReleaseDate_"),
|
||||
view: "datepicker",
|
||||
value: fromUtc(fieldset.releaseDate)
|
||||
};
|
||||
fieldset.expireDateModel = {
|
||||
alias: _.uniqueId("archetypeExpireDate_"),
|
||||
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
|
||||
$scope.getConfigFieldsetByAlias = function(alias) {
|
||||
return _.find($scope.model.config.fieldsets, function(fieldset){
|
||||
@@ -273,9 +565,15 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
{
|
||||
if(typeof fieldset.collapse === "undefined")
|
||||
{
|
||||
fieldset.collapse = true;
|
||||
fieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
|
||||
}
|
||||
return fieldset.collapse;
|
||||
};
|
||||
|
||||
// added to track loaded fieldsets
|
||||
$scope.loadedFieldsets = [];
|
||||
$scope.isLoaded = function (fieldset) {
|
||||
return $scope.loadedFieldsets.indexOf(fieldset) >= 0;
|
||||
}
|
||||
|
||||
//helper for expanding/collapsing fieldsets
|
||||
@@ -300,18 +598,26 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
if(!fieldset && $scope.model.value.fieldsets.length == 1)
|
||||
{
|
||||
$scope.model.value.fieldsets[0].collapse = false;
|
||||
$scope.loadedFieldsets.push($scope.model.value.fieldsets[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(iniState && fieldset)
|
||||
{
|
||||
fieldset.collapse = !iniState;
|
||||
$scope.loadedFieldsets.push(fieldset);
|
||||
}
|
||||
}
|
||||
|
||||
//ini the fieldset expand/collapse
|
||||
$scope.focusFieldset();
|
||||
|
||||
// Fieldsets which cannot be collapsed should start expanded.
|
||||
_.each($scope.model.value.fieldsets, function(fieldset) {
|
||||
fieldset.collapse = $scope.model.config.enableCollapsing;
|
||||
});
|
||||
$scope.loadedFieldsets = _.where($scope.model.value.fieldsets, { collapse: false });
|
||||
|
||||
//developerMode helpers
|
||||
$scope.model.value.toString = stringify;
|
||||
|
||||
@@ -361,6 +667,15 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
|
||||
// reset submit watcher counter on save
|
||||
$scope.activeSubmitWatcher = 0;
|
||||
|
||||
// init loaded fieldsets tracking
|
||||
_.each($scope.model.value.fieldsets, function (fieldset) {
|
||||
fieldset.collapse = $scope.model.config.enableCollapsing ? true : false;
|
||||
});
|
||||
$scope.loadedFieldsets = _.where($scope.model.value.fieldsets, { collapse: false });
|
||||
|
||||
// create properties needed for the backoffice to work (data that is not serialized to DB)
|
||||
addCustomProperties($scope.model.value.fieldsets);
|
||||
});
|
||||
|
||||
//helper to count what is visible
|
||||
@@ -421,24 +736,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 () {
|
||||
@@ -473,4 +785,99 @@ angular.module("umbraco").controller("Imulus.ArchetypeController", function ($sc
|
||||
$scope.submitWatcherOnSubmit = function (args) {
|
||||
$scope.$broadcast("archetypeFormSubmitting", args);
|
||||
}
|
||||
|
||||
// we'll use our own "archetypeFormSubmitting" event to save custom properties, as at least some
|
||||
// of the editors store their values back to the model on the core "formSubmitting" event
|
||||
$scope.$on("archetypeFormSubmitting", function (ev, args) {
|
||||
_.each($scope.model.value.fieldsets, function (fieldset) {
|
||||
// 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;
|
||||
});
|
||||
});
|
||||
|
||||
function toUtc(date) {
|
||||
if (!date) {
|
||||
return null;
|
||||
}
|
||||
return moment(date, "YYYY-MM-DD HH:mm:ss").utc().toDate();
|
||||
}
|
||||
|
||||
function fromUtc(date) {
|
||||
if (!date) {
|
||||
return null;
|
||||
}
|
||||
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;
|
||||
@@ -93,9 +99,11 @@ angular.module("umbraco.directives").directive('archetypeProperty', function ($c
|
||||
}
|
||||
}
|
||||
|
||||
//upload, colorpicker datatype hack
|
||||
if(view.indexOf('fileupload.html') != -1 || view.indexOf('colorpicker.html') != -1) {
|
||||
//hacks for various built-in datatyps including upload, colorpicker and tags
|
||||
if (!scope.propertyForm) {
|
||||
scope.propertyForm = scope.form;
|
||||
}
|
||||
if (!scope.model.validation) {
|
||||
scope.model.validation = {};
|
||||
scope.model.validation.mandatory = 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
angular.module("umbraco.directives").directive('archetypeSubmitWatcher', function ($rootScope) {
|
||||
var linker = function (scope, element, attrs, ngModelCtrl) {
|
||||
// call the load callback on scope to obtain the ID of this submit watcher
|
||||
var id = scope.loadCallback();
|
||||
scope.$on("formSubmitting", function (ev, args) {
|
||||
var id = scope.loadCallback ? scope.loadCallback() : 0;
|
||||
|
||||
scope.$on("formSubmitting", function (ev, args) {
|
||||
// on the "formSubmitting" event, call the submit callback on scope to notify the Archetype controller to do it's magic
|
||||
if (id == scope.activeSubmitWatcher) {
|
||||
scope.submitCallback(args);
|
||||
@@ -22,4 +22,4 @@ angular.module("umbraco.directives").directive('archetypeSubmitWatcher', functio
|
||||
activeSubmitWatcher: '='
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -13,7 +13,7 @@ var ArchetypeSampleLabelTemplates = (function() {
|
||||
var id = value.split(",")[0];
|
||||
|
||||
if (id) {
|
||||
var entity = scope.services.archetypeLabelService.getEntityById(scope, id, args.entityType);
|
||||
var entity = scope.services.archetypeCacheService.getEntityById(scope, id, args.entityType);
|
||||
|
||||
if(entity) {
|
||||
return entity[args.propertyName];
|
||||
@@ -34,13 +34,13 @@ var ArchetypeSampleLabelTemplates = (function() {
|
||||
switch (value.type) {
|
||||
case "content":
|
||||
if(value.typeData.contentId) {
|
||||
entity = scope.services.archetypeLabelService.getEntityById(scope, value.typeData.contentId, "Document");
|
||||
entity = scope.services.archetypeCacheService.getEntityById(scope, value.typeData.contentId, "Document");
|
||||
}
|
||||
break;
|
||||
|
||||
case "media":
|
||||
if(value.typeData.mediaId) {
|
||||
entity = scope.services.archetypeLabelService.getEntityById(scope, value.typeData.mediaId, "Media");
|
||||
entity = scope.services.archetypeCacheService.getEntityById(scope, value.typeData.mediaId, "Media");
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
+12
-1
@@ -44,5 +44,16 @@
|
||||
"fieldsetGroups":"Fieldset-grupper",
|
||||
"fieldsetGroupsDescription":"Hvis du har mange fieldsets, skal du måske overveje at gruppere dem i fieldset-vælgeren. Når du først har defineret dine grupper her, vil en gruppe-vælger dukke op i fieldset-editoren, og du kan derefter vælge grupper for alle dine fieldsets.",
|
||||
"fieldsetGroupName":"Navn",
|
||||
"addFieldsetGroup":"Tilføj gruppe"
|
||||
"addFieldsetGroup":"Tilføj gruppe",
|
||||
"enablePublishing": "Aktivér fieldset-publicering?",
|
||||
"enablePublishingDescription": "Tillad automatisk udgivelse og udløb for fieldsets",
|
||||
"publish": "Publicering",
|
||||
"publishHelpText": "Konfigurér udgivelses- og udløbsdato for dette element",
|
||||
"publishReleaseDate": "Udgivelsesdato",
|
||||
"publishExpireDate": "Udløbsdato",
|
||||
"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."
|
||||
}
|
||||
|
||||
+12
-1
@@ -44,5 +44,16 @@
|
||||
"fieldsetGroups":"Fieldset-grupper",
|
||||
"fieldsetGroupsDescription":"Hvis du har mange fieldsets, skal du måske overveje at gruppere dem i fieldset-vælgeren. Når du først har defineret dine grupper her, vil en gruppe-vælger dukke op i fieldset-editoren, og du kan derefter vælge grupper for alle dine fieldsets.",
|
||||
"fieldsetGroupName":"Navn",
|
||||
"addFieldsetGroup":"Tilføj gruppe"
|
||||
"addFieldsetGroup":"Tilføj gruppe",
|
||||
"enablePublishing": "Aktivér fieldset-publicering?",
|
||||
"enablePublishingDescription": "Tillad automatisk udgivelse og udløb for fieldsets",
|
||||
"publish": "Publicering",
|
||||
"publishHelpText": "Konfigurér udgivelses- og udløbsdato for dette element",
|
||||
"publishReleaseDate": "Udgivelsesdato",
|
||||
"publishExpireDate": "Udløbsdato",
|
||||
"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",
|
||||
|
||||
+85
-10
@@ -40,6 +40,17 @@
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.caret {
|
||||
margin: 8px 1px 0 2px;
|
||||
&.caret-right {
|
||||
border-bottom: 4px solid transparent;
|
||||
border-top: 4px solid transparent;
|
||||
border-left: 4px solid #ddd;
|
||||
content: "";
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-bottom: 1px dashed #dddddd;
|
||||
padding: 0;
|
||||
@@ -64,6 +75,19 @@
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.publishOptions {
|
||||
.publishDate {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: 20px;
|
||||
|
||||
label {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.archetypeFieldsetLabel {
|
||||
@@ -115,16 +139,7 @@
|
||||
}
|
||||
|
||||
.caret {
|
||||
margin: 8px 1px 0 2px;
|
||||
position: absolute;
|
||||
|
||||
&.caret-right {
|
||||
border-bottom: 4px solid transparent;
|
||||
border-top: 4px solid transparent;
|
||||
border-left: 4px solid #ddd;
|
||||
content: "";
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -162,8 +177,19 @@
|
||||
&.icon-delete:hover {
|
||||
color: #b94a48;
|
||||
}
|
||||
&.icon-disabled {
|
||||
cursor: default;
|
||||
&:hover {
|
||||
color: #ddd;
|
||||
}
|
||||
}
|
||||
&.icon-active {
|
||||
color: #333;
|
||||
&:hover {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@@ -285,6 +311,54 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settingsWrapper {
|
||||
&:hover {
|
||||
caret {
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
.settingsHeader {
|
||||
label {
|
||||
display: inline-block;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
label {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.caret-right {
|
||||
border-left-color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
.settings {
|
||||
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 {
|
||||
@@ -340,6 +414,7 @@
|
||||
|
||||
label {
|
||||
width: 120px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.archetypeEditorControls {
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
angular.module('umbraco.services').factory('archetypeService', function () {
|
||||
|
||||
// Variables.
|
||||
var draggedRteArchetype;
|
||||
var rteClass = ".archetypeEditor .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,15 +64,23 @@ 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];
|
||||
var renderModel = scope.archetypeRenderModel;
|
||||
return renderModel ? renderModel.fieldsets[scope.fieldsetIndex] : null;
|
||||
},
|
||||
getFieldsetProperty: function (scope) {
|
||||
return this.getFieldset(scope).properties[scope.renderModelPropertyIndex];
|
||||
var fieldset = this.getFieldset(scope);
|
||||
return fieldset ? fieldset.properties[scope.renderModelPropertyIndex] : null;
|
||||
},
|
||||
setFieldsetValidity: function (fieldset) {
|
||||
// mark the entire fieldset as invalid if there are any invalid properties in the fieldset, otherwise mark it as valid
|
||||
@@ -99,6 +114,107 @@ 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 ? property.editorState.temporaryId : null;
|
||||
|
||||
// Store the editor settings by the temporary ID?
|
||||
if (editor && editor.settings && tempId) {
|
||||
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 ? property.editorState.temporaryId : null;
|
||||
var settings = editorSettings[tempId];
|
||||
|
||||
// Remove and reinitialize the editor.
|
||||
if (settings) {
|
||||
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 = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -27,6 +27,15 @@
|
||||
<div>
|
||||
<label for="archetypeAdvancedOptionsDisabling"><input type="checkbox" id="archetypeAdvancedOptionsDisabling" ng-model="dialogData.model.enableDisabling" /><archetype-localize key="enableDisabling">Enable Fieldset Disabling?</archetype-localize><small><archetype-localize key="enableDisablingDescription">Allows fieldsets to be individually enabled/disabled.</archetype-localize></small></label>
|
||||
</div>
|
||||
<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,14 +1,23 @@
|
||||
{{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}">
|
||||
<div ng-click="focusFieldset(fieldset)" class="label-sub module-label">
|
||||
<span class="caret" ng-hide="fieldset.collapse || !model.config.enableCollapsing"></span>
|
||||
<span class="caret caret-right" ng-show="fieldset.collapse && model.config.enableCollapsing"></span>
|
||||
<label ng-class="{dimmed: fieldset.disabled}">
|
||||
<span class="caret" ng-class="{'caret-right': isCollapsed(fieldset)}" ng-show="model.config.enableCollapsing"></span>
|
||||
<label ng-class="{dimmed: isDisabled(fieldset)}">
|
||||
<i class="fieldsetIcon icon ng-class:fieldsetConfigModel.icon"></i>
|
||||
<span ng-bind="getFieldsetTitle(fieldsetConfigModel, $index)"></span>
|
||||
</label>
|
||||
@@ -17,11 +26,12 @@
|
||||
<i class="icon icon-add" ng-show="canAdd()" ng-click="openFieldsetPicker($index, $event)"></i>
|
||||
<i class="icon icon-navigation handle" ng-show="canSort()"></i>
|
||||
<i class="icon icon-documents" ng-click="cloneRow($index)" ng-show="canClone()"></i>
|
||||
<i class="icon icon-power" ng-click="enableDisable(fieldset)" ng-show="canDisable()"></i>
|
||||
<i class="icon icon-power" ng-class="{'icon-active': fieldset.disabled}" ng-click="enableDisable(fieldset)" ng-show="showDisableIcon(fieldset)"></i>
|
||||
<i class="icon icon-time icon-disabled" ng-class="{'icon-active': isDisabledByPublishing(fieldset)}" ng-show="showPublishingIcon(fieldset)"></i>
|
||||
<i class="icon icon-delete" ng-click="removeRow($index)" ng-show="canRemove()"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="archetypeCollapser animate-hide" ng-hide="model.config.enableCollapsing && isCollapsed(fieldset)" ng-if="!isCollapsed(fieldset)">
|
||||
<div class="archetypeCollapser animate-hide" ng-hide="model.config.enableCollapsing && isCollapsed(fieldset)" ng-if="isLoaded(fieldset)">
|
||||
<form class="form-inline">
|
||||
<div ng-class="[('archetype-property-' + property.alias), (getPropertyValidity($parent.$index, property.alias) === false ? 'archetypePropertyError' : '')]" class="archetypeProperty control-group" ng-repeat="property in fieldsetConfigModel.properties">
|
||||
<label ng-hide="model.config.hidePropertyLabels == '1'" class="control-label" for="archetype-property-{{model.alias}}-f{{$parent.$index}}-{{property.alias}}-p{{$index}}" ng-attr-title="{{ isDebuggingEnabled || model.config.developerMode ? property.alias : null }}">
|
||||
@@ -36,17 +46,62 @@
|
||||
<archetype-submit-watcher active-submit-watcher="activeSubmitWatcher" load-callback="submitWatcherOnLoad" submit-callback="submitWatcherOnSubmit"></archetype-submit-watcher>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group archetypeProperty settingsWrapper" ng-show="canConfigure()">
|
||||
<div ng-click="showSettings = !showSettings" class="settingsHeader">
|
||||
<span class="caret" ng-show="showSettings"></span>
|
||||
<span class="caret caret-right" ng-hide="showSettings"></span>
|
||||
<label>
|
||||
<span><archetype-localize key="settings">Settings</archetype-localize></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="settings" ng-show="showSettings">
|
||||
<!-- misc fieldset settings go here -->
|
||||
<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">
|
||||
<small><archetype-localize key="publishHelpText">Configure automatic publishing and/or unpublishing of this item</archetype-localize></small>
|
||||
</div>
|
||||
</label>
|
||||
<div class="publishOptions" ng-class="[(model.config.hidePropertyLabels == '1' ? 'controls-no-label' : 'controls')]">
|
||||
<div class="publishDate">
|
||||
<label for="{{fieldset.releaseDateModel.alias}}">
|
||||
<archetype-localize key="publishReleaseDate">Publish at</archetype-localize>
|
||||
</label>
|
||||
<umb-editor model="fieldset.releaseDateModel"></umb-editor>
|
||||
</div>
|
||||
<div class="publishDate">
|
||||
<label for="{{fieldset.expireDateModel.alias}}">
|
||||
<archetype-localize key="publishExpireDate">Unpublish at</archetype-localize>
|
||||
</label>
|
||||
<umb-editor model="fieldset.expireDateModel"></umb-editor>
|
||||
</div>
|
||||
<!-- we need a submit watcher here to make sure the publish dates are saved back to their models -->
|
||||
<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>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Archetype",
|
||||
"version": "1.12.4",
|
||||
"version": "1.13.1",
|
||||
"url": "http://github.com/imulus/archetype/",
|
||||
"author": "Imulus - Kevin Giszewski - Tom Fulton - Lee Kelleher - Matt Brailsford - Kenn Jacobsen - Et. Al.",
|
||||
"authorUrl": "http://imulus.com/",
|
||||
|
||||
Reference in New Issue
Block a user