diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings.cs b/src/Umbraco.Core/Configuration/UmbracoSettings.cs index 784ebfa686..3feb6ef034 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings.cs @@ -1251,6 +1251,41 @@ namespace Umbraco.Core.Configuration } } + private static IconPickerBehaviour? _iconPickerBehaviour; + + /// + /// This configuration setting defines how to show icons in the document type editor. + /// - ShowDuplicates - Show duplicates in files and sprites. (default and current Umbraco 'normal' behaviour) + /// - HideSpriteDuplicates - Show files on disk and hide duplicates from the sprite + /// - HideFileDuplicates - Show files in the sprite and hide duplicates on disk + /// + /// MacroErrorBehaviour enum defining how to show icons in the document type editor. + public static IconPickerBehaviour IconPickerBehaviour + { + get + { + if (_iconPickerBehaviour == null) + { + try + { + var behaviour = IconPickerBehaviour.ShowDuplicates; + var value = GetKey("/settings/content/DocumentTypeIconList"); + if (value != null) + { + Enum.TryParse(value, true, out behaviour); + } + _iconPickerBehaviour = behaviour; + } + catch (Exception ex) + { + LogHelper.Error("Could not load /settings/content/DocumentTypeIconList from umbracosettings.config", ex); + _iconPickerBehaviour = IconPickerBehaviour.ShowDuplicates; + } + } + return _iconPickerBehaviour.Value; + } + } + /// /// Configuration regarding webservices /// diff --git a/src/Umbraco.Core/IconPickerBehaviour.cs b/src/Umbraco.Core/IconPickerBehaviour.cs new file mode 100644 index 0000000000..d442bbe95d --- /dev/null +++ b/src/Umbraco.Core/IconPickerBehaviour.cs @@ -0,0 +1,24 @@ +namespace Umbraco.Core +{ + public enum IconPickerBehaviour + { + /// + /// Default umbraco behavior - show duplicates in files and sprites + /// + ShowDuplicates, + + /// + /// If a file exists on disk with the same name as one in the sprite + /// then the file on disk overrules the one in the sprite, the + /// sprite icon will not be shown + /// + HideSpriteDuplicates, + + /// + /// If a file exists on disk with the same name as one in the sprite + /// then the file in the sprite overrules the one on disk, the file + /// on disk will be shown + /// + HideFileDuplicates + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 981b4cb41d..4b4efece82 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -112,6 +112,7 @@ + diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index bdb473c0b0..733ef87d0f 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -86,7 +86,7 @@ false - + inline + + + ShowDuplicates @@ -143,7 +157,7 @@ true - + WebForms diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.config b/src/Umbraco.Web.UI/config/umbracoSettings.config index c8872d1c19..17d7a33b5e 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.config @@ -87,7 +87,21 @@ - throw - Throw an exception which can be caught by the global error handler defined in Application_OnError. If no such error handler is defined then you'll see the Yellow Screen Of Death (YSOD) error page. Note the error can also be handled by the umbraco.macro.Error event, where you can log/alarm with your own code and change the behaviour per event. --> - inline + inline + + + HideFileDuplicates diff --git a/src/Umbraco.Web.UI/umbraco_client/ui/jquery.dd.js b/src/Umbraco.Web.UI/umbraco_client/ui/jquery.dd.js index decbf15e43..01e5f6112f 100644 --- a/src/Umbraco.Web.UI/umbraco_client/ui/jquery.dd.js +++ b/src/Umbraco.Web.UI/umbraco_client/ui/jquery.dd.js @@ -97,24 +97,23 @@ var t = ""; var clsName = ""; var pH = ""; //addition html - if (options.useSprite != false) { - clsName = ' ' + options.useSprite + ' ' + currentOptOption.className; - } else { - arrow = $(currentOptOption).prop("title"); - var reg = new RegExp(/^\{.*\}$/); - var isJson = reg.test(arrow); - if (options.jsonTitle == true && isJson == true) { - if (arrow.length != 0) { - var obj = eval("[" + arrow + "]"); - img = (typeof obj[0].image == "undefined") ? "" : obj[0].image; - t = (typeof obj[0].title == "undefined") ? "" : obj[0].title; - pH = (typeof obj[0].postHTML == "undefined") ? "" : obj[0].postHTML; - arrow = (img.length == 0) ? "" : ' '; - }; - } else { - arrow = (arrow.length == 0) ? "" : ' '; + + clsName = ' ' + options.useSprite + ' ' + currentOptOption.className; + arrow = $(currentOptOption).prop("title"); + var reg = new RegExp(/^\{.*\}$/); + var isJson = reg.test(arrow); + if (options.jsonTitle == true && isJson == true) { + if (arrow.length != 0) { + var obj = eval("[" + arrow + "]"); + img = (typeof obj[0].image == "undefined") ? "" : obj[0].image; + t = (typeof obj[0].title == "undefined") ? "" : obj[0].title; + pH = (typeof obj[0].postHTML == "undefined") ? "" : obj[0].postHTML; + arrow = (img.length == 0) ? "" : ' '; }; + } else { + arrow = (arrow.length == 0) ? "" : ' '; }; + var sText = $(currentOptOption).text(); var sValue = $(currentOptOption).val(); var sEnabledClass = ($(currentOptOption).prop("disabled") == true) ? "disabled" : "enabled"; @@ -325,7 +324,7 @@ }; }); - } + } }); }; $("#" + childid).bind("mouseout", function (event) { setInsideWindow(false); $(document).unbind("keydown", d_onkeydown); actionSettings.keyboardAction = false; actionSettings.currentKey = null; }); @@ -385,7 +384,7 @@ //shift var currentSelected = $(obj).prop("id"); var currentIndex = a_array[currentSelected].index; - for (var i = Math.min(oldIndex, currentIndex); i <= Math.max(oldIndex, currentIndex); i++) { + for (var i = Math.min(oldIndex, currentIndex) ; i <= Math.max(oldIndex, currentIndex) ; i++) { $("#" + getByIndex(i).id).addClass(styles.selected); }; } else { @@ -621,7 +620,7 @@ if (has_handler('change') == true) { //alert(1); var currentSelected = a_array[$("#" + childid + " a.selected").prop("id")]; - if(currentSelected != undefined) { + if (currentSelected != undefined) { var currentSelectedValue = currentSelected.text; if ($.trim(oldSelectedValue) !== $.trim(currentSelectedValue) && oldSelectedValue !== "") { $("#" + elementid).trigger("change"); @@ -786,9 +785,9 @@ }; if ($("#" + childid).css("display") == "none") { var oldSelected = a_array[$("#" + childid + " a.selected").prop("id")]; - if(oldSelected != undefined) + if (oldSelected != undefined) oldSelectedValue = oldSelected.text; - + //keyboard action inputText = ""; oldHeight = $("#" + childid).height(); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index ed98b073c7..2182842d77 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -14,7 +14,7 @@ using umbraco.cms.businesslogic.propertytype; using umbraco.cms.businesslogic.web; using umbraco.cms.helpers; using umbraco.controls.GenericProperties; -using umbraco.IO; +using Umbraco.Core.IO; using umbraco.presentation; using umbraco.BasePages; using ContentType = umbraco.cms.businesslogic.ContentType; @@ -41,7 +41,7 @@ namespace umbraco.controls // "Tab" tab protected uicontrols.Pane Pane8; - + // "Structure" tab protected DualSelectbox DualAllowedContentTypes = new DualSelectbox(); @@ -50,7 +50,7 @@ namespace umbraco.controls // "Generic properties" tab public uicontrols.TabPage GenericPropertiesTabPage; - + public GenericPropertyWrapper gp; private DataTable _dataTypeTable; private ArrayList _genericProperties = new ArrayList(); @@ -113,7 +113,7 @@ namespace umbraco.controls var ea = new SaveClickEventArgs("Saved"); ea.IconType = BasePage.speechBubbleIcon.success; - + //NOTE The saving of the 5 properties (Name, Alias, Icon, Description and Thumbnail) are divided //to avoid the multiple cache flushing when each property is set using the legacy ContentType class, //which has been reduced to the else-clause. @@ -129,7 +129,7 @@ namespace umbraco.controls int i = 0; var ids = SaveAllowedChildTypes(); - _contentType.ContentTypeItem.AllowedContentTypes = ids.Select(x => new ContentTypeSort{ Id = new Lazy(() => x), SortOrder = i++ }); + _contentType.ContentTypeItem.AllowedContentTypes = ids.Select(x => new ContentTypeSort { Id = new Lazy(() => x), SortOrder = i++ }); var tabs = SaveTabs(); foreach (var tab in tabs) @@ -140,7 +140,7 @@ namespace umbraco.controls } else { - _contentType.ContentTypeItem.PropertyGroups.Add(new PropertyGroup{ Id = tab.Item1, Name = tab.Item2, SortOrder = tab.Item3 }); + _contentType.ContentTypeItem.PropertyGroups.Add(new PropertyGroup { Id = tab.Item1, Name = tab.Item2, SortOrder = tab.Item3 }); } } @@ -252,7 +252,7 @@ namespace umbraco.controls } #region "Info" Pane - + private void SetupInfoPane() { InfoTabPage = TabView1.NewTabPage("Info"); @@ -261,38 +261,62 @@ namespace umbraco.controls InfoTabPage.Style.Add("text-align", "center"); ImageButton Save = InfoTabPage.Menu.NewImageButton(); - Save.Click += new System.Web.UI.ImageClickEventHandler(save_click); + Save.Click += save_click; Save.ImageUrl = UmbracoPath + "/images/editor/save.gif"; Save.AlternateText = ui.Text("save"); Save.ID = "save"; - var listOfIcons = new List(); - // Get icons - // nh css file update, add support for css sprites - foreach (string iconClass in cms.businesslogic.CMSNode.DefaultIconClasses) - { - ListItem li = new ListItem(helper.SpaceCamelCasing((iconClass.Substring(1, iconClass.Length - 1))).Replace("Spr Tree", "").Trim(), iconClass); - li.Attributes.Add("class", "spriteBackground sprTree " + iconClass.Trim('.')); - li.Attributes.Add("style", "padding-left:20px !important; background-repeat:no-repeat;"); + + var dirInfo = new DirectoryInfo(UmbracoContext.Current.Server.MapPath(SystemDirectories.Umbraco + "/images/umbraco")); + var fileInfo = dirInfo.GetFiles(); - if (!this.Page.IsPostBack && li.Value == _contentType.IconUrl) li.Selected = true; - listOfIcons.Add(li); + var spriteFileNames = new List(); + foreach (var iconClass in cms.businesslogic.CMSNode.DefaultIconClasses) + spriteFileNames.Add(IconClassToIconFileName(iconClass)); + + var diskFileNames = new List(); + foreach (var file in fileInfo) + diskFileNames.Add(FileNameToIconFileName(file)); + + var listOfIcons = new List(); + + foreach (var iconClass in cms.businesslogic.CMSNode.DefaultIconClasses) + { + // .sprNew was never intended to be in the document type editor + if (iconClass.ToLowerInvariant() == ".sprNew".ToLowerInvariant()) + continue; + + if (_contentType.IconUrl == iconClass) + { + AddSpriteListItem(iconClass, listOfIcons); + continue; + } + + if (UmbracoSettings.IconPickerBehaviour == IconPickerBehaviour.HideSpriteDuplicates + && diskFileNames.Contains(IconClassToIconFileName(iconClass))) + continue; + + AddSpriteListItem(iconClass, listOfIcons); } - DirectoryInfo dirInfo = new DirectoryInfo(UmbracoContext.Current.Server.MapPath(SystemDirectories.Umbraco + "/images/umbraco")); - FileInfo[] fileInfo = dirInfo.GetFiles(); - for (int i = 0; i < fileInfo.Length; i++) + foreach (var file in fileInfo) { // NH: don't show the sprite file - if (fileInfo[i].Name != "sprites.png" && fileInfo[i].Name != "sprites_ie6.gif") - { - ListItem li = new ListItem(fileInfo[i].Name, fileInfo[i].Name); - li.Attributes.Add("title", this.ResolveClientUrl(SystemDirectories.Umbraco + "/images/umbraco/" + fileInfo[i].Name)); + if (file.Name.ToLowerInvariant() == "sprites.png".ToLowerInvariant() || file.Name.ToLowerInvariant() == "sprites_ie6.gif".ToLowerInvariant()) + continue; - if (li.Value == _contentType.IconUrl) - li.Selected = true; - listOfIcons.Add(li); + var listItemValue = this.ResolveClientUrl(SystemDirectories.Umbraco + "/images/umbraco/" + file.Name); + if (_contentType.IconUrl == listItemValue) + { + AddFileListItem(file.Name, listItemValue, listOfIcons); + continue; } + + if (UmbracoSettings.IconPickerBehaviour == IconPickerBehaviour.HideFileDuplicates + && spriteFileNames.Contains(FileNameToIconFileName(file))) + continue; + + AddFileListItem(file.Name, listItemValue, listOfIcons); } ddlIcons.Items.AddRange(listOfIcons.OrderBy(o => o.Text).ToArray()); @@ -300,11 +324,15 @@ namespace umbraco.controls // Get thumbnails dirInfo = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/images/thumbnails")); fileInfo = dirInfo.GetFiles(); - for (int i = 0; i < fileInfo.Length; i++) + + foreach (var file in fileInfo) { - ListItem li = new ListItem(fileInfo[i].Name); - li.Attributes.Add("title", this.ResolveClientUrl(SystemDirectories.Umbraco + "/images/thumbnails/" + fileInfo[i].Name)); - if (!this.Page.IsPostBack && li.Value == _contentType.Thumbnail) li.Selected = true; + var li = new ListItem(file.Name); + li.Attributes.Add("title", this.ResolveClientUrl(SystemDirectories.Umbraco + "/images/thumbnails/" + file.Name)); + + if (this.Page.IsPostBack == false && li.Value == _contentType.Thumbnail) + li.Selected = true; + ddlThumbnails.Items.Add(li); } @@ -320,9 +348,49 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); description.Text = _contentType.GetRawDescription(); } - + + private void AddSpriteListItem(string iconClass, ICollection listOfIcons) + { + var li = new ListItem( + helper.SpaceCamelCasing((iconClass.Substring(1, iconClass.Length - 1))) + .Replace("Spr Tree", "") + .Trim(), iconClass); + + li.Attributes.Add("class", "spriteBackground sprTree " + iconClass.Trim('.')); + li.Attributes.Add("style", "padding-left:24px !important; background-repeat:no-repeat; width:auto; height:auto;"); + + AddListItem(listOfIcons, li); + } + + private void AddFileListItem(string fileName, string listItemValue, ICollection listOfIcons) + { + var li = new ListItem(fileName, fileName); + + li.Attributes.Add("title", listItemValue); + + AddListItem(listOfIcons, li); + } + + private void AddListItem(ICollection listOfIcons, ListItem li) + { + if (this.Page.IsPostBack == false && li.Value == _contentType.IconUrl) + li.Selected = true; + + listOfIcons.Add(li); + } + + private static string IconClassToIconFileName(string iconClass) + { + return iconClass.Substring(1, iconClass.Length - 1).ToLowerInvariant().Replace("sprTree".ToLowerInvariant(), ""); + } + + private static string FileNameToIconFileName(FileInfo file) + { + return file.Name.Substring(0, file.Name.LastIndexOf(".", StringComparison.Ordinal)).ToLowerInvariant(); + } + #endregion - + #region "Structure" Pane private void SetupStructurePane() @@ -358,7 +426,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } DualAllowedContentTypes.Value = chosenContentTypeIDs; } - + allowAtRoot.Checked = _contentType.AllowAtRoot; } @@ -452,7 +520,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); foreach (cms.businesslogic.propertytype.PropertyType pt in propertyTypes) { //If the PropertyType doesn't belong on this ContentType skip it and continue to the next one - if(pt.ContentTypeId != _contentType.Id) continue; + if (pt.ContentTypeId != _contentType.Id) continue; var gpw = new GenericPropertyWrapper(); gpw.ID = "gpw_" + pt.Id; @@ -635,9 +703,9 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); //Loop through the _genericProperties ArrayList and update all existing PropertyTypes foreach (GenericPropertyWrapper gpw in _genericProperties) { - if(gpw.PropertyType == null) continue; + if (gpw.PropertyType == null) continue; - if(contentTypeItem.PropertyTypes == null || contentTypeItem.PropertyTypes.Any(x => x.Alias == gpw.PropertyType.Alias) == false) continue; + if (contentTypeItem.PropertyTypes == null || contentTypeItem.PropertyTypes.Any(x => x.Alias == gpw.PropertyType.Alias) == false) continue; var propertyType = contentTypeItem.PropertyTypes.First(x => x.Alias == gpw.PropertyType.Alias); if (propertyType == null) continue; @@ -786,7 +854,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); { PropertyTypes.Controls.Add(new LiteralControl("
No properties defined on this tab. Click on the \"add a new property\" link at the top to create a new property.
")); } - + private bool DoesPropertyTypeAliasExist(GenericProperty gpData) { bool hasAlias = _contentType.getPropertyType(Casing.SafeAliasWithForcingCheck(gpData.Alias.Trim())) != null; @@ -855,7 +923,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); LoadContentType(_contentType.Id); BindDataGenericProperties(true); } - + #endregion #region "Tab" Pane @@ -863,7 +931,7 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); private void SetupTabPane() { uicontrols.TabPage tp = TabView1.NewTabPage("Tabs"); - + pnlTab.Style.Add("text-align", "center"); tp.Controls.Add(pnlTab); diff --git a/src/umbraco.businesslogic/UmbracoSettings.cs b/src/umbraco.businesslogic/UmbracoSettings.cs index c7db9fdeb1..1e206dcc16 100644 --- a/src/umbraco.businesslogic/UmbracoSettings.cs +++ b/src/umbraco.businesslogic/UmbracoSettings.cs @@ -562,6 +562,18 @@ namespace umbraco get { return Umbraco.Core.Configuration.UmbracoSettings.MacroErrorBehaviour; } } + /// + /// This configuration setting defines how to show icons in the document type editor. + /// - ShowDuplicates - Show duplicates in files and sprites. (default and current Umbraco 'normal' behaviour) + /// - HideSpriteDuplicates - Show files on disk and hide duplicates from the sprite + /// - HideFileDuplicates - Show files in the sprite and hide duplicates on disk + /// + /// MacroErrorBehaviour enum defining how to show icons in the document type editor. + public static IconPickerBehaviour IconPickerBehaviour + { + get { return Umbraco.Core.Configuration.UmbracoSettings.IconPickerBehaviour; } + } + /// /// Configuration regarding webservices ///