Compare commits

..

247 Commits

Author SHA1 Message Date
Niels Lyngsø dbb99c2e6b use btn style action for consistency + cleaning button styles 2019-03-19 14:19:34 +01:00
Niels Lyngsø 61c76dc28b Merge remote-tracking branch 'origin/dev-v8' into temp8-4621-consistent-create-buttons 2019-03-19 13:54:03 +01:00
Niels Lyngsø 532f69e9b7 Show checkmark when list has selection 2019-03-19 13:53:13 +01:00
Bjarke Berg e2b1f0a82d Merge pull request #4948 from umbraco/temp8-temp-gather-url-for-list-items
V8: Provide url in content lists for ability to open item in a new tab
2019-03-19 13:35:45 +01:00
Niels Lyngsø 9597bde7bb visual corrections + remember view layout in a cookie 2019-03-19 13:03:59 +01:00
Niels Lyngsø 7f45d852ed fix search icon location 2019-03-19 13:03:02 +01:00
Niels Lyngsø 3f10c78bee correct js error 2019-03-19 11:04:52 +01:00
Niels Lyngsø 4b02bf2ddf use umb-table for users table 2019-03-19 10:55:36 +01:00
Niels Lyngsø 542eb2ab1d borrow great stuff from KJAC. 2019-03-19 10:04:13 +01:00
Sebastiaan Janssen 935a495d0e Merge branch 'dev-v8' of https://github.com/umbraco/Umbraco-CMS into dev-v8 2019-03-19 09:46:47 +01:00
Sebastiaan Janssen fe7c6bd1ed Merge branch 'patch-8.0.1' into dev-v8 2019-03-19 09:46:14 +01:00
Niels Lyngsø 137c08eaf3 Parse $event argument 2019-03-19 09:06:05 +01:00
Bjarke Berg 1545fe0d5f Merge pull request #5004 from kjac/v8-fix-filter-datatypes
V8: Improve the filtering in the datatype picker
2019-03-19 08:20:09 +01:00
Niels Lyngsø 9e7a062671 Merge branch 'dev-v8' into temp8-4621-consistent-create-buttons 2019-03-19 08:10:18 +01:00
Niels Lyngsø 970f336a5d clean up imports 2019-03-18 16:41:34 +01:00
Niels Lyngsø c6dfc28f03 make open user able to open in a new tab + only use pointer is selectable + improved hover state for open user 2019-03-18 16:34:41 +01:00
Niels Lyngsø 873d230e0e improve hover state for open user 2019-03-18 16:19:02 +01:00
Niels Lyngsø 4a26bfe3c3 clean up imports 2019-03-18 15:00:32 +01:00
Stephan 79dd593ee7 Merge pull request #5009 from umbraco/v8/bugfix/4967-list-view-ordering-ispublished
V8: Fix for ordering by isPublished in list view
2019-03-18 14:56:19 +01:00
Bjarke Berg 919f139607 https://github.com/umbraco/Umbraco-CMS/issues/4967 - fix for ordering by IsPublished in list view 2019-03-18 14:52:50 +01:00
Kenn Jacobsen 6e2c143d00 Improve the filtering in the datatype picker 2019-03-18 12:16:20 +01:00
Bjarke Berg 212b0ba932 Missing injection 2019-03-18 10:14:23 +01:00
Warren Buckley 51be2a5baf Merge pull request #5002 from umbraco/v8/bugfix/avoid-breaking-change-in-dashboard-events
Avoid removing a public method (used by forms 8.0.0)
2019-03-18 09:08:57 +00:00
Bjarke Berg 44beb81e71 Avoid removing a public method (used by forms 8.0.0) 2019-03-18 09:51:05 +01:00
Bjarke Berg 2f187fa403 Merge pull request #5001 from kjac/v8-fix-inherit-culture
V8: Fix the "Language" dropdown in the "Culture and Hostnames" dialog
2019-03-18 08:39:41 +01:00
Bjarke Berg 7359b0b244 Merge pull request #4996 from kjac/v8-fix-assign-domain-close-on-save
V8: Automatically close "Cultures and Hostnames" on successful save
2019-03-18 08:33:15 +01:00
Kenn Jacobsen 8f06603774 Fix the "Language" dropdown in the "Culture and Hostnames" dialog 2019-03-17 20:51:42 +01:00
Kenn Jacobsen 688173488d Automatically close "Cultures and Hostnames" on successful save 2019-03-15 09:26:21 +01:00
Warren Buckley edb8340b32 Merge pull request #4364 from umbraco/abjerner-patch-1
V8: fixed type of dashboards in EditorModelEventManager
2019-03-14 15:00:34 +00:00
Niels Lyngsø 808d33edf6 force commit white logo svg 2019-03-13 11:13:00 +01:00
Shannon Deminick 8714bb816f Merge pull request #4950 from umbraco/v8/feature/4590-resolvable-ifactory
IFactory is resolvable
2019-03-13 14:49:12 +11:00
Robert a3e842a315 Merge pull request #4844 from umbraco/dev-v8-4827-fix-for-image-mediapickervalueconverter
V8: Fix for MediaPickerValueConverter when config is only images
2019-03-12 11:59:54 +01:00
Robert c3bf6b0631 Merge pull request #4908 from umbraco/temp8-ui-use-umb-checkbox-for-all-variants-dialogs
Use umb-checkbox for all content variant dialogs
2019-03-12 11:55:40 +01:00
Robert bf28dfe04a Merge pull request #4956 from umbraco/temp8-fix-for-creating-content-templates
V8: Fixed issue when creating variant content templates,
2019-03-12 11:30:44 +01:00
Robert 3e6cdd86b2 Merge branch 'dev-v8' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs 2019-03-12 11:09:20 +01:00
Robert abd681277d Merge pull request #4906 from umbraco/temp8-ui-umb-checkbox-refactor-for-general-usage
changed umb-checkbox model to be the boolean property, not a object c…
2019-03-12 10:29:43 +01:00
Niels Lyngsø 5ad1dcf894 Merge remote-tracking branch 'origin/temp8-4728' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.html
2019-03-12 10:17:17 +01:00
Niels Lyngsø 485c1fbf6c use existing notifications for warnings 2019-03-12 10:14:25 +01:00
Niels Lyngsø 447eb88b86 Use tokens for translate + shortcut for reject 2019-03-12 10:14:06 +01:00
Robert ece9c4ae21 Merge branch 'dev-v8' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs 2019-03-12 10:08:14 +01:00
Robert 1509c7364c Reverted extra check 2019-03-12 09:52:03 +01:00
Bjarke Berg 010e2e1f19 Merge pull request #4954 from umbraco/temp8-grid-rte-image-size
Fixes inserting an image into the grid rte since it doesn't get resized correctly
2019-03-12 09:07:54 +01:00
Robert 9b25cdaa14 Merge pull request #4710 from umbraco/temp8-4708-fix-for-exit-preview-with-null-culture
Fix for exit preview with null culture
2019-03-12 08:31:28 +01:00
Bjarke Berg 31eea2a2a8 Fixed issue when creating variant content templates, but the properties is invariant. Before this change an exception was thrown 2019-03-12 08:17:09 +01:00
Shannon 2e52a869b6 Merge branch 'dev-v8' into temp8-4728
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/content/overlays/publish.controller.js
2019-03-12 13:03:32 +11:00
Shannon 95d1ceb529 overrides ToString on UrlInfo to return the correct result 2019-03-12 12:47:37 +11:00
Shannon 10f333081d Fixes inserting an image into the grid rte since it doesn't get resized correctly 2019-03-12 12:43:04 +11:00
Shannon Deminick cefff71edf Merge pull request #4853 from umbraco/temp8-js-error-unpublishing
Fixes javascript error when trying to unpublish an invariant item
2019-03-12 11:18:09 +11:00
Stephan 13d97fffdf IFactory is resolvable 2019-03-11 17:14:07 +01:00
Sebastiaan Janssen ba297ad839 Merge branch 'patch-8.0.1' into dev-v8 2019-03-11 16:12:18 +01:00
Stephan 7e67bbea13 Do not inject IEnumerable but Collection in UmbracoContextFactory 2019-03-11 15:37:07 +01:00
Kenn Jacobsen 9062b400d4 Show the content app for media folders with content properties 2019-03-11 14:57:25 +01:00
Niels Lyngsø 48ec795e7b do not open as infinite editing yet. 2019-03-11 14:54:11 +01:00
Bjarke Berg e7781cd95b DRY - Reused code 2019-03-11 14:53:49 +01:00
Niels Lyngsø 1098db1632 use item.editPath for new tab URL 2019-03-11 14:52:16 +01:00
Shannon 6880937b90 Merge branch 'dev-v8' of https://github.com/umbraco/Umbraco-CMS into dev-v8 2019-03-12 00:46:52 +11:00
Shannon 52beb01110 Removes faulty CustomRouteUrlProvider 2019-03-12 00:46:39 +11:00
Stephan fbfa7e8566 Cleanup temp path in global config 2019-03-11 14:40:18 +01:00
Stephan c85c055424 Upgrade to ModelsBuilder 8.0.2, support Custom Tool (#4881)
* Upgrade to ModelsBuilder 8.0.2, support Custom Tool

* Upgrade to ModelsBuilder 8.0.3

* Fix controllers

* Upgrade MB

* Fix previous commit
2019-03-11 23:14:32 +11:00
Niels Lyngsø 829980adb2 Merge remote-tracking branch 'origin/dev-v8' into temp8-temp-gather-url-for-list-items
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/layouts/list/list.listviewlayout.controller.js
2019-03-11 13:05:59 +01:00
Bjarke Berg 8f3bcf87da Merge pull request #4754 from umbraco/temp8-ui-update-login-screen
V8: UI — updated login screen
2019-03-11 12:41:40 +01:00
Niels Lyngsø ba55c24d43 correcting path in umbracoSettings.Release.config
+ changed comment to fit with new look
2019-03-11 12:40:23 +01:00
Niels Lyngsø 4d806c1cef correction error colors 2019-03-11 12:38:04 +01:00
Niels Lyngsø b237e4a55e make warnings an array that angularJS can watch 2019-03-11 12:37:53 +01:00
Niels Lyngsø 502794aae9 validation text provided for more languages, plus it takes action when name is an empty string 2019-03-11 11:51:13 +01:00
Kenn Jacobsen 2352b50d97 Handle checkbox value conversion from Nested Content 2019-03-08 15:07:50 +01:00
Niels Lyngsø 356b7681e8 Merge remote-tracking branch 'origin/temp8-ui-umb-checkbox-refactor-for-general-usage' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs 2019-03-08 14:37:42 +01:00
Niels Lyngsø 0d58b4c42b renamed umb-checkbox id attribute to input-id to avoid colliding Ids 2019-03-08 14:37:23 +01:00
Niels Lyngsø 171cae5fca reverted this line, cause for some wierd reason this line works, and corrected one ruins the feature. Super wierd, i guess the person writting this was just lucky to hit something that works. Need to look into what is going on here. 2019-03-08 14:24:54 +01:00
Niels Lyngsø 979a166b41 changed checkbox-inputs to umb-checkbox 2019-03-08 14:23:37 +01:00
Niels Lyngsø 623ace56bc Improved UX of Save
+ Remove special case for new content
+ Never disable the user choices, instead provide feedback for when the selection doesn't make sense.
2019-03-08 14:22:05 +01:00
Niels Lyngsø 7203926cea no need for type attribute 2019-03-08 12:54:55 +01:00
Niels Lyngsø 021bf470c2 umb-checkbox for save dialog 2019-03-08 12:41:33 +01:00
Niels Lyngsø 7194d7d6b8 update to input-id 2019-03-08 12:41:18 +01:00
Niels Lyngsø 45423ae932 use tokens in localizationsService for replace of %0% 2019-03-08 12:40:57 +01:00
Niels Lyngsø 2b7f12253f umb-checkbox id renamed to input-id for it not to collide, since ids have to be unique. 2019-03-08 12:40:24 +01:00
Niels Lyngsø 129210946d Merge branch 'temp8-ui-umb-checkbox-refactor-for-general-usage' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs 2019-03-07 15:29:47 +01:00
Niels Lyngsø 62808958b0 umb-checkbox callback should call a new digest for callbacks changes to take effect. 2019-03-07 15:29:28 +01:00
Niels Lyngsø c5096e67e6 Merge remote-tracking branch 'origin/temp8-ui-umb-checkbox-refactor-for-general-usage' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js
2019-03-07 14:47:40 +01:00
Niels Lyngsø 320d2ae953 Change umb-checkbox to AngularJs Component + fix for callback digest flow 2019-03-07 14:46:50 +01:00
Niels Lyngsø 264a96971a Merge remote-tracking branch 'origin/temp8-ui-umb-checkbox-refactor-for-general-usage' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html
2019-03-07 13:01:04 +01:00
Niels Lyngsø 8c28172efc changed umb-checkbox model to be the boolean property, not a object containing a checked property.
This means that the on-change callback will recive the model and the value instead.
2019-03-07 12:57:10 +01:00
Niels Lyngsø 8a4dfd4350 first step for implementing umb-checkbox 2019-03-07 11:54:46 +01:00
Niels Lyngsø 3814138d68 Merge remote-tracking branch 'origin/dev-v8' into temp8-ui-use-umb-checkbox-for-all-variants-dialogs 2019-03-07 11:32:04 +01:00
Niels Lyngsø f54a8725db CSS: change form check inputs to use not-allow cursor when disabled 2019-03-07 11:25:13 +01:00
Bjarne Fyrstenborg bbc2b1a54a v8: Checkbox/radio with disabled state (#4902)
* Add property to disable checkbox and radiobutton

* Add basic styles of disabled state

* Change cursor style for disabled checkbox/radiobutton

* Remove duplicate classes

* Change nested elements to span since it is not valid to have div as nested elements in labels

* Adjust positions

* Better way to center align icon

* Add white as default background color in checkbox/radiobutton e.g. for Nested Content

* Adjust sizing a bit
2019-03-07 11:24:16 +01:00
Niels Lyngsø 6dc5a3b130 Make login background image configurable 2019-03-07 10:33:10 +01:00
Niels Lyngsø 3d941e89a6 Merge remote-tracking branch 'origin/dev-v8' into temp8-ui-update-login-screen 2019-03-07 10:00:56 +01:00
Bjarke Berg a988bd2608 Merge pull request #4809 from umbraco/temp8-3958-fixes-doc-type-details-in-narrow-screen
V8: makes document-details go on to two lines when necessary
2019-03-07 09:57:28 +01:00
Niels Lyngsø cb083d5b2a increased break point to 500px 2019-03-07 09:47:03 +01:00
Sebastiaan Janssen 8334d088e7 Update PULL_REQUEST_TEMPLATE.md 2019-03-07 09:33:43 +01:00
Shannon Deminick 75a9109971 Merge pull request #4874 from umbraco/temp8-4873-register-api-controllers
Enable adding api controllers
2019-03-07 18:33:13 +11:00
Shannon 52cea17816 Merge remote-tracking branch 'origin/dev-v8' into temp8-4873-register-api-controllers 2019-03-07 18:32:48 +11:00
Niels Lyngsø 6a67f38025 Merge remote-tracking branch 'origin/dev-v8' into temp8-3958-fixes-doc-type-details-in-narrow-screen 2019-03-07 08:12:04 +01:00
Shannon Deminick 22d012c4db Merge pull request #4856 from umbraco/temp8-4775-components-sequence
Better composer sequence
2019-03-07 18:04:01 +11:00
Niels Lyngsø 3046e44f60 minor UI improvements regarding validation 2019-03-07 07:59:13 +01:00
Shannon 790da9d5ca Adds todo note 2019-03-07 17:23:00 +11:00
Stephan 7f31ddc250 Bugfix CoreRuntime 2019-03-06 18:11:10 +01:00
Kenn Jacobsen 869d088e4b Always update URLs on save no matter which app is active 2019-03-06 17:02:10 +01:00
Sebastiaan Janssen 6e3ffc0dd9 Removes outdated gulp setup helper 2019-03-06 16:19:14 +01:00
Niels Lyngsø 45dc3a26ed Updated CONTRIBUTING_DETAILED to fit new npm setup (#4735)
Updated CONTRIBUTING_DETAILED to fit new npm setup

Co-authored-by: Sebastiaan Janssen <sebastiaan@umbraco.com>
2019-03-06 16:06:56 +01:00
Sebastiaan Janssen 9ef94a667c Merge pull request #4716 from MMasey/#4715
#4715 - V8 - IE11: section name underline not in correct position
2019-03-06 15:39:24 +01:00
Niels Lyngsø 70122c85d7 Merge pull request #4825 from kjac/v8-fix-validation-colors
V8: Update validation colors throughout the backoffice
2019-03-06 15:31:39 +01:00
Niels Lyngsø 72e236a7ab css class umb-list-item--error for when a list item contain a error that needs to be highlighted, currently only used for Save & Publish overlay-dialog. 2019-03-06 14:58:32 +01:00
Niels Lyngsø 444497be7c For Save & Publish overlay:
+ Publish multiple variants for new nodes.
+ Auto selects mandatory languages.
+ Auto selects languages that has changes.
+ Provides feedback if missing nodeName on variant that isn't open.
+ Highlights "mandatory language"-description if variant isn't selected.
2019-03-06 14:42:31 +01:00
Sebastiaan Janssen b7e929a675 Fix checkboxes and radiobuttons saving the wrong value 2019-03-06 14:01:11 +01:00
Stephan 2209b37a4a Bugfix CoreRuntime 2019-03-06 11:56:03 +01:00
Sebastiaan Janssen 634138724b Merge branch 'dev-v7' into dev-v8
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbcheckbox.directive.js
#	src/Umbraco.Web.UI.Client/src/common/directives/components/forms/umbradiobutton.directive.js
#	src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less
#	src/Umbraco.Web.UI.Client/src/views/components/forms/umb-checkbox.html
#	src/Umbraco.Web.UI.Client/src/views/components/forms/umb-radiobutton.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html
#	src/Umbraco.Web/Trees/ApplicationTreeController.cs
#	src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
#	src/Umbraco.Web/Trees/MemberTreeController.cs
#	src/Umbraco.Web/Trees/TreeControllerBase.cs
#	src/Umbraco.Web/WebApi/Filters/HttpQueryStringFilterAttribute.cs
#	src/Umbraco.Web/WebApi/Filters/HttpQueryStringModelBinder.cs
#	src/umbraco.cms/businesslogic/translation/Translation.cs
2019-03-06 11:47:30 +01:00
Sebastiaan Janssen d1c9b7a060 Merge pull request #4842 from umbraco/temp8-merge-3464
Merge branch 'dev-v7' into dev-v8
2019-03-06 11:45:09 +01:00
Sebastiaan Janssen b65f60af78 Update documentation 2019-03-06 11:43:50 +01:00
Niels Lyngsoe 92453c4652 fix front-end requirement of umb-checkbox 2019-03-06 11:18:07 +01:00
Sebastiaan Janssen b86a1aa687 Merge branch 'patch-8.0.1' into dev-v8
# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
2019-03-06 10:44:09 +01:00
Sebastiaan Janssen ac4c98a7fc Enable direct command execution in build script (#4882) 2019-03-06 10:35:26 +01:00
Bjarke Berg b406835c9c Code styling 2019-03-06 10:08:15 +01:00
Stephan 9f7fae2ca6 Enable adding api controllers 2019-03-05 19:23:57 +01:00
Bjarke Berg f508a97c18 Merge pull request #4857 from umbraco/temp8-4649-fix-nucache-url-segments
Fix NuCache url segments
2019-03-05 14:54:51 +01:00
Niels Lyngsø b4de15f19e removed unused htmlId 2019-03-05 14:22:58 +01:00
Bjarke Berg f8abd0f457 Merge pull request #4858 from umbraco/temp8-fix-malformed-logs
Fixes LogViewer if JSON log contains malformed JSON lines
2019-03-05 13:41:54 +01:00
Niels Lyngsø 31eefe1cf9 Front-end mandatory-validation for radiolist and checkbox list. 2019-03-05 13:20:59 +01:00
Stephan a14b1540ea Merge pull request #4848 from umbraco/temp8-modernize-nuget-install
Modernize NuGet install
2019-03-05 13:15:10 +01:00
Warren Buckley 67fdf75da8 Fix typo 2019-03-05 11:12:04 +00:00
Niels Lyngsø 21405266ef align the use of input name attribute for checkboxes and radiobuttons 2019-03-05 11:38:43 +01:00
Niels Lyngsø a4d25dc594 design corrections 2019-03-05 11:38:04 +01:00
Warren Buckley 27cc433b96 We will ignore malformed log lines in the JSON log file - so the rest of the logviewer continues to work 2019-03-05 10:35:51 +00:00
Stephan cfa94b1bb8 Import missing file 2019-03-05 11:21:29 +01:00
Stephan 7a0f11bc6f Fix NuCache url segments 2019-03-05 11:15:30 +01:00
Stephan b665d4825d Better composer sequence 2019-03-05 08:53:37 +01:00
Stephan 55a374bb71 Introduce WebFinalComponent 2019-03-05 08:53:37 +01:00
Shannon 13b35ec7ce Fix to get Js unit tests working again. 2019-03-05 18:53:26 +11:00
Shannon Deminick 54968c819b Merge pull request #4803 from umbraco/temp8-4752-fix-reordering-grid-in-non-debug-mode
Solution for not using scope() in grid redordering
2019-03-05 16:52:01 +11:00
Shannon c910e3afaf Fixes javascript error when trying to unpublish an invariant item 2019-03-05 16:39:03 +11:00
Shannon Deminick 4b7c14113c Merge pull request #4770 from umbraco/temp8-4652-tinymce-inserting-image
change insertContent to selection.setContent
2019-03-05 16:12:05 +11:00
Shannon Deminick b294b83c9f Merge pull request #4753 from mvanhelmont/#4580
Redirects management is broken #4580
2019-03-05 16:04:11 +11:00
Shannon Deminick 5ddba3e64e Merge pull request #3851 from umbraco/temp-fix-formdatacollection-3796
Fixes binding of FormDataCollection for GET requests which fixes the …
2019-03-05 13:39:37 +10:00
Stephan 31b293aa4d Fix distributed cache handler for medias (#4751) 2019-03-04 16:39:34 +01:00
Sebastiaan Janssen 82b78c7530 Require higher NuGet version so we don't target VS15 any more (unsupported)
Making backups before we upgrade is no longer needed, removed from install.ps1
If people include the bin folder in their solution they will get errors upgrading now, we shouldn't need to try and fix this, there's no reason to include the bin folder in the solution - therefore install.core.ps1 is removed
2019-03-04 16:27:44 +01:00
Niels Lyngsø d3904086f0 adjusted PR 3464 for V8 2019-03-04 14:39:49 +01:00
Bjarke Berg a43f8c4cbd Bugfix: https://github.com/umbraco/Umbraco-CMS/issues/4827 - Fixed MediaPickerValueConverter when config is Only Images 2019-03-04 14:31:45 +01:00
Pete Duncanson f1d1c95709 Removed System.Web.WebPages reference in nonodes.aspx as was failing a clean build 2019-03-04 14:21:38 +01:00
Sebastiaan Janssen 58ce528324 Merge branch 'dev-v7' into dev-v8
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html
#	src/umbraco.cms/businesslogic/translation/Translation.cs
2019-03-04 13:43:31 +01:00
Niels Lyngsø c72a5c07fe Merge pull request #3464 from BatJan/feature/temp-U4-3463
3463 - Suggestion: Make checkboxes look prettier in checkbox lists
2019-03-04 11:39:08 +01:00
Stephan eace918258 Merge pull request #4782 from kjac/v8-fix-create-content-template
V8: Fix "ambient scope" error when creating content template from content
2019-03-04 10:40:40 +01:00
Niels Lyngsoe 409ae99bf7 renaming of umb-form-control to umb-form-check 2019-03-04 10:29:58 +01:00
Stephan 8961e8b4be Merge pull request #4834 from umbraco/temp-4609-rte-not-rendering-macros-correctly
Bugfix: Could not render macros in RTE
2019-03-04 09:29:06 +01:00
Stephan b526b8f246 Merge pull request #4823 from Brianmanden/patch-1
Update Utf8ToAsciiConverter.cs
2019-03-04 09:15:00 +01:00
Bjarke Berg ed6d54e64a Bugfix: Don't use ToString :'( 2019-03-04 08:43:19 +01:00
Kenn Jacobsen 89153c2fcf Update log viewer error color 2019-03-03 16:22:12 +01:00
Kenn Jacobsen c134aff6cd Update validation colors throughout the backoffice 2019-03-03 13:54:45 +01:00
Brian Juul Andersen 57290c4180 Update Utf8ToAsciiConverter.cs
Fix minor grammatical error.
2019-03-02 21:44:28 +01:00
Kenn Jacobsen 84b60f8b9f V8: Make variant state clickable in various dialogs (#4760)
* Fix publish dialog

* Fix publish with descendants dialog

* Fix save dialog

* Fix scheduled publishing dialog

* Fix send to publish dialog

* Fix unpublish dialog

* Add missing magic IDs to send to publish dialog so the labels actually work.

* Add missing magic IDs to unpublish dialog
2019-03-01 15:21:34 +01:00
Niels Lyngsø e33ae253ee makes document-details go on to two lines when necessary 2019-03-01 12:56:09 +01:00
Niels Lyngsø a67163fd03 hack for not using scope() in grid redordering 2019-02-28 15:29:28 +01:00
Shannon Deminick cfc04a9df0 Merge pull request #4799 from robertjf/dev-v8-uduf-4749
v8 - uduf - Changed all references of umbracoUseSSL to Umbraco.Core.UseHttps to f…
2019-02-28 18:20:25 +11:00
Shannon 3e4ff05d37 removes unused params 2019-02-28 17:18:47 +10:00
Shannon Deminick 28034d664e Merge pull request #4793 from dinc5150/4489
v8 - uduf - Redirect away from page under the settings section when the page is deleted
2019-02-28 18:16:33 +11:00
David Sheiles edf3fd5d31 remove test code 2019-02-28 18:15:41 +11:00
David Sheiles f7705ef134 Move logic from navigation service to controller 2019-02-28 18:10:03 +11:00
David Sheiles f0caa6be4b Update null check 2019-02-28 17:57:05 +11:00
Robert Foster 5aa38c1f43 Changed all references of umbracoUseSSL to Umbraco.Core.UseHttps to fix issue #4749 2019-02-28 16:48:56 +10:00
Shannon Deminick b809f9f03a Merge pull request #4798 from emmanueltissera/v8-uduf2019-issue-4757
v8 - uduf - Fixes hotkey for navigation between apps and content apps
2019-02-28 17:45:16 +11:00
Shannon Deminick eabc05d6f8 Merge pull request #4796 from skartknet/4790_-_Content_and_media_overloads_for_Udis
4790 - added Content and Media Udi overloads
2019-02-28 17:33:05 +11:00
David Sheiles d3fb614d25 Move logic to tree service and navigarion service 2019-02-28 17:29:40 +11:00
Emmanuel Tissera 0eb7d404a1 Fixes hotkey for navigation between apps and content apps
Fixes https://github.com/umbraco/Umbraco-CMS/issues/4757
2019-02-28 17:10:39 +11:00
Mario Lopez 093b57505a simplified GuidUdi checking. 2019-02-28 16:50:28 +11:00
Shannon Deminick 2a7cd4d94a Merge pull request #4797 from iofsauron/4766-list-view-selected-bug
4766 list view selected bug
2019-02-28 16:31:56 +11:00
iofsauron 4503967778 4766 fixed up other instances where nodeSelectHandler was being passed incorrect number of arguments 2019-02-28 15:02:53 +10:00
Mario Lopez f227f2ed61 added Content and Media Udi overloads 2019-02-28 13:55:14 +11:00
iofsauron 553bf6d304 4766 Fixed list view selected item issue by uncommenting out the umb-table-body__fileicon and umb-table-body__checkicon. Fixed call to nodeSelectHandler in selectListViewNode by removing erroneous first parameter 2019-02-28 12:33:49 +10:00
David Sheiles 45dfef78bd Redirect away from page under the settings section when the page is deleted. Affects DataTypes, DocTypes, Macros, MediaTypes, Membertypes, PartialViewMacro,PartialViews, Scripts, Stylesheet & Templates 2019-02-28 13:33:45 +11:00
Shannon Deminick b172a5f847 Merge pull request #4791 from robertjf/dev-v8-uduf-4787
Fix for Content App badges not being displayed (#4787)
2019-02-28 13:05:56 +11:00
Shannon 41f80b942a Merge branch 'dev-v8' of https://github.com/umbraco/Umbraco-CMS into dev-v8 2019-02-28 11:02:12 +10:00
Robert Foster 055f983cbf Fix for Content App badges not being displayed (#4787) 2019-02-28 11:01:59 +10:00
Shannon 3e9556fead Adds TODO note 2019-02-28 11:01:53 +10:00
Kenn Jacobsen b403272389 Fix "ambient scope" error when creating content template from content 2019-02-27 18:54:53 +01:00
Niels Lyngsø 60b178dcf7 change insertContent to selection.setContent 2019-02-27 10:28:40 +01:00
Jan Skovgaard 969a9c1193 Update radiobuttons view to make use of the umb-radiobutton directive 2019-02-27 09:56:22 +01:00
Jan Skovgaard 3643e170c5 Add radiobutton directive 2019-02-27 09:55:52 +01:00
Kenn Jacobsen b5bda772af Make it possible to upload the same file twice or more (#4755) 2019-02-27 09:51:11 +01:00
Mikkel Holck Madsen 02ccd45bf6 Merge pull request #4767 from umbraco/temp8-4711-update-web-config-transformations-to-v8
Updated versions and added new dependencies to transformation file
2019-02-27 09:47:12 +01:00
Bjarke Berg b86e7dc0e8 Ensure we dont double insert 2019-02-27 09:44:28 +01:00
Jan Skovgaard afc872de5a Refactor checkboxlist to make use of the umbcheckbox directive 2019-02-27 09:40:48 +01:00
Jan Skovgaard 56aef39014 Add checkbox directive 2019-02-27 09:40:25 +01:00
Bjarke Berg 578eb8b32c Updated versions and added new dependencies to transformation file 2019-02-27 09:30:15 +01:00
Bjarke Berg f2d7ed69ca Fix for printing html in uninstall package readme 2019-02-27 09:10:33 +01:00
Jan Skovgaard ae07b2b299 Refactor modifiers so they're done on the top level of the block instead 2019-02-26 21:42:24 +01:00
Jan Skovgaard b99cf238e0 Fix order of pseudo selectors to :focus is actually also set on the focused radio button 2019-02-26 21:26:14 +01:00
Jan Skovgaard 68457b7eaf Refactor styling of checkbox and radiobuttons so they're now both "umb-form-control" with modifiers, which makes it easier to reuse especially the checkbox throughout the UI. The umb-editor has been extented to have a item element and a list modfier too. Overall less code too 2019-02-26 21:21:55 +01:00
Jan Skovgaard 24c74676f6 Merge branch 'dev-v7' into feature/temp-U4-3463 2019-02-26 19:45:51 +01:00
Marcel van Helmont ff708f4ad5 fixed correct destination url instead of #. 2019-02-26 12:17:20 +01:00
Marcel van Helmont 44212f6b53 Revert "the wrong id was used for getting the correct destination url."
This reverts commit 5d1fccb2c4.
2019-02-26 12:15:22 +01:00
Marcel van Helmont 5d1fccb2c4 the wrong id was used for getting the correct destination url. 2019-02-26 12:12:18 +01:00
Per Søndergaard Jensen ac1ee2fe0c #4428 - Replaced Log.Add calls with AuditService.Add in translations 2019-02-26 11:17:08 +01:00
Claus 746573d28b Merge pull request #4744 from kjac/v8-fix-selectable-search-results
V8: Fix the "selected" checkmark in tree picker search results
2019-02-26 10:36:07 +01:00
Kenn Jacobsen 09dc908332 Fix the "selected" checkmark in tree picker search results 2019-02-26 09:56:07 +01:00
Sebastiaan Janssen 35bf75bc82 Do the version dance 2019-02-26 09:28:51 +01:00
Sebastiaan Janssen cb0268dfdd Merge branch 'dev-v8-fix' into dev-v8 2019-02-26 09:17:47 +01:00
Sebastiaan Janssen 9186b392c9 Merge branch 'dev-v7' into dev-v8
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagecrop.directive.js
#	src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
2019-02-25 19:57:18 +01:00
Skid Vis 4f30dd32f1 Updates link to dev-v8
Was linked to temp8 which no longer exists and results in 404
2019-02-25 17:20:35 +01:00
Anders Bjerner bac2b1cb71 Updated reference to dev-v7 as dev-v8 is now the default branch 2019-02-25 17:19:55 +01:00
Bjarke Berg 57af355d85 Bugfix: get installStateComplete variable from labels 2019-02-25 14:37:25 +01:00
Carole Rennie Logan 9bcce48df3 still reference to temp8 2019-02-25 07:43:35 +01:00
Skid Vis c83630b376 Updates branch to checkout when trying v8
The branch specified, 'temp8', no longer exists. Updates to the current 'dev-v8' branch name.
2019-02-23 16:32:33 +01:00
Sebastiaan Janssen e1856bf851 Update README.md 2019-02-23 16:30:18 +01:00
Sebastiaan Janssen fe3525bc54 Update README.md 2019-02-23 16:27:18 +01:00
Sebastiaan Janssen 452dcbd016 Update README.md 2019-02-23 16:26:48 +01:00
Mike 7a20ea8781 fix: position of section name underline on IE11 2019-02-23 15:00:29 +00:00
Sebastiaan Janssen 4b489b52ef Bump version again 2019-02-23 14:21:44 +01:00
Stephan aa86dfa956 Bugfix exception during install, live models collision 2019-02-22 19:13:48 +01:00
Bjarke Berg 02122f5e03 Revert "Fix NullReferenceException when using LocalDb, because SqlServerSyntaxProvider.ServerVersion can be null"
This reverts commit a9450f9e.

Root cause is fixed
2019-02-22 14:27:54 +01:00
Bjarke Berg 064f637006 #4708 - Fix for YSOD when exiting preview with on invariant cultures 2019-02-22 11:23:57 +01:00
Bjarke Berg a9450f9ed9 Fix NullReferenceException when using LocalDb, because SqlServerSyntaxProvider.ServerVersion can be null 2019-02-22 11:02:37 +01:00
Stephan 04e916b41c Fix test 2019-02-22 10:42:43 +01:00
Stephan e167388e74 Make UniqueMediaPathScheme deterministic 2019-02-22 09:44:25 +01:00
Anders Bjerner 4d71c5550b Should be returned as a list - otherwise we'll get a new collection and new tab objects each time we iterate over it 2019-02-19 22:36:12 +01:00
Anders Bjerner 0636616a6b #H5IS 2019-02-19 22:21:13 +01:00
Sebastiaan Janssen 8f17d6fcd2 Merge branch 'patch-7.13.2' into dev-v7
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js
2019-02-19 11:22:59 +01:00
SimonHilo 1300389ce6 Skip the check for approved/locked out members if not using Umbracos built-in MembershipProvider (#4607) 2019-02-18 20:49:27 +01:00
Kenn Jacobsen ea8d8ba771 Make the image crop slider move at 66 hertz 2019-02-18 20:39:16 +01:00
Kenn Jacobsen 1eb6ae10c9 Fix the initial zoom offset for new crops in the image cropper 2019-02-18 20:38:11 +01:00
Jan Skovgaard 5d3e1fb95f v7: User history showing even if it's empty (#4524) 2019-02-18 20:36:36 +01:00
Niels Lyngsø a9fa049183 #4621 consistent create button colors 2019-02-18 15:28:33 +01:00
Niels Lyngsø 9f182e6461 compressed login.jpg 2019-02-12 13:17:48 +01:00
Niels Lyngsø 2403373457 added login image 2019-02-12 13:16:28 +01:00
Niels Lyngsø e9e852c499 Merge remote-tracking branch 'origin/temp8' into temp8-ui-update-login-screen 2019-02-12 12:59:01 +01:00
Niels Lyngsø a1abdc8ba7 Login screen color changes 2019-02-12 11:56:51 +01:00
Anders Bjerner 6130b475da IDashboard -> IDashboardSlim 2019-02-01 18:47:17 +01:00
Niels Lyngsø 95b4f9141e test for gathering url for list items so you can Ctrl/CMD+click. 2019-02-01 14:06:44 +01:00
Kenn Jacobsen 19aae9ffdc Make sure the color picker label is stored on first select (#4325)
(cherry picked from commit 4dd51f645a)
2019-01-30 16:14:09 +01:00
Sebastiaan Janssen 218995f9e0 Set version to 7.13.2 2019-01-29 17:34:05 +01:00
Kenn Jacobsen 0d167c4f2d Fix callback
(cherry picked from commit 99f0e6c05a)
2019-01-29 14:13:22 +01:00
Kenn Jacobsen d44e0d8664 Set form dirty on color picker value change + update selected item label + clean up unused code
(cherry picked from commit 2c40e46d56)
2019-01-29 14:13:14 +01:00
Jan Skovgaard 94a4e34e0c Make sure to hide the whole state element for screen readers rather than just the icon 2019-01-27 15:32:52 +01:00
Jan Skovgaard 7d42583177 Fix animation 2019-01-27 15:30:02 +01:00
Jan Skovgaard 77a8697ca7 Make sure the border is green when the checkbox is checked and that it's darker gray when it is focused (also while checked) 2019-01-27 15:24:49 +01:00
Jan Skovgaard 7462686778 Add styles and minor refactoring so we no longer use the "wing" icon when a radio button is checked but rather make it look like a readio button 2019-01-27 15:22:37 +01:00
Jan Skovgaard 44d5ce4a2e Include the radiobuttonlist component 2019-01-27 15:21:54 +01:00
Jan Skovgaard 34888f31e6 Place the ul styling in the proper context 2019-01-27 14:50:15 +01:00
Jan Skovgaard 0994aac656 Restructure markup to ensure the label text is properly wrapped and make sure it all aligns nicely on small breakpoints too 2019-01-27 14:38:27 +01:00
Jan Skovgaard 0e0d38be30 Refactor styling of the checkbox so the boxes are now a tad smaller and better aligned with one another and the UI in general 2019-01-27 14:18:41 +01:00
Jan Skovgaard 91735e7e3f Fix bad merge 2019-01-27 14:17:59 +01:00
Jan Skovgaard 0d4b3e4173 Merge branch 'dev-v7' into feature/temp-U4-3463
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/components/umb-radiobuttons-list.less
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/checkboxlist/checkboxlist.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/radiobuttons/radiobuttons.html
2019-01-27 13:31:33 +01:00
Shannon a0f15bba44 Fixes binding of FormDataCollection for GET requests which fixes the compatibility with the latest WebApi 5.2.7 version 2018-12-11 11:58:39 +11:00
Jan Skovgaard 007cdeb9ab Fix long label issue on RADIO button list 2018-10-31 21:48:47 +01:00
Jan Skovgaard 5716353518 Offset the state 1 px to align it with the radio button list if they ever occur as properties beneath each other 2018-10-31 21:46:00 +01:00
Jan Skovgaard 87340d6c2c Fix width issue on the checkbox label 2018-10-31 21:24:35 +01:00
Jan Skovgaard fc742bcf18 Merge branch 'dev-v7' into feature/temp-U4-3463 2018-10-31 20:03:05 +01:00
Jan Skovgaard 4eb4cd8962 Refactor checkboxlist to show a prettier checkbox than the default browser one 2018-10-28 16:35:37 +01:00
159 changed files with 1888 additions and 1663 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/dev-v8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
+2 -4
View File
@@ -103,11 +103,9 @@ There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean
npm install -g gulp
npm install -g gulp-cli
npm cache clean --force
npm install
gulp build
npm run build
```
2. "The rest" is a C# based codebase, with some traces of our WebForms past but mostly ASP.NET MVC based these days. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
+1 -1
View File
@@ -2,7 +2,7 @@
- [ ] I have added steps to test this contribution in the description below
If there's an existing issue for this PR then this fixes: <!-- link to the issue here! -->
If there's an existing issue for this PR then this fixes <!-- link to the issue here! -->
### Description
<!-- A description of the changes proposed in the pull-request and how to test these changes -->
+5 -3
View File
@@ -1,7 +1,9 @@
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
[![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
[![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=dev-v8)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS/tree/dev-v7) to go to the v7 branch._
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
+1 -1
View File
@@ -5,7 +5,7 @@
* [Visual Studio 2017 Community (Free)](https://www.visualstudio.com/vs/community/), or Professional, Enterprise, etc... _(**Minimum of Version 15.7** or higher, this is important, you WILL get issues with lesser versions)_
* .NET Framework 4.7.2 installed, get it here: https://www.microsoft.com/net/download/thank-you/net472?survey=false
* .NET Framework 4.7.2 developer pack, get it here: https://www.microsoft.com/net/download/thank-you/net472-developer-pack _(be sure this is the ENU file which will be named `NDP472-DevPack-ENU.exe`)_
* Clone the Umbraco repository using the `temp8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b temp8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `temp8` branch you **might** get problems)_
* Clone the Umbraco repository using the `dev-v8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b dev-v8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `dev-v8` branch you **might** get problems)_
### Start the solution
+11 -2
View File
@@ -15,7 +15,12 @@
[Parameter(Mandatory=$false)]
[Alias("c")]
[Alias("cont")]
[switch] $continue = $false
[switch] $continue = $false,
# execute a command
[Parameter(Mandatory=$false, ValueFromRemainingArguments=$true)]
[String[]]
$command
)
# ################################################################
@@ -475,7 +480,11 @@
# run
if (-not $get)
{
$ubuild.Build()
if ($command.Length -eq 0)
{
$command = @( "Build" )
}
$ubuild.RunMethod($command);
if ($ubuild.OnError()) { return }
}
if ($get) { return $ubuild }
+2 -2
View File
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.0.2")]
[assembly: AssemblyInformationalVersion("8.0.2")]
[assembly: AssemblyFileVersion("8.1.0")]
[assembly: AssemblyInformationalVersion("8.1.0")]
+34 -91
View File
@@ -70,23 +70,7 @@ namespace Umbraco.Core.Composing
}
}
internal IEnumerable<Type> PrepareComposerTypes()
{
var requirements = GetRequirements();
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
var sortedComposerTypes = SortComposers(requirements);
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal Dictionary<Type, List<Type>> GetRequirements(bool throwOnMissing = true)
private IEnumerable<Type> PrepareComposerTypes()
{
// create a list, remove those that cannot be enabled due to runtime level
var composerTypeList = _composerTypes
@@ -105,69 +89,25 @@ namespace Umbraco.Core.Composing
// enable or disable composers
EnableDisableComposers(composerTypeList);
void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2)
where TAttribute : Attribute
{
foreach (var attribute in type.GetCustomAttributes<TAttribute>())
{
var typeInAttribute = getTypeInAttribute(attribute);
if (typeInAttribute != null && // if the attribute references a type ...
typeInAttribute.IsInterface && // ... which is an interface ...
typeof(IComposer).IsAssignableFrom(typeInAttribute) && // ... which implements IComposer ...
!iset.Contains(typeInAttribute)) // ... which is not already in the list
{
// add it to the new list
iset.Add(typeInAttribute);
set2.Add(typeInAttribute);
// add all its interfaces implementing IComposer
foreach (var i in typeInAttribute.GetInterfaces().Where(x => typeof(IComposer).IsAssignableFrom(x)))
{
iset.Add(i);
set2.Add(i);
}
}
}
}
// gather interfaces too
var interfaces = new HashSet<Type>(composerTypeList.SelectMany(x => x.GetInterfaces().Where(y => typeof(IComposer).IsAssignableFrom(y))));
composerTypeList.AddRange(interfaces);
var list1 = composerTypeList;
while (list1.Count > 0)
{
var list2 = new List<Type>();
foreach (var t in list1)
{
GatherInterfaces<ComposeAfterAttribute>(t, a => a.RequiredType, interfaces, list2);
GatherInterfaces<ComposeBeforeAttribute>(t, a => a.RequiringType, interfaces, list2);
}
composerTypeList.AddRange(list2);
list1 = list2;
}
// sort the composers according to their dependencies
var requirements = new Dictionary<Type, List<Type>>();
foreach (var type in composerTypeList)
requirements[type] = null;
foreach (var type in composerTypeList) requirements[type] = null;
foreach (var type in composerTypeList)
{
GatherRequirementsFromAfterAttribute(type, composerTypeList, requirements, throwOnMissing);
GatherRequirementsFromBeforeAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequireAttribute(type, composerTypeList, requirements);
GatherRequirementsFromRequiredByAttribute(type, composerTypeList, requirements);
}
return requirements;
}
// only for debugging, this is verbose
//_logger.Debug<Composers>(GetComposersReport(requirements));
internal IEnumerable<Type> SortComposers(Dictionary<Type, List<Type>> requirements)
{
// sort composers
var graph = new TopoGraph<Type, KeyValuePair<Type, List<Type>>>(kvp => kvp.Key, kvp => kvp.Value);
graph.AddItems(requirements);
List<Type> sortedComposerTypes;
try
{
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).Where(x => !x.IsInterface).ToList();
sortedComposerTypes = graph.GetSortedItems().Select(x => x.Key).ToList();
}
catch (Exception e)
{
@@ -177,37 +117,40 @@ namespace Umbraco.Core.Composing
throw;
}
// bit verbose but should help for troubleshooting
//var text = "Ordered Composers: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComposerTypes) + Environment.NewLine;
_logger.Debug<Composers>("Ordered Composers: {SortedComposerTypes}", sortedComposerTypes);
return sortedComposerTypes;
}
internal static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
private static string GetComposersReport(Dictionary<Type, List<Type>> requirements)
{
var text = new StringBuilder();
text.AppendLine("Composers & Dependencies:");
text.AppendLine(" < compose before");
text.AppendLine(" > compose after");
text.AppendLine(" : implements");
text.AppendLine(" = depends");
text.AppendLine();
bool HasReq(IEnumerable<Type> types, Type type)
=> types.Any(x => type.IsAssignableFrom(x) && !x.IsInterface);
foreach (var kvp in requirements)
{
var type = kvp.Key;
text.AppendLine(type.FullName);
foreach (var attribute in type.GetCustomAttributes<ComposeAfterAttribute>())
{
var weak = !(attribute.RequiredType.IsInterface ? attribute.Weak == false : attribute.Weak != true);
text.AppendLine(" > " + attribute.RequiredType +
(weak ? " (weak" : " (strong") + (HasReq(requirements.Keys, attribute.RequiredType) ? ", found" : ", missing") + ")");
}
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in type.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" < " + attribute.RequiringType);
text.AppendLine(" -< " + attribute.RequiringType);
foreach (var i in type.GetInterfaces())
{
text.AppendLine(" : " + i.FullName);
foreach (var attribute in i.GetCustomAttributes<ComposeAfterAttribute>())
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in i.GetCustomAttributes<ComposeBeforeAttribute>())
text.AppendLine(" -< " + attribute.RequiringType);
}
if (kvp.Value != null)
foreach (var t in kvp.Value)
text.AppendLine(" = " + t);
@@ -278,16 +221,16 @@ namespace Umbraco.Core.Composing
types.Remove(kvp.Key);
}
private static void GatherRequirementsFromAfterAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements, bool throwOnMissing = true)
private static void GatherRequirementsFromRequireAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'require' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var afterAttributes = type
var requireAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeAfterAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeAfterAttribute>()); // those marking the composer
// what happens in case of conflicting attributes (different strong/weak for same type) is not specified.
foreach (var attr in afterAttributes)
foreach (var attr in requireAttributes)
{
if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below)
@@ -295,13 +238,13 @@ namespace Umbraco.Core.Composing
// unless strong, and then require at least one enabled composer implementing that interface
if (attr.RequiredType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList();
if (implems.Count > 0)
{
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].AddRange(implems);
}
else if (attr.Weak == false && throwOnMissing) // if explicitly set to !weak, is strong, else is weak
else if (attr.Weak == false) // if explicitly set to !weak, is strong, else is weak
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
// requiring a class = require that the composer is enabled
@@ -313,28 +256,28 @@ namespace Umbraco.Core.Composing
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].Add(attr.RequiredType);
}
else if (attr.Weak != true && throwOnMissing) // if not explicitly set to weak, is strong
else if (attr.Weak != true) // if not explicitly set to weak, is strong
throw new Exception($"Broken composer dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
}
}
private static void GatherRequirementsFromBeforeAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
private static void GatherRequirementsFromRequiredByAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'required' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var beforeAttributes = type
var requiredAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<ComposeBeforeAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<ComposeBeforeAttribute>()); // those marking the composer
foreach (var attr in beforeAttributes)
foreach (var attr in requiredAttributes)
{
if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below)
// required by an interface = by any enabled composer implementing this that interface
if (attr.RequiringType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x) && !x.IsInterface).ToList();
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList();
foreach (var implem in implems)
{
if (requirements[implem] == null) requirements[implem] = new List<Type>();
+5 -1
View File
@@ -133,7 +133,11 @@ namespace Umbraco.Core.Composing
Configs.RegisterWith(_register);
return _register.CreateFactory();
IFactory factory = null;
// ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory();
return factory;
}
/// <summary>
-22
View File
@@ -203,28 +203,6 @@ namespace Umbraco.Core
composition.RegisterUnique(_ => registrar);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, Func<IFactory, DatabaseServerMessengerOptions> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, DatabaseServerMessengerOptions options)
{
composition.RegisterUnique(_ => options);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
@@ -4,6 +4,7 @@ using System.Linq;
using System.Net.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Xml.Linq;
using Umbraco.Core.IO;
@@ -17,16 +18,15 @@ namespace Umbraco.Core.Configuration
/// </summary>
public class GlobalSettings : IGlobalSettings
{
private string _localTempPath;
#region Private static fields
// TODO these should not be static
private static string _reservedPaths;
private static string _reservedUrls;
//ensure the built on (non-changeable) reserved paths are there at all times
internal const string StaticReservedPaths = "~/app_plugins/,~/install/,~/mini-profiler-resources/,"; //must end with a comma!
internal const string StaticReservedUrls = "~/config/splashes/noNodes.aspx,~/.well-known,"; //must end with a comma!
#endregion
/// <summary>
/// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config)
@@ -131,7 +131,7 @@ namespace Umbraco.Core.Configuration
: "~/App_Data/umbraco.config";
}
}
/// <summary>
/// Gets the path to umbraco's root directory (/umbraco by default).
/// </summary>
@@ -163,7 +163,7 @@ namespace Umbraco.Core.Configuration
SaveSetting(Constants.AppSettings.ConfigurationStatus, value);
}
}
/// <summary>
/// Saves a setting into the configuration file.
/// </summary>
@@ -206,7 +206,7 @@ namespace Umbraco.Core.Configuration
ConfigurationManager.RefreshSection("appSettings");
}
}
/// <summary>
/// Gets a value indicating whether umbraco is running in [debug mode].
/// </summary>
@@ -250,7 +250,7 @@ namespace Umbraco.Core.Configuration
}
}
}
/// <summary>
/// Returns the number of days that should take place between version checks.
/// </summary>
@@ -269,7 +269,7 @@ namespace Umbraco.Core.Configuration
}
}
}
/// <inheritdoc />
public LocalTempStorage LocalTempStorageLocation
{
@@ -288,25 +288,43 @@ namespace Umbraco.Core.Configuration
{
get
{
if (_localTempPath != null)
return _localTempPath;
switch (LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
return System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
return _localTempPath = System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
case LocalTempStorage.EnvironmentTemp:
// include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId
var appDomainHash = HttpRuntime.AppDomainAppId.GenerateHash();
return System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash);
// environment temp is unique, we need a folder per site
// use a hash
// combine site name and application id
// site name is a Guid on Cloud
// application id is eg /LM/W3SVC/123456/ROOT
// the combination is unique on one server
// and, if a site moves from worker A to B and then back to A...
// hopefully it gets a new Guid or new application id?
var siteName = HostingEnvironment.SiteName;
var applicationId = HostingEnvironment.ApplicationID; // ie HttpRuntime.AppDomainAppId
var hashString = siteName + "::" + applicationId;
var hash = hashString.GenerateHash();
var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash);
return _localTempPath = System.IO.Path.Combine(siteTemp, "umbraco.config");
//case LocalTempStorage.Default:
//case LocalTempStorage.Unknown:
default:
return IOHelper.MapPath("~/App_Data/TEMP");
return _localTempPath = IOHelper.MapPath("~/App_Data/TEMP");
}
}
}
/// <summary>
/// Gets the default UI language.
/// </summary>
+5 -11
View File
@@ -7,7 +7,6 @@ using System.Web;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPoco.Expressions;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
@@ -53,8 +52,8 @@ namespace Umbraco.Core
return ContentStatus.Unpublished;
}
#endregion
/// <summary>
@@ -135,14 +134,9 @@ namespace Umbraco.Core
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, HttpPostedFileBase postedFile, string culture = null, string segment = null)
{
content.SetValue(contentTypeBaseServiceProvider, propertyTypeAlias, postedFile.FileName, postedFile.InputStream, culture, segment);
}
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
/// you need to store the file by yourself using Store and then figure out
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
{
if (filename == null || filestream == null) return;
-17
View File
@@ -10,23 +10,6 @@ namespace Umbraco.Core
///</summary>
public static class EnumerableExtensions
{
internal static bool HasDuplicates<T>(this IEnumerable<T> items, bool includeNull)
{
var hs = new HashSet<T>();
foreach (var item in items)
{
if (item != null || includeNull)
{
if (!hs.Add(item))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Wraps this object instance into an IEnumerable{T} consisting of a single item.
/// </summary>
@@ -8,12 +8,9 @@ namespace Umbraco.Core.IO.MediaPathSchemes
/// </summary>
/// <remarks>
/// <para>Path is "{combinedGuid}/{filename}" where combinedGuid is a combination of itemGuid and propertyGuid.</para>
/// <para>This scheme is dangerous, as it does not prevent potential collisions.</para>
/// </remarks>
public class CombinedGuidsMediaPathScheme : IMediaPathScheme
{
private const int DirectoryLength = 8;
/// <inheritdoc />
public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null)
{
@@ -21,7 +18,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes
// for a single content cannot store two different files with the same name
var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid);
var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); // see also HexEncoder, we may want to fragment path eg 12/e4/f3...
var directory = HexEncoder.Encode(combinedGuid.ToByteArray()/*'/', 2, 4*/); // could use ext to fragment path eg 12/e4/f2/...
return Path.Combine(directory, filename).Replace('\\', '/');
}
@@ -7,7 +7,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes
/// Implements a unique directory media path scheme.
/// </summary>
/// <remarks>
/// <para>This scheme provides short paths, yet handle potential collisions.</para>
/// <para>This scheme provides deterministic short paths, with potential collisions.</para>
/// </remarks>
public class UniqueMediaPathScheme : IMediaPathScheme
{
@@ -16,23 +16,22 @@ namespace Umbraco.Core.IO.MediaPathSchemes
/// <inheritdoc />
public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null)
{
string directory;
// no point "combining" guids if all we want is some random guid - just get a new one
// and then, because we don't want collisions, ensure that the directory does not already exist
// (should be quite rare, but eh...)
do
{
var combinedGuid = Guid.NewGuid();
directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); // see also HexEncoder, we may want to fragment path eg 12/e4/f3...
} while (fileSystem.DirectoryExists(directory));
var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid);
var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength);
return Path.Combine(directory, filename).Replace('\\', '/');
}
/// <inheritdoc />
public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => Path.GetDirectoryName(filepath);
/// <remarks>
/// <para>Returning null so that <see cref="MediaFileSystem.DeleteMediaFiles"/> does *not*
/// delete any directory. This is because the above shortening of the Guid to 8 chars
/// means we're increasing the risk of collision, and we don't want to delete files
/// belonging to other media items.</para>
/// <para>And, at the moment, we cannot delete directory "only if it is empty" because of
/// race conditions. We'd need to implement locks in <see cref="MediaFileSystem"/> for
/// this.</para>
/// </remarks>
public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => null;
}
}
}
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Serilog.Events;
using Serilog.Formatting.Compact.Reader;
@@ -10,13 +11,15 @@ namespace Umbraco.Core.Logging.Viewer
internal class JsonLogViewer : LogViewerSourceBase
{
private readonly string _logsPath;
private readonly ILogger _logger;
public JsonLogViewer(string logsPath = "", string searchPath = "") : base(searchPath)
public JsonLogViewer(ILogger logger, string logsPath = "", string searchPath = "") : base(searchPath)
{
if (string.IsNullOrEmpty(logsPath))
logsPath = $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\";
_logsPath = logsPath;
_logger = logger;
}
private const int FileSizeCap = 100;
@@ -77,8 +80,14 @@ namespace Umbraco.Core.Logging.Viewer
using (var stream = new StreamReader(fs))
{
var reader = new LogEventReader(stream);
while (reader.TryRead(out var evt))
while (TryRead(reader, out var evt))
{
//We may get a null if log line is malformed
if (evt == null)
{
continue;
}
if (count > skip + take)
{
break;
@@ -105,5 +114,21 @@ namespace Umbraco.Core.Logging.Viewer
return logs;
}
private bool TryRead(LogEventReader reader, out LogEvent evt)
{
try
{
return reader.TryRead(out evt);
}
catch (JsonReaderException ex)
{
// As we are reading/streaming one line at a time in the JSON file
// Thus we can not report the line number, as it will always be 1
_logger.Error<JsonLogViewer>(ex, "Unable to parse a line in the JSON log file");
evt = null;
return true;
}
}
}
}
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Logging.Viewer
{
public void Compose(Composition composition)
{
composition.SetLogViewer(_ => new JsonLogViewer());
composition.SetLogViewer(_ => new JsonLogViewer(composition.Logger));
}
}
}
@@ -226,8 +226,6 @@ namespace Umbraco.Core.Migrations.Install
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 32, UniqueId = 32.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLockoutDate, Name = Constants.Conventions.Member.LastLockoutDateLabel, SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 33, UniqueId = 33.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastLoginDate, Name = Constants.Conventions.Member.LastLoginDateLabel, SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 34, UniqueId = 34.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = 11, Alias = Constants.Conventions.Member.LastPasswordChangeDate, Name = Constants.Conventions.Member.LastPasswordChangeDateLabel, SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte) ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 35, UniqueId = 35.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordQuestion, Name = Constants.Conventions.Member.PasswordQuestionLabel, SortOrder = 7, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
_database.Insert(Constants.DatabaseSchema.Tables.PropertyType, "id", false, new PropertyTypeDto { Id = 36, UniqueId = 36.ToGuid(), DataTypeId = Constants.DataTypes.LabelDateTime, ContentTypeId = 1044, PropertyTypeGroupId = null, Alias = Constants.Conventions.Member.PasswordAnswer, Name = Constants.Conventions.Member.PasswordAnswerLabel, SortOrder = 8, Mandatory = false, ValidationRegExp = null, Description = null, Variations = (byte)ContentVariation.Nothing });
}
@@ -0,0 +1,16 @@
using Umbraco.Core.Migrations.PostMigrations;
namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
{
public class ChangeNuCacheJsonFormat : MigrationBase
{
public ChangeNuCacheJsonFormat(IMigrationContext context) : base(context)
{ }
public override void Migrate()
{
// nothing - just adding the post-migration
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
}
}
+3 -19
View File
@@ -95,21 +95,6 @@ namespace Umbraco.Core.Models
protected void PropertyTypesChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//enable this to detect duplicate property aliases. We do want this, however making this change in a
//patch release might be a little dangerous
////detect if there are any duplicate aliases - this cannot be allowed
//if (e.Action == NotifyCollectionChangedAction.Add
// || e.Action == NotifyCollectionChangedAction.Replace)
//{
// var allAliases = _noGroupPropertyTypes.Concat(PropertyGroups.SelectMany(x => x.PropertyTypes)).Select(x => x.Alias);
// if (allAliases.HasDuplicates(false))
// {
// var newAliases = string.Join(", ", e.NewItems.Cast<PropertyType>().Select(x => x.Alias));
// throw new InvalidOperationException($"Other property types already exist with the aliases: {newAliases}");
// }
//}
OnPropertyChanged(nameof(PropertyTypes));
}
@@ -412,16 +397,15 @@ namespace Umbraco.Core.Models
var group = PropertyGroups[propertyGroupName];
if (group == null) return;
// first remove the group
PropertyGroups.RemoveItem(propertyGroupName);
// Then re-assign the group's properties to no group
// re-assign the group's properties to no group
foreach (var property in group.PropertyTypes)
{
property.PropertyGroupId = null;
_noGroupPropertyTypes.Add(property);
}
// actually remove the group
PropertyGroups.RemoveItem(propertyGroupName);
OnPropertyChanged(nameof(PropertyGroups));
}
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models
public class PropertyCollection : KeyedCollection<string, Property>, INotifyCollectionChanged, IDeepCloneable
{
private readonly object _addLocker = new object();
internal Action OnAdd;
internal Func<Property, bool> AdditionValidator { get; set; }
/// <summary>
@@ -49,12 +49,10 @@ namespace Umbraco.Core.Models
/// </summary>
internal void Reset(IEnumerable<Property> properties)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var property in properties)
Add(property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
/// <summary>
@@ -62,9 +60,8 @@ namespace Umbraco.Core.Models
/// </summary>
protected override void SetItem(int index, Property property)
{
var oldItem = index >= 0 ? this[index] : property;
base.SetItem(index, property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, property, oldItem));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property, index));
}
/// <summary>
@@ -123,8 +120,10 @@ namespace Umbraco.Core.Models
}
}
//collection events will be raised in InsertItem with Add
base.Add(property);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, property));
}
}
@@ -19,6 +19,9 @@ namespace Umbraco.Core.Models
{
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
// TODO: this doesn't seem to be used anywhere
internal Action OnAdd;
internal PropertyGroupCollection()
{ }
@@ -34,19 +37,16 @@ namespace Umbraco.Core.Models
/// <remarks></remarks>
internal void Reset(IEnumerable<PropertyGroup> groups)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var group in groups)
Add(group);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, PropertyGroup item)
{
var oldItem = index >= 0 ? this[index] : item;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
@@ -84,7 +84,6 @@ namespace Umbraco.Core.Models
if (keyExists)
throw new Exception($"Naming conflict: Changing the name of PropertyGroup '{item.Name}' would result in duplicates");
//collection events will be raised in SetItem
SetItem(IndexOfKey(item.Id), item);
return;
}
@@ -97,14 +96,16 @@ namespace Umbraco.Core.Models
var exists = Contains(key);
if (exists)
{
//collection events will be raised in SetItem
SetItem(IndexOfKey(key), item);
return;
}
}
}
//collection events will be raised in InsertItem
base.Add(item);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
finally
{
@@ -19,6 +19,9 @@ namespace Umbraco.Core.Models
[IgnoreDataMember]
private readonly ReaderWriterLockSlim _addLocker = new ReaderWriterLockSlim();
// TODO: This doesn't seem to be used
[IgnoreDataMember]
internal Action OnAdd;
internal PropertyTypeCollection(bool supportsPublishing)
{
@@ -40,44 +43,36 @@ namespace Umbraco.Core.Models
/// <remarks></remarks>
internal void Reset(IEnumerable<PropertyType> properties)
{
//collection events will be raised in each of these calls
Clear();
//collection events will be raised in each of these calls
foreach (var property in properties)
Add(property);
Add(property);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void SetItem(int index, PropertyType item)
{
item.SupportsPublishing = SupportsPublishing;
var oldItem = index >= 0 ? this[index] : item;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, oldItem));
item.PropertyChanged += Item_PropertyChanged;
base.SetItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
protected override void RemoveItem(int index)
{
var removed = this[index];
base.RemoveItem(index);
removed.PropertyChanged -= Item_PropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
}
protected override void InsertItem(int index, PropertyType item)
{
item.SupportsPublishing = SupportsPublishing;
base.InsertItem(index, item);
base.InsertItem(index, item);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
item.PropertyChanged += Item_PropertyChanged;
}
protected override void ClearItems()
{
base.ClearItems();
foreach (var item in this)
item.PropertyChanged -= Item_PropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
@@ -96,7 +91,6 @@ namespace Umbraco.Core.Models
var exists = Contains(key);
if (exists)
{
//collection events will be raised in SetItem
SetItem(IndexOfKey(key), item);
return;
}
@@ -109,8 +103,10 @@ namespace Umbraco.Core.Models
item.SortOrder = this.Max(x => x.SortOrder) + 1;
}
//collection events will be raised in InsertItem
base.Add(item);
OnAdd?.Invoke();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
finally
{
@@ -119,17 +115,6 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// Occurs when a property changes on a PropertyType that exists in this collection
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var propType = (PropertyType)sender;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, propType, propType));
}
/// <summary>
/// Determines whether this collection contains a <see cref="Property"/> whose alias matches the specified PropertyType.
/// </summary>
@@ -9,10 +9,11 @@ namespace Umbraco.Core.Persistence.Factories
{
internal static class MemberTypeReadOnlyFactory
{
public static IMemberType BuildEntity(MemberTypeReadOnlyDto dto)
public static IMemberType BuildEntity(MemberTypeReadOnlyDto dto, out bool needsSaving)
{
var standardPropertyTypes = Constants.Conventions.Member.GetStandardPropertyTypeStubs();
needsSaving = false;
var memberType = new MemberType(dto.ParentId);
try
@@ -40,29 +41,27 @@ namespace Umbraco.Core.Persistence.Factories
var propertyTypeGroupCollection = GetPropertyTypeGroupCollection(dto, memberType, standardPropertyTypes);
memberType.PropertyGroups = propertyTypeGroupCollection;
memberType.NoGroupPropertyTypes = GetNoGroupPropertyTypes(dto, memberType, standardPropertyTypes);
var propertyTypes = GetPropertyTypes(dto, memberType, standardPropertyTypes);
// By Convention we add 9 standard PropertyTypes - This is only here to support loading of types that didn't have these conventions before.
// In theory this should not happen! The only reason this did happen was because:
// A) we didn't install all of the default membership properties by default
// B) the existing data is super old when we didn't store membership properties at all (very very old)
// So what to do? We absolutely do not want to update the database when only a read was requested, this will cause problems
// so we should just add these virtual properties, they will have no ids but they will have aliases and this should be perfectly
// fine for any membership provider logic to work since neither membership providers or asp.net identity care about whether a property
// has an ID or not.
// When the member type is saved, all the properties will correctly be added.
//This will add this group if it doesn't exist, no need to error check here
memberType.AddPropertyGroup(Constants.Conventions.Member.StandardPropertiesGroupName);
//By Convention we add 9 standard PropertyTypes - This is only here to support loading of types that didn't have these conventions before.
foreach (var standardPropertyType in standardPropertyTypes)
{
//This will add the property if it doesn't exist, no need to error check here
memberType.AddPropertyType(standardPropertyType.Value, Constants.Conventions.Member.StandardPropertiesGroupName);
if (dto.PropertyTypes.Any(x => x.Alias.Equals(standardPropertyType.Key))) continue;
// beware!
// means that we can return a memberType "from database" that has some property types
// that do *not* come from the database and therefore are incomplete eg have no key,
// no id, no dataTypeDefinitionId - ouch! - better notify caller of the situation
needsSaving = true;
//Add the standard PropertyType to the current list
propertyTypes.Add(standardPropertyType.Value);
//Internal dictionary for adding "MemberCanEdit", "VisibleOnProfile", "IsSensitive" properties to each PropertyType
if (!memberType.MemberTypePropertyTypes.TryGetValue(standardPropertyType.Key, out var memberTypePropertyProfile))
memberType.MemberTypePropertyTypes[standardPropertyType.Key] = new MemberTypePropertyProfileAccess(false, false, false);
memberType.MemberTypePropertyTypes.Add(standardPropertyType.Key,
new MemberTypePropertyProfileAccess(false, false, false));
}
memberType.NoGroupPropertyTypes = propertyTypes;
return memberType;
}
@@ -148,14 +147,13 @@ namespace Umbraco.Core.Persistence.Factories
return propertyGroups;
}
private static List<PropertyType> GetNoGroupPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary<string, PropertyType> standardProps)
private static List<PropertyType> GetPropertyTypes(MemberTypeReadOnlyDto dto, MemberType memberType, Dictionary<string, PropertyType> standardProps)
{
//Find PropertyTypes that does not belong to a PropertyTypeGroup
var propertyTypes = new List<PropertyType>();
foreach (var typeDto in dto.PropertyTypes.Where(x => (x.PropertyTypeGroupId.HasValue == false || x.PropertyTypeGroupId.Value == 0) && x.Id.HasValue))
{
//Internal dictionary for adding "MemberCanEdit" and "VisibleOnProfile" properties to each PropertyType
memberType.MemberTypePropertyTypes.Add(typeDto.Alias,
new MemberTypePropertyProfileAccess(typeDto.ViewOnProfile, typeDto.CanEdit, typeDto.IsSensitive));
@@ -292,7 +292,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (psql.Arguments[i] is string s && s == "[[[ISOCODE]]]")
{
psql.Arguments[i] = ordering.Culture;
break;
}
}
}
@@ -982,8 +982,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// invariant: left join will yield NULL and we must use pcv to determine published
// variant: left join may yield NULL or something, and that determines published
var joins = Sql()
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype");
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype")
// left join on optional culture variation
//the magic "[[[ISOCODE]]]" parameter value will be replaced in ContentRepositoryBase.GetPage() by the actual ISO code
.LeftJoin<ContentVersionCultureVariationDto>(nested =>
nested.InnerJoin<LanguageDto>("langp").On<ContentVersionCultureVariationDto, LanguageDto>((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == "[[[ISOCODE]]]", "ccvp", "langp"), "ccvp")
.On<ContentVersionDto, ContentVersionCultureVariationDto>((version, ccv) => version.Id == ccv.VersionId, aliasLeft: "pcv", aliasRight: "ccvp");
sql = InsertJoins(sql, joins);
@@ -993,7 +999,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// when invariant, ie 'variations' does not have the culture flag (value 1), use the global 'published' flag on pcv.id,
// otherwise check if there's a version culture variation for the lang, via ccv.id
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccv.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccvp.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
sql = Sql(sqlText, sql.Arguments);
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
protected override IIdentityUserLogin PerformGet(int id)
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { id = id });
sql.Where(GetBaseWhereClause(), new { Id = id });
var dto = Database.Fetch<ExternalLoginDto>(SqlSyntax.SelectTop(sql, 1)).FirstOrDefault();
if (dto == null)
@@ -308,7 +308,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (dtos == null || dtos.Any() == false)
return Enumerable.Empty<IMemberType>();
return dtos.Select(MemberTypeReadOnlyFactory.BuildEntity).ToList();
return dtos.Select(x =>
{
bool needsSaving;
var memberType = MemberTypeReadOnlyFactory.BuildEntity(x, out needsSaving);
if (needsSaving) PersistUpdatedItem(memberType);
return memberType;
}).ToList();
}
/// <summary>
@@ -1,8 +1,8 @@
namespace Umbraco.Core.PropertyEditors
{
public class DropDownFlexibleConfiguration : ValueListConfiguration
internal class DropDownFlexibleConfiguration : ValueListConfiguration
{
[ConfigurationField("multiple", "Enable multiple choice", "boolean", Description = "When checked, the dropdown will be a select multiple / combo box style dropdown.")]
public bool Multiple { get; set; }
}
}
}
+1 -7
View File
@@ -5,7 +5,6 @@ using System.Linq;
using System.Reflection;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
@@ -71,15 +70,10 @@ namespace Umbraco.Core.Runtime
// objects.
using (var timer = profilingLogger.TraceDuration<CoreRuntime>(
$"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()}.",
$"Booting Umbraco {UmbracoVersion.SemanticVersion.ToSemanticString()} on {NetworkHelper.MachineName}.",
"Booted.",
"Boot failed."))
{
logger.Info<CoreRuntime>("Booting site '{HostingSiteName}', app '{HostingApplicationID}', path '{HostingPhysicalPath}', server '{MachineName}'.",
HostingEnvironment.SiteName,
HostingEnvironment.ApplicationID,
HostingEnvironment.ApplicationPhysicalPath,
NetworkHelper.MachineName);
logger.Debug<CoreRuntime>("Runtime: {Runtime}", GetType().FullName);
// application environment
@@ -2,61 +2,39 @@
namespace Umbraco.Core.Scoping
{
/// <summary>
/// Provides a base class for scope contextual objects.
/// </summary>
/// <remarks>
/// <para>A scope contextual object is enlisted in the current scope context,
/// if any, and released when the context exists. It must be used in a 'using'
/// block, and will be released when disposed, if not part of a scope.</para>
/// </remarks>
// base class for an object that will be enlisted in scope context, if any. it
// must be used in a 'using' block, and if not scoped, released when disposed,
// else when scope context runs enlisted actions
public abstract class ScopeContextualBase : IDisposable
{
private bool _scoped;
private bool _using, _scoped;
/// <summary>
/// Gets a contextual object.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="scopeProvider">A scope provider.</param>
/// <param name="key">A context key for the object.</param>
/// <param name="ctor">A function producing the contextual object.</param>
/// <returns>The contextual object.</returns>
/// <remarks>
/// <para></para>
/// </remarks>
public static T Get<T>(IScopeProvider scopeProvider, string key, Func<bool, T> ctor)
where T : ScopeContextualBase
{
// no scope context = create a non-scoped object
var scopeContext = scopeProvider.Context;
if (scopeContext == null)
return ctor(false);
// create & enlist the scoped object
var w = scopeContext.Enlist("ScopeContextualBase_" + key,
() => ctor(true),
(completed, item) => { item.Release(completed); });
if (w._using) throw new InvalidOperationException("panic: used.");
w._using = true;
w._scoped = true;
return w;
}
/// <inheritdoc />
/// <remarks>
/// <para>If not scoped, then this releases the contextual object.</para>
/// </remarks>
public void Dispose()
{
_using = false;
if (_scoped == false)
Release(true);
}
/// <summary>
/// Releases the contextual object.
/// </summary>
/// <param name="completed">A value indicating whether the scoped operation completed.</param>
public abstract void Release(bool completed);
}
}
@@ -1902,8 +1902,9 @@ namespace Umbraco.Core.Services.Implement
content.ParentId = parentId;
// get the level delta (old pos to new pos)
// note that recycle bin (id:-20) level is 0!
var levelDelta = 1 - content.Level + (parent?.Level ?? 0);
var levelDelta = parent == null
? 1 - content.Level + (parentId == Constants.System.RecycleBinContent ? 1 : 0)
: parent.Level + 1 - content.Level;
var paths = new Dictionary<int, string>();
@@ -2861,7 +2862,7 @@ namespace Umbraco.Core.Services.Implement
{
if (blueprint == null) throw new ArgumentNullException(nameof(blueprint));
var contentType = _contentTypeRepository.Get(blueprint.ContentType.Id);
var contentType = GetContentType(blueprint.ContentType.Alias);
var content = new Content(name, -1, contentType);
content.Path = string.Concat(content.ParentId.ToString(), ",", content.Id);
@@ -289,7 +289,7 @@ namespace Umbraco.Core.Services.Implement
scope.Events.Dispatch(Saved, this, saveEventArgs);
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, TreeChangeTypes.RefreshNode).ToEventArgs());
}
if (withIdentity == false)
return;
@@ -716,7 +716,7 @@ namespace Umbraco.Core.Services.Implement
#endregion
#region Delete
/// <summary>
/// Permanently deletes an <see cref="IMedia"/> object
/// </summary>
@@ -975,8 +975,9 @@ namespace Umbraco.Core.Services.Implement
media.ParentId = parentId;
// get the level delta (old pos to new pos)
// note that recycle bin (id:-20) level is 0!
var levelDelta = 1 - media.Level + (parent?.Level ?? 0);
var levelDelta = parent == null
? 1 - media.Level + (parentId == Constants.System.RecycleBinMedia ? 1 : 0)
: parent.Level + 1 - media.Level;
var paths = new Dictionary<int, string>();
@@ -1023,7 +1024,6 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
/// </summary>
/// <param name="userId">Optional Id of the User emptying the Recycle Bin</param>
public OperationResult EmptyRecycleBin()
{
var nodeObjectType = Constants.ObjectTypes.Media;
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Strings
/// Converts an Utf8 string into an Ascii string.
/// </summary>
/// <param name="text">The text to convert.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The converted text.</returns>
public static string ToAsciiString(string text, char fail = '?')
{
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Strings
/// Converts an Utf8 string into an array of Ascii characters.
/// </summary>
/// <param name="text">The text to convert.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The converted text.</returns>
public static char[] ToAsciiCharArray(string text, char fail = '?')
{
@@ -66,7 +66,7 @@ namespace Umbraco.Core.Strings
/// </summary>
/// <param name="input">The input array.</param>
/// <param name="output">The output array.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The number of characters in the output array.</returns>
/// <remarks>The caller must ensure that the output array is big enough.</remarks>
/// <exception cref="OverflowException">The output array is not big enough.</exception>
@@ -112,7 +112,7 @@ namespace Umbraco.Core.Strings
/// <param name="ipos">The input position.</param>
/// <param name="output">The output array.</param>
/// <param name="opos">The output position.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <remarks>
/// <para>Adapted from various sources on the 'net including <c>Lucene.Net.Analysis.ASCIIFoldingFilter</c>.</para>
/// <para>Input should contain Utf8 characters exclusively and NOT Unicode.</para>
+2
View File
@@ -28,6 +28,8 @@ namespace Umbraco.Examine
/// </remarks>
internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled);
//TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression
/// <summary>
/// Returns all index fields that are culture specific (suffixed)
/// </summary>
+27 -213
View File
@@ -5,7 +5,6 @@ using Moq;
using NUnit.Framework;
using Umbraco.Core.Scoping;
using Umbraco.Web.PublishedCache.NuCache;
using Umbraco.Web.PublishedCache.NuCache.Snap;
namespace Umbraco.Tests.Cache
{
@@ -389,7 +388,8 @@ namespace Umbraco.Tests.Cache
// collect liveGen
GC.Collect();
Assert.IsTrue(d.Test.GenObjs.TryPeek(out var genObj));
SnapDictionary<int, string>.GenerationObject genObj;
Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj));
genObj = null;
// in Release mode, it works, but in Debug mode, the weak reference is still alive
@@ -399,14 +399,14 @@ namespace Umbraco.Tests.Cache
GC.Collect();
#endif
Assert.IsTrue(d.Test.GenObjs.TryPeek(out genObj));
Assert.IsFalse(genObj.WeakGenRef.IsAlive); // snapshot is gone, along with its reference
Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj));
Assert.IsFalse(genObj.WeakReference.IsAlive); // snapshot is gone, along with its reference
await d.CollectAsync();
Assert.AreEqual(0, d.Test.GetValues(1).Length); // null value is gone
Assert.AreEqual(0, d.Count); // item is gone
Assert.AreEqual(0, d.Test.GenObjs.Count);
Assert.AreEqual(0, d.Test.GenerationObjects.Count);
Assert.AreEqual(0, d.SnapCount); // snapshot is gone
Assert.AreEqual(0, d.GenCount); // and generation has been dequeued
}
@@ -632,7 +632,7 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
using (d.GetScopedWriteLock(GetScopeProvider()))
using (d.GetWriter(GetScopeProvider()))
{
var s1 = d.CreateSnapshot();
@@ -685,7 +685,7 @@ namespace Umbraco.Tests.Cache
Assert.IsFalse(d.Test.NextGen);
Assert.AreEqual("uno", s2.Get(1));
using (d.GetScopedWriteLock(GetScopeProvider()))
using (d.GetWriter(GetScopeProvider()))
{
// gen 3
Assert.AreEqual(2, d.Test.GetValues(1).Length);
@@ -712,102 +712,16 @@ namespace Umbraco.Tests.Cache
}
[Test]
public void NestedWriteLocking1()
{
var d = new SnapDictionary<int, string>();
var t = d.Test;
t.CollectAuto = false;
Assert.AreEqual(0, d.CreateSnapshot().Gen);
// no scope context: writers nest, last one to be disposed commits
var scopeProvider = GetScopeProvider();
using (var w1 = d.GetScopedWriteLock(scopeProvider))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
using (var w2 = d.GetScopedWriteLock(scopeProvider))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(2, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreNotSame(w1, w2); // get a new writer each time
d.Set(1, "one");
Assert.AreEqual(0, d.CreateSnapshot().Gen);
}
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(0, d.CreateSnapshot().Gen);
}
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(0, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, d.CreateSnapshot().Gen);
}
[Test]
public void NestedWriteLocking2()
public void NestedWriteLocking()
{
var d = new SnapDictionary<int, string>();
d.Test.CollectAuto = false;
Assert.AreEqual(0, d.CreateSnapshot().Gen);
// scope context: writers enlist
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
using (var w1 = d.GetScopedWriteLock(scopeProvider))
var scopeProvider = GetScopeProvider();
using (d.GetWriter(scopeProvider))
{
using (var w2 = d.GetScopedWriteLock(scopeProvider))
using (d.GetWriter(scopeProvider))
{
Assert.AreSame(w1, w2);
d.Set(1, "one");
}
}
}
[Test]
public void NestedWriteLocking3()
{
var d = new SnapDictionary<int, string>();
var t = d.Test;
t.CollectAuto = false;
Assert.AreEqual(0, d.CreateSnapshot().Gen);
var scopeContext = new ScopeContext();
var scopeProvider1 = GetScopeProvider();
var scopeProvider2 = GetScopeProvider(scopeContext);
using (var w1 = d.GetScopedWriteLock(scopeProvider1))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
using (var w2 = d.GetScopedWriteLock(scopeProvider2))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(2, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreNotSame(w1, w2);
d.Set(1, "one");
}
}
@@ -850,7 +764,7 @@ namespace Umbraco.Tests.Cache
var scopeProvider = GetScopeProvider();
using (d.GetScopedWriteLock(scopeProvider))
using (d.GetWriter(scopeProvider))
{
// gen 3
Assert.AreEqual(2, d.Test.GetValues(1).Length);
@@ -895,7 +809,7 @@ namespace Umbraco.Tests.Cache
var scopeProvider = GetScopeProvider();
using (d.GetScopedWriteLock(scopeProvider))
using (d.GetWriter(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -932,10 +846,9 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s2.Gen);
Assert.AreEqual("uno", s2.Get(1));
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
var scopeProvider = GetScopeProvider(true);
using (d.GetScopedWriteLock(scopeProvider))
using (d.GetWriter(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -954,7 +867,7 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s4.Gen);
Assert.AreEqual("uno", s4.Get(1));
scopeContext.ScopeExit(true);
((ScopeContext) scopeProvider.Context).ScopeExit(true);
var s5 = d.CreateSnapshot();
Assert.AreEqual(3, s5.Gen);
@@ -965,8 +878,7 @@ namespace Umbraco.Tests.Cache
public void ScopeLocking2()
{
var d = new SnapDictionary<int, string>();
var t = d.Test;
t.CollectAuto = false;
d.Test.CollectAuto = false;
// gen 1
d.Set(1, "one");
@@ -979,13 +891,12 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s2.Gen);
Assert.AreEqual("uno", s2.Get(1));
Assert.AreEqual(2, t.LiveGen);
Assert.IsFalse(t.NextGen);
var scopeProviderMock = new Mock<IScopeProvider>();
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
scopeProviderMock.Setup(x => x.Context).Returns(scopeContext);
var scopeProvider = scopeProviderMock.Object;
using (d.GetScopedWriteLock(scopeProvider))
using (d.GetWriter(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -994,35 +905,18 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s3.Gen);
Assert.AreEqual("uno", s3.Get(1));
// we made some changes, so a next gen is required
Assert.AreEqual(3, t.LiveGen);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, t.WLocked);
// but live snapshot contains changes
var ls = t.LiveSnapshot;
var ls = d.Test.LiveSnapshot;
Assert.AreEqual("ein", ls.Get(1));
Assert.AreEqual(3, ls.Gen);
}
// nothing is committed until scope exits
Assert.AreEqual(3, t.LiveGen);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, t.WLocked);
// no changes until exit
var s4 = d.CreateSnapshot();
Assert.AreEqual(2, s4.Gen);
Assert.AreEqual("uno", s4.Get(1));
scopeContext.ScopeExit(false);
// now things have changed
Assert.AreEqual(2, t.LiveGen);
Assert.IsFalse(t.NextGen);
Assert.AreEqual(0, t.WLocked);
// no changes since not completed
var s5 = d.CreateSnapshot();
Assert.AreEqual(2, s5.Gen);
Assert.AreEqual("uno", s5.Get(1));
@@ -1061,92 +955,12 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual("four", all[3]);
}
[Test]
public void DontPanic()
private IScopeProvider GetScopeProvider(bool withContext = false)
{
var d = new SnapDictionary<int, string>();
d.Test.CollectAuto = false;
Assert.IsNull(d.Test.GenObj);
// gen 1
d.Set(1, "one");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsNull(d.Test.GenObj);
var s1 = d.CreateSnapshot();
Assert.IsFalse(d.Test.NextGen);
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(1, d.Test.GenObj.Gen);
Assert.AreEqual(1, s1.Gen);
Assert.AreEqual("one", s1.Get(1));
d.Set(1, "uno");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(2, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(1, d.Test.GenObj.Gen);
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
// scopeProvider.Context == scopeContext -> writer is scoped
// writer is scope contextual and scoped
// when disposed, nothing happens
// when the context exists, the writer is released
using (d.GetScopedWriteLock(scopeProvider))
{
d.Set(1, "ein");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
}
// writer has not released
Assert.AreEqual(1, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
// nothing changed
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(3, d.Test.LiveGen);
// panic!
var s2 = d.CreateSnapshot();
Assert.AreEqual(1, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
// release writer
scopeContext.ScopeExit(true);
Assert.AreEqual(0, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
var s3 = d.CreateSnapshot();
Assert.AreEqual(0, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(3, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsFalse(d.Test.NextGen);
}
private IScopeProvider GetScopeProvider(ScopeContext scopeContext = null)
{
var scopeProvider = Mock.Of<IScopeProvider>();
Mock.Get(scopeProvider)
.Setup(x => x.Context).Returns(scopeContext);
var scopeProviderMock = new Mock<IScopeProvider>();
var scopeContext = withContext ? new ScopeContext() : null;
scopeProviderMock.Setup(x => x.Context).Returns(scopeContext);
var scopeProvider = scopeProviderMock.Object;
return scopeProvider;
}
}
@@ -4,7 +4,6 @@ using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Compose;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
@@ -300,19 +299,11 @@ namespace Umbraco.Tests.Components
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer2) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
Composed.Clear();
Assert.Throws<Exception>(() => composers.Compose());
Console.WriteLine("throws:");
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
requirements = composers.GetRequirements(false);
Console.WriteLine(Composers.GetComposersReport(requirements));
types = new[] { typeof(Composer12) };
composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
@@ -358,25 +349,6 @@ namespace Umbraco.Tests.Components
Assert.AreEqual(typeof(Composer27), Composed[1]);
}
[Test]
public void AllComposers()
{
var typeLoader = new TypeLoader(AppCaches.Disabled.RuntimeCache, IOHelper.MapPath("~/App_Data/TEMP"), Mock.Of<IProfilingLogger>());
var register = MockRegister();
var composition = new Composition(register, typeLoader, Mock.Of<IProfilingLogger>(), MockRuntimeState(RuntimeLevel.Run));
var types = typeLoader.GetTypes<IComposer>().Where(x => x.FullName.StartsWith("Umbraco.Core.") || x.FullName.StartsWith("Umbraco.Web"));
var composers = new Composers(composition, types, Mock.Of<IProfilingLogger>());
var requirements = composers.GetRequirements();
var report = Composers.GetComposersReport(requirements);
Console.WriteLine(report);
var composerTypes = composers.SortComposers(requirements);
foreach (var type in composerTypes)
Console.WriteLine(type);
}
#region Compothings
public class TestComposerBase : IComposer
@@ -0,0 +1,53 @@
using System;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Tests.Composing
{
[TestFixture]
public class CompositionTests
{
[Test]
public void FactoryIsResolvable()
{
Func<IFactory, IFactory> factoryFactory = null;
var mockedRegister = Mock.Of<IRegister>();
var mockedFactory = Mock.Of<IFactory>();
// the mocked register creates the mocked factory
Mock.Get(mockedRegister)
.Setup(x => x.CreateFactory())
.Returns(mockedFactory);
// the mocked register can register a factory factory
Mock.Get(mockedRegister)
.Setup(x => x.Register(It.IsAny<Func<IFactory, IFactory>>(), Lifetime.Singleton))
.Callback<Func<IFactory, IFactory>, Lifetime>((ff, lt) => factoryFactory = ff);
// the mocked factory can invoke the factory factory
Mock.Get(mockedFactory)
.Setup(x => x.GetInstance(typeof(IFactory)))
.Returns(() => factoryFactory?.Invoke(mockedFactory));
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), "", logger);
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>());
// create the factory, ensure it is the mocked factory
var factory = composition.CreateFactory();
Assert.AreSame(mockedFactory, factory);
// ensure we can get an IFactory instance,
// meaning that it has been properly registered
var resolved = factory.GetInstance<IFactory>();
Assert.IsNotNull(resolved);
Assert.AreSame(factory, resolved);
}
}
}
+13 -3
View File
@@ -112,9 +112,19 @@ namespace Umbraco.Tests.IO
fs.DeleteMediaFiles(new[] { virtPath });
Assert.IsFalse(File.Exists(physPath));
// ~/media/1234 is gone
physPath = Path.GetDirectoryName(physPath);
Assert.IsFalse(Directory.Exists(physPath));
var scheme = Current.Factory.GetInstance<IMediaPathScheme>();
if (scheme is UniqueMediaPathScheme)
{
// ~/media/1234 is *not* gone
physPath = Path.GetDirectoryName(physPath);
Assert.IsTrue(Directory.Exists(physPath));
}
else
{
// ~/media/1234 is gone
physPath = Path.GetDirectoryName(physPath);
Assert.IsFalse(Directory.Exists(physPath));
}
// ~/media exists
physPath = Path.GetDirectoryName(physPath);
+4 -2
View File
@@ -1,4 +1,5 @@
using NUnit.Framework;
using Moq;
using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
@@ -47,7 +48,8 @@ namespace Umbraco.Tests.Logging
File.Copy(exampleLogfilePath, _newLogfilePath, true);
File.Copy(exampleSearchfilePath, _newSearchfilePath, true);
_logViewer = new JsonLogViewer(logsPath: _newLogfileDirPath, searchPath: _newSearchfilePath);
var logger = Mock.Of<Core.Logging.ILogger>();
_logViewer = new JsonLogViewer(logger, logsPath: _newLogfileDirPath, searchPath: _newSearchfilePath);
}
[OneTimeTearDown]
+9 -43
View File
@@ -15,47 +15,13 @@ namespace Umbraco.Tests.Models
[TestFixture]
public class ContentTypeTests : UmbracoTestBase
{
[Test]
[Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")]
public void Cannot_Add_Duplicate_Property_Aliases()
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[]
{
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" }
})));
Assert.Throws<InvalidOperationException>(() =>
contentType.PropertyTypeCollection.Add(
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType" }));
}
[Test]
[Ignore("Ignoring this test until we actually enforce this, see comments in ContentTypeBase.PropertyTypesChanged")]
public void Cannot_Update_Duplicate_Property_Aliases()
{
var contentType = MockedContentTypes.CreateBasicContentType();
contentType.PropertyGroups.Add(new PropertyGroup(new PropertyTypeCollection(false, new[]
{
new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar){ Alias = "myPropertyType" }
})));
contentType.PropertyTypeCollection.Add(new PropertyType("testPropertyEditor", ValueStorageType.Nvarchar) { Alias = "myPropertyType2" });
var toUpdate = contentType.PropertyTypeCollection["myPropertyType2"];
Assert.Throws<InvalidOperationException>(() => toUpdate.Alias = "myPropertyType");
}
[Test]
public void Can_Deep_Clone_Content_Type_Sort()
{
var contentType = new ContentTypeSort(new Lazy<int>(() => 3), 4, "test");
var clone = (ContentTypeSort)contentType.DeepClone();
var clone = (ContentTypeSort) contentType.DeepClone();
Assert.AreNotSame(clone, contentType);
Assert.AreEqual(clone, contentType);
Assert.AreEqual(clone.Id.Value, contentType.Id.Value);
@@ -88,7 +54,7 @@ namespace Umbraco.Tests.Models
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -151,12 +117,12 @@ namespace Umbraco.Tests.Models
{
group.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -201,12 +167,12 @@ namespace Umbraco.Tests.Models
{
group.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] {new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2")};
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -298,12 +264,12 @@ namespace Umbraco.Tests.Models
{
propertyType.Id = ++i;
}
contentType.AllowedTemplates = new[] { new Template((string)"Name", (string)"name") { Id = 200 }, new Template((string)"Name2", (string)"name2") { Id = 201 } };
contentType.AllowedTemplates = new[] { new Template((string) "Name", (string) "name") { Id = 200 }, new Template((string) "Name2", (string) "name2") { Id = 201 } };
contentType.AllowedContentTypes = new[] { new ContentTypeSort(new Lazy<int>(() => 888), 8, "sub"), new ContentTypeSort(new Lazy<int>(() => 889), 9, "sub2") };
contentType.Id = 10;
contentType.CreateDate = DateTime.Now;
contentType.CreatorId = 22;
contentType.SetDefaultTemplate(new Template((string)"Test Template", (string)"testTemplate")
contentType.SetDefaultTemplate(new Template((string) "Test Template", (string) "testTemplate")
{
Id = 88
});
@@ -232,35 +232,6 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
/// <summary>
/// This demonstates an issue found: https://github.com/umbraco/Umbraco-CMS/issues/4963#issuecomment-483516698
/// </summary>
[Test]
[Ignore("Still testing")]
public void Bug_Changing_Built_In_Member_Type_Property_Type_Aliases_Results_In_Exception()
{
//TODO: Fix this bug and then change this test
var provider = TestObjects.GetScopeProvider(Logger);
using (var scope = provider.CreateScope())
{
var repository = CreateRepository(provider);
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
repository.Save(memberType);
foreach (var stub in Constants.Conventions.Member.GetStandardPropertyTypeStubs())
{
var prop = memberType.PropertyTypes.First(x => x.Alias == stub.Key);
prop.Alias = prop.Alias + "__0000";
}
repository.Save(memberType);
//Assert.Throws<ArgumentException>(() => );
}
}
[Test]
public void Built_In_Member_Type_Properties_Are_Automatically_Added_When_Creating()
{
+1
View File
@@ -118,6 +118,7 @@
<Compile Include="Cache\SnapDictionaryTests.cs" />
<Compile Include="Clr\ReflectionUtilitiesTests.cs" />
<Compile Include="Collections\OrderedHashSetTests.cs" />
<Compile Include="Composing\CompositionTests.cs" />
<Compile Include="Composing\LightInjectValidation.cs" />
<Compile Include="Composing\ContainerConformingTests.cs" />
<Compile Include="CoreThings\CallContextTests.cs" />
-22
View File
@@ -1,22 +0,0 @@
@ECHO OFF
ECHO.
ECHO.
ECHO This will only work when you have NPM available on the command line
ECHO Works great with NodeJS Portable - https://gareth.flowers/nodejs-portable/
ECHO.
ECHO.
set /P c=Are you sure you want to continue [Y/N]?
if /I "%c%" EQU "Y" goto :setupgulp
if /I "%c%" EQU "N" goto :eof
:setupgulp
call npm install
call npm -g install gulp
call npm -g install gulp-cli
ECHO.
ECHO.
ECHO You should now be able to run: gulp build or gulp dev
ECHO.
ECHO.
@@ -331,8 +331,35 @@
// This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
function performSave(args) {
// Check that all variants for publishing have a name.
if (args.action === "publish" || args.action === "sendToPublish") {
if ($scope.content.variants) {
var iVariant;
for (var i = 0; i < $scope.content.variants.length; i++) {
iVariant = $scope.content.variants[i];
iVariant.notifications = [];// maybe not needed, need to investigate.
if(iVariant.publish === true) {
if (iVariant.name == null) {
var tokens = [iVariant.language.name];
localizationService.localize("publish_contentPublishedFailedByMissingName", tokens).then(function (value) {
iVariant.notifications.push({"message": value, "type": 1});
});
return $q.reject();
}
}
}
}
}
//Used to check validility of nested form - coming from Content Apps mostly
//Set them all to be invalid
var fieldsToRollback = checkValidility();
@@ -42,7 +42,7 @@
item: '=',
onOpen: '&',
onOpenAnchor: '&',
index: '@'
hotkey: '<'
}
});
@@ -0,0 +1,72 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbCheckbox
@restrict E
@scope
@description
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco checkbox.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-checkbox
name="checkboxlist"
value="{{key}}"
model="true"
text="{{text}}">
</umb-checkbox>
</div>
</pre>
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the checkbox to checked or unchecked.
@param {string} input-id Set the <code>id</code> of the checkbox.
@param {string} value Set the value of the checkbox.
@param {string} name Set the name of the checkbox.
@param {string} text Set the text for the checkbox label.
@param {string} server-validation-field Set the <code>val-server-field</code> of the checkbox.
@param {boolean} disabled Set the checkbox to be disabled.
@param {boolean} required Set the checkbox to be required.
@param {string} on-change Callback when the value of the checkbox changed by interaction.
**/
(function () {
'use strict';
function UmbCheckboxController($timeout) {
var vm = this;
vm.callOnChange = function() {
$timeout(function() {
vm.onChange({model:vm.model, value:vm.value});
}, 0);
}
}
var component = {
templateUrl: 'views/components/forms/umb-checkbox.html',
controller: UmbCheckboxController,
controllerAs: 'vm',
bindings: {
model: "=",
inputId: "@",
value: "@",
name: "@",
text: "@",
serverValidationField: "@",
disabled: "<",
required: "<",
onChange: "&"
}
};
angular.module('umbraco.directives').component('umbCheckbox', component);
})();
@@ -0,0 +1,57 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbRadiobutton
@restrict E
@scope
@description
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco radio button.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-radiobutton
name="radiobuttonlist"
value="{{key}}"
model="true"
text="{{text}}">
</umb-radiobutton>
</div>
</pre>
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the radiobutton to checked or unchecked.
@param {string} value Set the value of the radiobutton.
@param {string} name Set the name of the radiobutton.
@param {string} text Set the text for the radiobutton label.
@param {boolean} disabled Set the radiobutton to be disabled.
@param {boolean} required Set the radiobutton to be required.
**/
(function () {
'use strict';
function RadiobuttonDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/forms/umb-radiobutton.html',
scope: {
model: "=",
value: "@",
name: "@",
text: "@",
disabled: "=",
required: "="
}
};
return directive;
}
angular.module('umbraco.directives').directive('umbRadiobutton', RadiobuttonDirective);
})();
@@ -19,9 +19,15 @@ angular.module("umbraco.directives")
promises.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
}
var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"];
if (scope.configuration && scope.configuration.toolbar) {
toolbar = scope.configuration.toolbar;
var editorConfig = scope.configuration ? scope.configuration : null;
if (!editorConfig || angular.isString(editorConfig)) {
editorConfig = tinyMceService.defaultPrevalues();
//for the grid by default, we don't want to include the macro toolbar
editorConfig.toolbar = _.without(editorConfig, "umbmacro");
}
//make sure there's a max image size
if (!scope.configuration.maxImageSize && scope.configuration.maxImageSize !== 0) {
editorConfig.maxImageSize = tinyMceService.defaultPrevalues().maxImageSize;
}
//stores a reference to the editor
@@ -29,9 +35,9 @@ angular.module("umbraco.directives")
promises.push(tinyMceService.getTinyMceEditorConfig({
htmlId: scope.uniqueId,
stylesheets: scope.configuration ? scope.configuration.stylesheets : null,
toolbar: toolbar,
mode: scope.configuration.mode
stylesheets: editorConfig.stylesheets,
toolbar: editorConfig.toolbar,
mode: editorConfig.mode
}));
// pin toolbar to top of screen if we have focus and it scrolls off the screen
@@ -46,9 +52,16 @@ angular.module("umbraco.directives")
$q.all(promises).then(function (result) {
var tinyMceEditorConfig = result[promises.length - 1];
var standardConfig = result[promises.length - 1];
tinyMceEditorConfig.setup = function (editor) {
//create a baseline Config to extend upon
var baseLineConfigObj = {
maxImageSize: editorConfig.maxImageSize
};
angular.extend(baseLineConfigObj, standardConfig);
baseLineConfigObj.setup = function (editor) {
//set the reference
tinyMceEditor = editor;
@@ -111,7 +124,7 @@ angular.module("umbraco.directives")
//the elements needed
$timeout(function () {
tinymce.DOM.events.domLoaded = true;
tinymce.init(tinyMceEditorConfig);
tinymce.init(baseLineConfigObj);
}, 150, false);
}
@@ -117,9 +117,11 @@ Use this directive to generate a list of content items presented as a flexbox gr
};
scope.clickItemName = function(item, $event, $index) {
if(scope.onClickName) {
if(scope.onClickName && !($event.metaKey || $event.ctrlKey)) {
scope.onClickName(item, $event, $index);
$event.preventDefault();
}
$event.stopPropagation();
};
}
@@ -117,10 +117,11 @@
var vm = this;
vm.clickItem = function (item, $event) {
if (vm.onClick) {
if (vm.onClick && !($event.metaKey || $event.ctrlKey)) {
vm.onClick({ item: item});
$event.stopPropagation();
$event.preventDefault();
}
$event.stopPropagation();
};
vm.selectItem = function (item, $index, $event) {
@@ -62,8 +62,8 @@ function valPropertyMsg(serverValidationManager) {
if (!watcher) {
watcher = scope.$watch("currentProperty.value",
function (newValue, oldValue) {
if (angular.equals(newValue, oldValue)) {
if (!newValue || angular.equals(newValue, oldValue)) {
return;
}
@@ -78,12 +78,10 @@ function valPropertyMsg(serverValidationManager) {
// based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
// is the only one, then we'll clear.
if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
scope.errorMsg = "";
formCtrl.$setValidity('valPropertyMsg', true);
} else if (showValidation && scope.errorMsg === "") {
formCtrl.$setValidity('valPropertyMsg', false);
scope.errorMsg = getErrorMsg();
stopWatch();
}
}, true);
}
@@ -154,7 +152,6 @@ function valPropertyMsg(serverValidationManager) {
showValidation = true;
if (hasError && scope.errorMsg === "") {
scope.errorMsg = getErrorMsg();
startWatch();
}
else if (!hasError) {
scope.errorMsg = "";
@@ -301,6 +301,19 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return allProps;
},
/**
* @ngdoc method
* @name umbraco.services.contentEditingHelper#buildCompositeVariantId
* @methodOf umbraco.services.contentEditingHelper
* @function
*
* @description
* Returns a id for the variant that is unique between all variants on the content
*/
buildCompositeVariantId: function (variant) {
return (variant.language ? variant.language.culture : "invariant") + "_" + (variant.segment ? variant.segment : "");
},
/**
* @ngdoc method
@@ -112,7 +112,6 @@ When building a custom infinite editor view you can use the same components as a
type="button"
button-style="link"
label-key="general_close"
shortcut="esc"
action="vm.close()">
</umb-button>
<umb-button
@@ -26,6 +26,8 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve
navReadyPromise.resolve(mainTreeApi);
});
//A list of query strings defined that when changed will not cause a reload of the route
var nonRoutingQueryStrings = ["mculture", "cculture", "lq"];
var retainedQueryStrings = ["mculture"];
@@ -427,8 +427,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
data["rel"] = img.id;
data["data-id"] = img.id;
}
editor.insertContent(editor.dom.createHTML('img', data));
editor.selection.setContent(editor.dom.createHTML('img', data));
$timeout(function () {
var imgElm = editor.dom.get('__mcenew');
@@ -350,6 +350,9 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS
parent.children.splice(parent.children.indexOf(treeNode), 1);
parent.hasChildren = parent.children.length !== 0;
//Notify that the node has been removed
eventsService.emit("treeService.removeNode", { node: treeNode });
},
/**
@@ -9,7 +9,7 @@
*
* @param {navigationService} navigationService A reference to the navigationService
*/
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource) {
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource, editorState) {
//this is used to trigger the tree to start loading once everything is ready
var treeInitPromise = $q.defer();
@@ -249,6 +249,20 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
}));
evts.push(eventsService.on("treeService.removeNode", function (e, args) {
//check to see if the current page has been removed
var currentEditorState = editorState.getCurrent()
if (currentEditorState && currentEditorState.id.toString() === args.node.id.toString()) {
//current page is loaded, so navigate to root
var section = appState.getSectionState("currentSection");
$location.path("/" + section);
}
}));
/**
* Based on the current state of the application, this configures the scope variables that control the main tree and language drop down
*/
@@ -117,6 +117,7 @@
@import "components/umb-confirm-action.less";
@import "components/umb-keyboard-shortcuts-overview.less";
@import "components/umb-checkbox-list.less";
@import "components/umb-form-check.less";
@import "components/umb-locked-field.less";
@import "components/umb-tabs.less";
@import "components/umb-load-indicator.less";
@@ -166,6 +167,7 @@
@import "components/umb-property-file-upload.less";
@import "components/users/umb-user-cards.less";
@import "components/users/umb-user-table.less";
@import "components/users/umb-user-details.less";
@import "components/users/umb-user-group-picker-list.less";
@import "components/users/umb-user-group-preview.less";
@@ -195,12 +195,9 @@ input[type="button"] {
}
// Made for Umbraco, 2019
.btn-action {
.buttonBackground(@blueExtraDark, @blueDark, @white, @u-white);
}
// Made for Umbraco, 2019
.btn-selection {
@btnSelectionBackgroundHover: darken(@pinkLight, 10%);
.buttonBackground(@pinkLight, @btnSelectionBackgroundHover, @blueExtraDark, @blueDark);
.buttonBackground(@blueExtraDark, lighten(@blueExtraDark, 10), @white, @u-white);
transition: background-color 240ms;
font-weight: 700;
}
// Made for Umbraco, 2019, used for buttons that has to stand back.
.btn-white {
@@ -1,7 +1,7 @@
.umb-editor-sub-header {
padding: 10px 0;
margin-bottom: 10px;
background: @gray-10;
background: @brownGrayLight;
display: flex;
justify-content: space-between;
margin-top: -10px;
@@ -12,7 +12,7 @@
&.nested {
margin-top: 0;
margin-bottom: 0;
background: @gray-10;
background: @brownGrayLight;
}
}
@@ -250,3 +250,11 @@
.form-horizontal .umb-overlay .controls {
margin-left: 0 !important;
}
.umb-overlay .text-error {
color: @formErrorText;
}
.umb-overlay .text-success {
color: @formSuccessText;
}
@@ -1,3 +1,6 @@
@checkboxWidth: 15px;
@checkboxHeight: 15px;
.umb-checkbox-list {
list-style: none;
margin-left: 0;
@@ -13,7 +13,6 @@
user-select: none;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
border-radius: 3px;
}
.umb-content-grid__item.-selected {
@@ -59,7 +58,8 @@
display: inline-flex;
color: @ui-option-type;
&:hover {
&:hover, &:focus {
text-decoration: none;
color:@ui-option-type-hover;
}
}
@@ -61,6 +61,9 @@
.show-validation .umb-sub-views-nav-item > a.-has-error {
color: @red;
&::after {
background-color: @red;
}
}
.umb-sub-views-nav-item .icon {
@@ -0,0 +1,136 @@
@checkboxWidth: 16px;
@checkboxHeight: 16px;
.umb-form-check {
display: flex;
flex-wrap: wrap;
align-items: center;
position: relative;
padding: 0;
margin: 0;
min-height: 22px;
line-height: 22px;
cursor: pointer !important;
&__text {
margin: 0 0 0 26px;
position: relative;
top: 0;
}
&__input {
position: absolute;
top: 0;
left: 0;
opacity: 0;
&:checked ~ .umb-form-check__state .umb-form-check__check {
border-color: @ui-option-type;
}
&:focus:checked ~ .umb-form-check .umb-form-check__check,
&:focus ~ .umb-form-check__state .umb-form-check__check {
border-color: @inputBorderFocus;
}
&:checked ~ .umb-form-check__state {
.umb-form-check__check {
// This only happens if the state has a radiobutton modifier
.umb-form-check--radiobutton & {
&:before {
opacity: 1;
transform: scale(1);
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
&:before {
width: @checkboxWidth;
height: @checkboxHeight;
}
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
.umb-form-check__icon {
opacity: 1;
}
}
}
}
&__state {
display: flex;
height: 18px;
margin-top: 2px;
position: absolute;
top: 0;
left: 0;
}
&__check {
display: flex;
position: relative;
background: @white;
border: 1px solid @inputBorder;
width: @checkboxWidth;
height: @checkboxHeight;
&:before {
content: "";
background: @ui-option-type;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
// This only happens if state has the radiobutton modifier
.umb-form-check--radiobutton & {
border-radius: 100%;
&:before {
width: 10px;
height: 10px;
border-radius: 100%;
opacity: 0;
transform: scale(0);
transition: .15s ease-out;
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
&:before {
width: 0;
height: 0;
transition: .05s ease-out;
}
}
}
&__icon {
color: @white;
text-align: center;
font-size: 12px;
opacity: 0;
transition: .2s ease-out;
&:before {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
margin: auto;
}
}
&.umb-form-check--disabled {
cursor: not-allowed !important;
opacity: 0.5;
}
}
@@ -22,6 +22,10 @@ a.umb-list-item:focus {
opacity: 0.6;
}
.umb-list-item--error {
color: @red;
}
.umb-list-item:hover .umb-list-checkbox,
.umb-list-item--selected .umb-list-checkbox {
opacity: 1;
@@ -34,4 +38,4 @@ a.umb-list-item:focus {
.umb-list-checkbox--visible {
opacity: 1;
}
}
@@ -261,6 +261,7 @@
.umb-package-details {
display: flex;
flex-flow: row wrap;
}
a.umb-package-details__back-link {
@@ -280,6 +281,7 @@ a.umb-package-details__back-link {
flex: 1 1 auto;
margin-right: 20px;
width: calc(~'100%' - ~'@{sidebarwidth}' - ~'20px'); // Make sure that the main content area doesn't gets affected by inline styling
min-width: 500px;
}
.umb-package-details__sidebar {
@@ -17,6 +17,10 @@
outline: none;
text-decoration: none !important;
}
.umb-user-card.-selectable {
cursor: pointer;
}
.umb-user-card.-selected {
&::before {
content: "";
@@ -31,7 +35,6 @@
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
}
}
.umb-user-card__content {
@@ -44,14 +47,18 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
cursor: pointer;
max-width: 100%;
}
.umb-user-card__goToUser {
&:hover {
&:hover, &:focus {
text-decoration: none;
.umb-user-card__name {
text-decoration: underline;
text-decoration: underline;
color: @ui-option-type-hover;
}
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
@@ -0,0 +1,81 @@
.umb-user-table {
.umb-user-table-col-avatar {
flex: 0 0 32px;
padding: 15px 0;
}
.umb-table-cell a {
&:hover, &:focus {
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
.umb-table-body .umb-table-cell.umb-table__name {
margin: 0;
padding: 0;
a {
display: flex;
padding: 6px 2px;
height: 42px;
span {
margin: auto 14px;
}
}
}
.umb-table-cell.umb-table__name a {
&:hover, &:focus {
text-decoration: underline;
}
}
.umb-user-table-row {
cursor: default;
.umb-checkmark {
visibility: hidden;
}
}
.umb-user-table-row.-selectable {
cursor: pointer;
}
&.-has-selection {
.umb-user-table-row.-selectable {
.umb-checkmark {
visibility: visible;
}
}
}
.umb-user-table-row.-selectable:hover {
.umb-checkmark {
visibility: visible;
}
}
.umb-user-table-row.-selected {
.umb-checkmark {
visibility: visible;
}
&::before {
content: "";
position: absolute;
z-index:1;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
border: 2px solid @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
pointer-events: none;
}
}
}
@@ -30,7 +30,7 @@
position: absolute;
padding: 5px 8px;
pointer-events: none;
top: 0;
top: 2px;
}
input[type="text"] {
@@ -55,16 +55,6 @@
position: absolute;;
}
.--notInFront .umb-modalcolumn::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: rgba(0,0,0,.4);
}
/* re-align loader */
.umb-modal .umb-loader-wrapper, .umb-modalcolumn .umb-loader-wrapper, .umb-dialog .umb-loader-wrapper{
position:relative;
@@ -13,23 +13,25 @@
border: none;
border-radius: 0;
overflow-y: auto;
background-color: @purple-d2;
background-color: @blueNight;
}
.login-overlay__background-image {
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
background-image: url('../img/login.jpg');
width: 100%;
height: 100%;
position: absolute;
opacity: 0.05;
}
.login-overlay__logo {
position: absolute;
top: 22px;
left: 25px;
width: 30px;
height: 30px;
z-index: 1;
}
@@ -18,7 +18,16 @@
&-push {
float:right;
}
}
&--list{
float: left;
}
&__item{
line-height: 1;
margin: 0 0 5px;
}
}
.umb-property-editor-tiny {
@@ -69,6 +78,11 @@
}
}
.umb-property .alert {
border-radius: 3px;
}
//
// Content picker
@@ -213,7 +227,7 @@
margin: 24px 0 0;
display: flex;
}
&__input {
width: 100%;
&-wrap{
@@ -37,7 +37,8 @@ ul.sections>li>a::after {
height: 4px;
width: 100%;
background-color: @pinkLight;
position: absolute;
position: absolute;
left: 0;
bottom: -4px;
border-radius: 3px 3px 0 0;
opacity: 0;
@@ -41,8 +41,8 @@
@purple-washed: #F6F3FD;
// UI Colors
@red-d1: #F02E28;// currently used for validation, and is hard coded inline in various html places :/
@red: #D42054;// updated 2019 - should be used as validation! and is already in some cases like the .umb-validation-label
@red-d1: #F02E28;
@red: #D42054;// updated 2019
@red-l1: #e22c60;// updated 2019
@red-l2: #FE8B88;
@red-l3: #FFB2B0;
@@ -508,7 +508,7 @@
@warningBorder: transparent;
@errorText: @white;
@errorBackground: @red-d1;
@errorBackground: @red;
@errorBorder: transparent;
@successText: @white;
@@ -10,7 +10,7 @@
(function() {
"use strict";
function DataTypePicker($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
function DataTypePicker($scope, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
var vm = this;
@@ -119,13 +119,28 @@
$scope.model.itemDetails = null;
if (vm.searchTerm) {
vm.showFilterResult = true;
vm.showTabs = false;
var regex = new RegExp(vm.searchTerm, "i");
vm.filterResult = {
userConfigured: filterCollection(vm.userConfigured, regex),
typesAndEditors: filterCollection(vm.typesAndEditors, regex)
};
} else {
vm.showFilterResult = false;
vm.filterResult = null;
vm.showTabs = true;
}
}
function filterCollection(collection, regex) {
return _.map(_.keys(collection), function (key) {
return {
group: key,
dataTypes: $filter('filter')(collection[key], function (dataType) {
return regex.test(dataType.name) || regex.test(dataType.alias);
})
}
});
}
function showDetailsOverlay(property) {
@@ -201,4 +216,4 @@
angular.module("umbraco").controller("Umbraco.Editors.DataTypePickerController", DataTypePicker);
})();
})();
@@ -79,13 +79,13 @@
</umb-tab-content>
</div>
<!-- FILTER RESULTS -->
<div ng-if="vm.showFilterResult">
<div ng-if="vm.filterResult">
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_reuse"></localize></h5>
<div ng-repeat="(key,value) in vm.userConfigured">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.userConfigured">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="dataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="dataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(dataType)"
ng-click="vm.pickDataType(dataType)"
class="-four-in-row">
@@ -101,11 +101,11 @@
</div>
</div>
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_availableEditors"></localize></h5>
<div ng-repeat="(key,value) in vm.typesAndEditors">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.typesAndEditors">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="systemDataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="systemDataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
ng-click="vm.pickEditor(systemDataType)"
class="-four-in-row">
@@ -1,5 +1,4 @@
<div ng-controller="Umbraco.Overlays.UserController">
<div class="umb-control-group" ng-if="!showPasswordFields">
<h5><localize key="user_yourProfile" /></h5>
@@ -79,7 +78,7 @@
</div>
<div class="umb-control-group" ng-if="!showPasswordFields">
<div class="umb-control-group" ng-if="!showPasswordFields && history.length">
<h5><localize key="user_yourHistory" /></h5>
<ul class="umb-tree">
<li ng-repeat="item in history | orderBy:'time':true">
@@ -127,7 +126,7 @@
</div>
<div class="umb-control-group">
<div class="umb-control-group" ng-if="tab.length">
<div ng-repeat="tab in dashboard">
<div ng-repeat="property in tab.properties">
<div>
@@ -2,10 +2,10 @@
<div id="login" class="umb-modalcolumn umb-dialog" ng-class="{'show-validation': vm.loginForm.$invalid}" ng-cloak>
<div class="login-overlay__background-image" ng-if="vm.backgroundImage" ng-style="{'background-image':'url(' + vm.backgroundImage + ')'}"></div>
<div class="login-overlay__background-image" ng-style="{'background-image': 'url('+vm.backgroundImage+')'}"></div>
<div class="login-overlay__logo">
<img ng-src="assets/img/application/logo.png" ng-srcset="assets/img/application/logo@2x.png 2x, assets/img/application/logo@3x.png 3x">
<img src="assets/img/application/umbraco_logo_white.svg">
</div>
<div ng-show="vm.invitedUser != null" class="umb-login-container">
@@ -37,11 +37,11 @@
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" ng-message="valCompare"><localize key="user_passwordMismatch">The confirmed password doesn't match the new password!</localize></span>
</span>
</div>
<div class="flex justify-between items-center">
<umb-button
<umb-button
type="submit"
button-style="success"
state="vm.invitedUserPasswordModel.buttonState"
@@ -58,7 +58,7 @@
<ng-form name="vm.avatarForm">
<umb-progress-bar
<umb-progress-bar
style="max-width: 100px; margin-bottom: 5px;"
ng-show="vm.avatarFile.uploadStatus === 'uploading'"
progress="{{ vm.avatarFile.uploadProgress }}"
@@ -77,7 +77,7 @@
ngf-pattern="{{vm.avatarFile.acceptedFileTypes}}"
ngf-max-size="{{ vm.avatarFile.maxFileSize }}">
<umb-avatar
<umb-avatar
color="gray"
size="xl"
unknown-char="+"
@@ -94,7 +94,7 @@
<localize key="user_userinviteAvatarMessage"></localize>
</p>
<div class="flex justify-center items-center">
<umb-button
<umb-button
type="button"
button-style="success"
label="Done"
@@ -102,7 +102,7 @@
</umb-button>
</div>
</div>
</div>
<div ng-show="vm.invitedUser == null && vm.inviteStep === 3" ng-if="vm.inviteStep === 3" class="umb-login-container">
<div class="form">
@@ -169,7 +169,7 @@
</div>
<div class="flex justify-between items-center">
<umb-button
<umb-button
button-style="success"
size="m"
label-key="general_login"
@@ -261,4 +261,4 @@
</div>
</div>
</div>
</div>
</div>
@@ -1,10 +1,10 @@
<a data-element="sub-view-{{vm.item.alias}}"
tabindex="-1"
ng-href=""
ng-click="vm.clicked()"
hotkey="{{vm.index+1}}"
hotkey-when-hidden="true"
ng-class="{'is-active': vm.item.active, '-has-error': vm.item.hasError}">
tabindex="-1"
ng-href=""
ng-click="vm.clicked()"
hotkey="{{::vm.hotkey}}"
hotkey-when-hidden="true"
ng-class="{'is-active': vm.item.active, '-has-error': vm.item.hasError}">
<i class="icon {{ vm.item.icon }}"></i>
<span class="umb-sub-views-nav-item-text">{{ vm.item.name }}</span>
<div ng-show="vm.item.badge" class="badge -type-{{vm.item.badge.type}}">{{vm.item.badge.count}}</div>
@@ -7,7 +7,7 @@
item="navItem"
on-open="openNavigationItem(item)"
on-open-anchor="openAnchorItem(item, anchor)"
index="{{$index}}">
hotkey="$index + 1">
</umb-editor-navigation-item>
</div>
</li>
@@ -0,0 +1,19 @@
<label class="checkbox umb-form-check umb-form-check--checkbox" ng-class="{ 'umb-form-check--disabled': disabled }">
<input type="checkbox"
id="{{vm.inputId}}"
name="{{vm.name}}"
value="{{vm.value}}"
class="umb-form-check__input"
val-server-field="{{vm.serverValidationField}}"
ng-model="vm.model"
ng-disabled="vm.disabled"
ng-required="vm.required"
ng-change="vm.callOnChange()"/>
<span class="umb-form-check__state" aria-hidden="true">
<span class="umb-form-check__check">
<i class="umb-form-check__icon icon-check"></i>
</span>
</span>
<span class="umb-form-check__text">{{vm.text}}</span>
</label>
@@ -0,0 +1,13 @@
<label class="radio umb-form-check umb-form-check--radiobutton" ng-class="{ 'umb-form-check--disabled': disabled }">
<input type="radio" name="{{name}}"
value="{{value}}"
class="umb-form-check__input"
ng-model="model"
ng-disabled="disabled"
ng-required="required" />
<span class="umb-form-check__state" aria-hidden="true">
<span class="umb-form-check__check"></span>
</span>
<span class="umb-form-check__text">{{text}}</span>
</label>
@@ -10,10 +10,13 @@
<div class="umb-content-grid__content">
<div class="umb-content-grid__item-name" ng-click="clickItemName(item, $event, $index)" ng-class="{'-light': !item.published && item.updater != null}">
<a class="umb-content-grid__item-name"
ng-href="{{'#' + item.editPath}}"
ng-click="clickItemName(item, $event, $index)"
ng-class="{'-light': !item.published && item.updater != null}">
<i class="umb-content-grid__icon {{ item.icon }}"></i>
<span>{{ item.name }}</span>
</div>
</a>
<ul class="umb-content-grid__details-list" ng-class="{'-light': !item.published && item.updater != null}">
<li class="umb-content-grid__details-item" ng-if="item.state">
@@ -40,7 +40,8 @@
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
<div class="umb-table-cell umb-table__name">
<a title="{{ item.name }}" class="umb-table-body__link" href=""
<a title="{{ item.name }}" class="umb-table-body__link"
ng-href="{{'#' + item.editPath}}"
ng-click="vm.clickItem(item, $event)"
ng-bind="item.name">
</a>
@@ -115,6 +115,7 @@
if(response.valid) {
vm.submitButtonState = "success";
closeDialog();
// show validation messages for each domain
} else {
@@ -1,7 +1,7 @@
(function () {
"use strict";
function PublishController($scope, localizationService) {
function PublishController($scope, localizationService, contentEditingHelper) {
var vm = this;
vm.loading = true;
@@ -14,34 +14,51 @@
/** Returns true if publishing is possible based on if there are un-published mandatory languages */
function canPublish() {
var selected = [];
var possible = false;
for (var i = 0; i < vm.variants.length; i++) {
var variant = vm.variants[i];
//if this variant will show up in the publish-able list
var publishable = dirtyVariantFilter(variant);
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
if ((variant.language.isMandatory && !published) && (!publishable || !variant.publish)) {
//if a mandatory variant isn't published
//and it's not publishable or not selected to be published
//then we cannot continue
// TODO: Show a message when this occurs
var state = canVariantPublish(variant);
if (state === true) {
possible = true;
}
if (state === false) {
return false;
}
if (variant.publish) {
selected.push(variant.publish);
}
}
return selected.length > 0;
return possible;
}
/** Returns true if publishing is possible based on if the variant is a un-published mandatory language */
function canVariantPublish(variant) {
//if this variant will show up in the publish-able list
var publishable = dirtyVariantFilter(variant);
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
// is this variant mandatory:
if (variant.language.isMandatory && !published && !variant.publish) {
//if a mandatory variant isn't published or set to be published
//then we cannot continue
return false;
}
// is this variant selected for publish:
if (variant.publish === true) {
return publishable;
}
return null;
}
function changeSelection(variant) {
$scope.model.disableSubmitButton = !canPublish();
//need to set the Save state to true if publish is true
variant.save = variant.publish;
variant.willPublish = canVariantPublish(variant);
}
function dirtyVariantFilter(variant) {
@@ -99,32 +116,49 @@
_.each(vm.variants,
function (variant) {
if(variant.state !== "NotCreated"){
vm.isNew = false;
if(variant.state === "NotCreated") {
vm.isNew = true;
}
});
}
);
_.each(vm.variants,
function (variant) {
variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : "");
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
// reset to not be published
variant.publish = false;
variant.save = false;
//check for pristine variants
if (!vm.hasPristineVariants) {
vm.hasPristineVariants = pristineVariantFilter(variant);
}
if(hasAnyData(variant)){
// If the variant havent been created jet.
if(variant.state === "NotCreated") {
// If the variant is mandatory, then set the variant to be published.
if (variant.language.isMandatory === true) {
variant.publish = true;
variant.save = true;
}
}
variant.canPublish = dirtyVariantFilter(variant);
// if we have data on this variant.
if(variant.canPublish && hasAnyData(variant)) {
// and if some varaints havent been saved before, or they dont have a publishing date set, then we set it for publishing.
if(vm.isNew || variant.publishDate == null){
variant.publish = true;
variant.save = true;
}
}else{
variant.publish = false;
variant.save = false;
variant.canSave = false;
}
});
variant.willPublish = canVariantPublish(variant);
}
);
if (vm.variants.length !== 0) {
//now sort it so that the current one is at the top
@@ -153,7 +187,6 @@
localizationService.localize(labelKey).then(function (value) {
vm.headline = value;
vm.loading = false;
});
@@ -10,7 +10,7 @@
<div class="umb-list umb-list--condensed" ng-if="!vm.loading">
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId">
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid || (variant.notifications | filter:{type:1}).length > 0}">
<ng-form name="publishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
@@ -18,27 +18,28 @@
type="checkbox"
ng-model="variant.publish"
ng-change="vm.changeSelection(variant)"
ng-disabled="(vm.isNew && variant.language.isMandatory) || (variant.canSave === false)"
ng-disabled="(variant.canPublish === false)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
<span class="db umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - </span>
<span ng-if="variant.language.isMandatory" ng-class="{'text-error': (variant.language.isMandatory && variant.willPublish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
</span>
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description" ng-class="{'text-success': notification.type === 3, 'text-error': notification.type !== 3}">{{notification.message}}</span>
</span>
</label>
<div class="umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
@@ -64,7 +65,7 @@
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
<div class="umb-permission__description text-success">{{notification.message}}</div>
</div>
</div>
@@ -22,11 +22,13 @@
<p><localize key="content_publishDescendantsWithVariantsHelp"></localize></p>
</div>
<div style="margin-bottom: 15px;">
<label>
<input type="checkbox"
ng-model="model.includeUnpublished"
style="margin-right: 8px;" />
<div style="margin-bottom: 15px;" class="flex">
<umb-checkbox
input-id="includeUnpublishedSelector"
model="model.includeUnpublished" />
<label for="includeUnpublishedSelector">
<localize key="content_includeUnpublished"></localize>
</label>
</div>
@@ -40,31 +42,31 @@
<div class="umb-list-item umb-list--condensed" ng-repeat="variant in vm.variants">
<ng-form name="publishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="publishVariantSelector"
type="checkbox"
ng-model="variant.publish"
ng-change="vm.changeSelection(variant)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<umb-checkbox
input-id="{{variant.htmlId}}"
name="publishVariantSelector"
model="variant.publish"
on-change="vm.changeSelection(variant)"
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 0;">
<span>{{ variant.language.name }}</span>
<span class="db umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</span>
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
</label>
<div class="umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
(function () {
"use strict";
function SaveContentController($scope, localizationService) {
function SaveContentController($scope, localizationService, contentEditingHelper) {
var vm = this;
vm.loading = true;
@@ -75,7 +75,7 @@
_.each(vm.variants,
function (variant) {
variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : "");
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
//check for pristine variants
@@ -6,54 +6,12 @@
<umb-load-indicator></umb-load-indicator>
</div>
<div ng-if="vm.isNew && !vm.loading">
<div ng-if="!vm.loading">
<div style="margin-bottom: 15px;">
<p><localize key="content_languagesToSaveForFirstTime"></localize></p>
</div>
<div class="umb-list umb-list--condensed">
<div class="umb-list-item" ng-repeat="variant in vm.variants">
<ng-form name="saveVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="saveVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-disabled="vm.isNew"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
</label>
<div ng-if="!saveVariantSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
</div>
<div ng-messages="saveVariantSelectorForm.saveVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
</ng-form>
</div>
<br/>
</div>
</div>
<div ng-if="!vm.isNew && !vm.loading">
<div style="margin-bottom: 15px;">
<p><localize key="content_languagesToSave"></localize></p>
<p>
<localize ng-if="!vm.isNew" key="content_languagesToSave"></localize>
<localize ng-if="vm.isNew" key="content_languagesToSaveForFirstTime"></localize>
</p>
</div>
<div class="umb-list umb-list--condensed">
@@ -61,31 +19,31 @@
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId">
<ng-form name="saveVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="saveVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-change="vm.changeSelection(variant)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<umb-checkbox
input-id="{{variant.htmlId}}"
name="saveVariantSelector"
model="variant.save"
on-change="vm.changeSelection(variant)"
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
<span class="db" ng-if="!saveVariantSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
</span>
<span class="db" ng-messages="saveVariantSelectorForm.saveVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
</label>
<div ng-if="!saveVariantSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
</div>
<div ng-messages="saveVariantSelectorForm.saveVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{saveVariantSelectorForm.saveVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
@@ -96,7 +54,10 @@
<div class="umb-list umb-list--condensed" ng-if="vm.hasPristineVariants">
<div style="margin-bottom: 15px; font-weight: bold;">
<p><localize key="content_unmodifiedLanguages"></localize></p>
<p>
<localize ng-if="!vm.isNew" key="content_unmodifiedLanguages"></localize>
<localize ng-if="vm.isNew" key="content_untouchedLanguagesForFirstTime"></localize>
</p>
</div>
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter">
@@ -111,7 +72,7 @@
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
<div class="umb-permission__description text-success">{{notification.message}}</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
(function () {
"use strict";
function ScheduleContentController($scope, $timeout, localizationService, dateHelper, userService) {
function ScheduleContentController($scope, $timeout, localizationService, dateHelper, userService, contentEditingHelper) {
var vm = this;
@@ -45,7 +45,7 @@
_.each(vm.variants,
function (variant) {
variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : "");
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
//check for pristine variants
@@ -87,23 +87,24 @@
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter">
<ng-form name="scheduleSelectorForm">
<div class="flex">
<input id="{{'saveVariantSelector_' + variant.language.culture}}"
name="saveVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-change="vm.changeSelection(variant)"
style="margin-right: 8px;" />
<umb-checkbox
input-id="{{'saveVariantSelector_' + variant.language.culture}}"
name="saveVariantSelector"
model="variant.save"
on-change="vm.changeSelection(variant)"/>
<div>
<label for="{{'saveVariantSelector_' + variant.language.culture}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
</label>
<div class="umb-permission__description"
ng-if="!scheduleSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<span class="db umb-permission__description"
ng-if="!scheduleSelectorForm.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</span>
</label>
<div class="flex items-center" style="margin-top: 10px; margin-bottom: 10px;">
@@ -166,11 +167,11 @@
val-server-field="{{variant.htmlId}}" />
<div ng-messages="scheduleSelectorForm.saveVariantReleaseDate.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{scheduleSelectorForm.saveVariantReleaseDate.errorMsg}}</div>
<div class="umb-permission__description text-error" ng-message="valServerField">{{scheduleSelectorForm.saveVariantReleaseDate.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
<div class="umb-permission__description text-success">{{notification.message}}</div>
</div>
</div>
@@ -1,7 +1,7 @@
(function () {
"use strict";
function SendToPublishController($scope, localizationService) {
function SendToPublishController($scope, localizationService, contentEditingHelper) {
var vm = this;
vm.loading = true;
@@ -23,6 +23,12 @@
if (vm.variants.length !== 0) {
_.each(vm.variants,
function (variant) {
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
});
//now sort it so that the current one is at the top
vm.variants = _.sortBy(vm.variants, function (v) {
return v.active ? 0 : 1;
@@ -13,31 +13,31 @@
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.modifiedVariantFilter">
<ng-form name="publishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="publishVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-change="vm.changeSelection(variant)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<umb-checkbox
input-id="{{variant.htmlId}}"
name="publishVariantSelector"
model="variant.save"
on-change="vm.changeSelection(variant)"
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
<span class="db" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
</span>
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
</label>
<div ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state class="umb-permission__description" variant="variant"></umb-variant-state>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
(function () {
"use strict";
function UnpublishController($scope, localizationService) {
function UnpublishController($scope, localizationService, contentEditingHelper) {
var vm = this;
var autoSelectedVariants = [];
@@ -21,6 +21,12 @@
});
}
_.each(vm.variants,
function (variant) {
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
});
// node has variants
if (vm.variants.length !== 1) {
//now sort it so that the current one is at the top
@@ -51,7 +57,7 @@
});
$scope.model.disableSubmitButton = !firstSelected; //disable submit button if there is none selected
// if a mandatory variant is selected we want to selet all other variants
// if a mandatory variant is selected we want to select all other variants
// and disable selection for the others
if(selectedVariant.save && selectedVariant.language.isMandatory) {
@@ -16,24 +16,24 @@
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.publishedVariantFilter">
<ng-form name="unpublishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="unpublishVariantSelector"
type="checkbox"
ng-model="variant.save"
ng-change="vm.changeSelection(variant)"
ng-disabled="variant.disabled"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<umb-checkbox
input-id="{{variant.htmlId}}"
name="unpublishVariantSelector"
model="variant.save"
on-change="vm.changeSelection(variant)"
disabled="variant.disabled"
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
</label>
<div class="umb-permission__description">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<span class="db umb-permission__description">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</span>
</label>
</div>
</div>
@@ -22,7 +22,7 @@ function startUpVideosDashboardController($scope, dashboardResource) {
angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController);
function startUpDynamicContentController($timeout, $scope, dashboardResource, assetsService, tourService, eventsService) {
function startUpDynamicContentController($q, $timeout, $scope, dashboardResource, assetsService, tourService, eventsService) {
var vm = this;
var evts = [];
@@ -167,7 +167,7 @@ function FormsController($scope, $cookies, packageResource, localizationService)
return packageResource.installData(pack);
}, vm.error)
.then(function(pack) {
vm.state = installStateComplete;
vm.state = labels.installStateComplete;
return packageResource.cleanUp(pack);
}, vm.error)
.then(vm.complete, vm.error);
@@ -22,7 +22,6 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat
treeService.removeNode($scope.currentNode);
navigationService.hideMenu();
});
};
$scope.performContainerDelete = function () {

Some files were not shown because too many files have changed in this diff Show More