Compare commits

...
Author SHA1 Message Date
Bjarke BergandGitHub 93badabcb1 Added extra (unnessasary) WHERE-clause to help sql server generate a smarter query plan (#12198) 2022-03-31 11:52:02 +02:00
Bjarke Berg 181a7cb27d Bump versions to 9.4.2 2022-03-29 09:33:37 +02:00
Andy ButlandandBjarke Berg 0418be4318 Corrected the base URL to use when retrieving and setting public access details. (#12178)
* Corrected the base URL to use when retrieving and setting public access details.

* Fixed original issue from a merge conflict.. Now using the correct v9+ resource

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-03-29 09:19:59 +02:00
Ronald BarendseandPaul Johnson 037580b305 Fix obsolete constructor in RecurringHostedServiceBase (#12172)
* Use NullLogger in obsoleted constructor

* Create missing logger during execution instead

(cherry picked from commit d0823d4236)
2022-03-24 10:37:52 +00:00
Mole 4fffb9f7a7 Bump versions to non-rc 2022-03-23 09:48:21 +01:00
78cfb29908 Item tracking fixes (#12146)
* Cleanup; Fix lang keys

* Documentation

* Typos

* Distinct the results

* Changed GetPagedRelationsForItems to GetPagedRelationsForItem as we would only expect a single id to be passed when calling this + fix more docs

* Changed to the correct reference

* Unused code

* Only load references when info tab is clicked

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-03-23 09:33:36 +01:00
Nikolaj Geisle 20f0ceeda6 Merge pull request #12161 from umbraco/v9/bugfix/amend_breaking_change_in_RecurringHostedServiceBase
Amend breaking change in RecurringHostedServiceBase
2022-03-23 09:15:27 +01:00
Nikolaj GeisleandNikolaj Geisle 0d836875c7 Merge pull request #12153 from vsilvar/v9/bugfix/12022_recurring_hosted_service_scope_leak
Fixes RecurringHostServices leaking the execution context / ambient scope
2022-03-22 13:03:14 +01:00
Sebastiaan Janssen 04c292f167 Merge pull request #12155 from umbraco/v9/bugfix/10066
Same fix as #12154 - fixes #10066

(cherry picked from commit a302b10f66)
2022-03-22 10:09:36 +01:00
Nikolaj GeisleandGitHub b97d016c13 Merge pull request #12156 from umbraco/v9/bugfix/youtube-link
Use an umbra.co link for the TV replacement channel
2022-03-22 08:19:58 +01:00
Sebastiaan Janssen 0aa4d1956a Also update Umbraco TV link + text in the help panel 2022-03-21 16:14:48 +01:00
Sebastiaan Janssen cde312b6d4 Use an umbra.co link for the TV replacement channel so we can change it in the future if we need to 2022-03-21 15:47:51 +01:00
Nikolaj GeisleandNikolaj Geisle a9daab5a15 Merge pull request #12139 from umbraco/v9/bugfix/track-media-items-picked-as-macro-params
Fix media tracking of items added via macro parameters in RTE and Grid
2022-03-21 08:34:36 +01:00
c7e45ae13a Encode path (#12132)
Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
2022-03-17 14:53:53 +01:00
Paul JohnsonandBjarke Berg 1ab0bb9254 fix additional legacy password formats (#12120)
* Added failing test to demo issue.

* Handle old machine key default.
2022-03-10 15:00:16 +01:00
Bjarke Berg 8746d4c13d Small typos fixed.. 2022-03-09 14:59:55 +01:00
Andy ButlandandNikolaj 5d9e2b0bb8 Updates JSON schema to reference latest Forms and Deploy. (#12106) 2022-03-09 09:19:34 +01:00
Bjarke BergandGitHub c20f45dcfc Fixed issue with pageindex (0-indexed) vs pagenumber (1 indexed) (#12113) 2022-03-09 08:53:18 +01:00
MoleandGitHub fff3e5e9d4 Fix saving content not updating the page (#12108)
* Fix relation type tests

* Fix saving content not updating the UI

We have to map to display after we've saved content or we'll be missing information generated when saving content such as ID

* Dont map to display to get isBlueprint
2022-03-08 11:33:22 +01:00
413a278373 V9: Fix missing EditedCultures and Name in content saving notifications (#12070)
* Map dirty culture to EditedCultures before saving

And set the name of the content when setting default culture variant name

* Update src/Umbraco.Web.BackOffice/Controllers/ContentController.cs

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

Co-authored-by: nikolajlauridsen <nel@umbraco.dk>
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2022-03-08 09:23:44 +01:00
Bjarke Berg 5d6fc6005c Unbreak breaking change 2022-03-08 07:45:53 +01:00
d9d5dc5626 Item tracking improvements (#11919)
* Adding TrackedReferencesController

* Adding/changing views

* Adding/Editing js files

* RelationService changes

* RelationRepository changes

* Adding missing translations

* Adding/Modifying tests

* Beginning of #9125 migration

* Introducing a new component + refactoring based on that

* Abstracting + refactoring

* Work on content unpublishing

* Work on media.delete

* Various small changes

* Beginning of #9119 migration

* Changes on content.delete

* Various fixes

* Adding new keys used in the listview bulk actions

* Adding methods to get the items used in relations from array of ids

* Adding the checkLinkedItems function to the trackedReferencesResource

* Passing the selected items from a listview to unpublish and delete

* Adding umb-tracked-references-listview

* Adding umb-tracked-references-listview-table with language column

* Fixes for tracked references

* Changes in listview unpublish dialog

* Changes in listview delete dialog

* Removing Variants logic as it is not currently supported

* Visual fixes

* Closing dialogs on click

* Fix wording

* Fix breaking changes

* Change to a single title "Items in use" instead of 2 different for Content and Media

* No need for obsoleting because we can change new controllers

* Return ActionResult from actions

* V9:  Prevent delete or unpublish of items that have references (#12047)

* Introducing config settings that prevent delete or unpublish of items referenced by other items

* Disable deletion of content items and show a new warning

* Disable deletion of media items and show a new warning

* Disable deletion of list view items

* Disable unpublish and bulk unpublish

* Add a new warning

* V9: Displaying descendants in use as part of item tracking (#12039)

* Replace HasReferencesInDescendants with GetPagedDescendantsInReferences

* Display descendants in use on parent's info tab

* Add getPagedDescendantsInReferences to trackedReferencesResource

* Add lang keys for Descendants in use

* Refactoring controller actions

* Don't call check descendants usage when it is a new item

* rename busfy to busy

* always show references

* rearrange for scrollbar to appear at the edge of the dialog

* use the word referenced instead of used

* change fallback texts

* Added "IsDependency" to relation types

* refactor of umb-tracked-references

* rename checkLinkedItems to getPagedReferencedItems

* rename check to load, to be consistent with the rest.

* Refactored backend . Needs frontend fixes

* Cleanup

* Use filters

* Front-end refactor to match refactored end-points

* Fixed bug + warning

* Fixed query (more then 2100 descensdants) and optimized it (using nested select instead of inner join).

* remove comment

* hideNoneDependencies including varying text for the configuration

* Hack for SqlCE :(

* some final adjustments for item tracking ui

* Unbreak change

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-03-07 22:46:16 +01:00
Bjarke Berg 2ff2ae3f0a Unbreak a change.. 2022-03-07 22:14:21 +01:00
Bjarke Berg cfacf4e150 Rollback unnecessary breaking change 2022-03-07 22:07:50 +01:00
Sebastiaan Janssen d8551e4640 Merge branch 'v9/dev' into v9/contrib 2022-03-07 11:41:25 +01:00
Owain WilliamsandGitHub 373138d70f Make GridPropertyEditor dirty if a control is removed
This is the same fix that @inetzo made for a PR I checked earlier today except the fix was for U8.  Since I didn't notice it was U8 until I had merged, I thought I'd fix it here but credit should go to @inetzo 

#12097
2022-03-05 17:24:19 +00:00
Callum WhyteandMichael Latouche 10d252819d Adding null check to IsCulturePublished on IContent 2022-03-05 03:05:31 +01:00
Paul JohnsonandGitHub ca5c85e656 v9 fix misc issues external member login (#12093)
* Add missing override for SetTokenAsync

* Fix mismatch between expected scheme prefix and exception message

* Store tokens on member update
2022-03-03 17:18:39 +00:00
a6aaf4b0b1 Introduced new "large" overlay size (#11945)
* Introduced new "large" overlay size (1/2)

* Introduced new "large" overlay size (2/2)

* #H5IS

Co-authored-by: Owain <owilliams@spindogs.com>
2022-03-03 12:53:58 +00:00
Matt BrailsfordandGitHub 44e8808079 Check form and querystring when validating ufprt in ValidateUmbracoFormRouteStringAttribute (#11957)
* Check form and querystring when validating ufprt

Checks to see if the request has form data before validating the `ufprt` parameter, and if it doesn't assumes it must be on the querystring

* Create GetUfprt extension method

* Use GetUfprt extension

* Update UmbracoRouteValueTransformer to use GetUfrpt()

* Added missing using statement

* Check for StringValues.Empty
2022-03-03 10:42:14 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
59b0224eff Bump karma from 6.3.14 to 6.3.16 in /src/Umbraco.Web.UI.Client (#12090)
Bumps [karma](https://github.com/karma-runner/karma) from 6.3.14 to 6.3.16.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v6.3.14...v6.3.16)

---
updated-dependencies:
- dependency-name: karma
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-02 15:06:45 +01:00
Nikolaj GeisleandGitHub 65723ea9ec Merge pull request #12087 from umbraco/v9/bugfix/history-cleanup-make-contenttype-dirty
V9: Fix history cleanup not making content type dirty
2022-03-02 12:23:24 +01:00
Nikolaj GeisleandGitHub 50a239a016 Merge pull request #12081 from umbraco/v9/bugfix/fix-invariant-redirect-tracking
Fix invariant redirect tracking
2022-03-02 12:20:15 +01:00
nikolajlauridsen 5ef013ca62 Ensure ContentType is only marked as dirty when it's actually changed 2022-03-02 10:49:06 +01:00
ff477c3930 Update src/Umbraco.Infrastructure/Routing/RedirectTrackingHandler.cs
Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
2022-03-02 10:26:23 +01:00
nikolajlauridsen 1cdc6f0fd2 Make HistoryCleanup register as dirty in ContentType 2022-03-02 10:15:50 +01:00
nikolajlauridsen 49e0e6c1c9 Implement BeingDirtyBase on HistoryCleanup 2022-03-02 10:11:48 +01:00
nikolajlauridsen 7e9def9df4 Add tests 2022-03-02 10:11:33 +01:00
009b36b49e Send key from frontend to postsave (#12080)
Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
2022-03-02 09:12:07 +01:00
44ddf7acef Add extra check (#12075)
Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk>
2022-03-02 08:47:35 +01:00
Chad CurrieandMichael Latouche 6b3a589aa0 Check Debug logs are enabled for hot path 2022-03-02 02:06:52 +01:00
nikolajlauridsen 95f1ed33ec Make empty string invariant culture in DefaultUrlProvider 2022-03-01 14:14:48 +01:00
nikolajlauridsen 993c582bd9 Handle empty string as invariant when generating cache key 2022-03-01 13:51:21 +01:00
Bjarne FyrstenborgandNathan Woulfe a72698e1a9 Transition of lazy loaded image 2022-03-01 17:16:32 +10:00
Martin BentancourandGitHub c905fdd9b5 Validate that imageUrl param is a relative path (#11606)
* Validate that imageUrl param is a relative path

To prevent open redirects, the imagePath should point to a relative path (i.e. not point to a different domain).

* LocalRedirectResult instead of RedirectResult

Uri.Relative does not prevent paths like //google.com/test.jpg
Checking for both relative and local seems to cover all the cases.
2022-02-28 23:59:18 +01:00
34bfd3a8f3 Fixes issue with OrderByDescending generating invalid SQL (#11673)
* Fixes issue with OrderByDescending generating invalid SQL

* Update OrderByDexcending with single field for consistecy

Co-authored-by: Michael <michael@crossingpaths.be>
2022-02-28 23:06:59 +01:00
nikolajlauridsen dfbb182a94 Handle invariant culture in RedirectTrackingHandler
GetRouteById and RedirectUrlService expects the culture to be null if it's invariant, however, Cultures in IPublished content uses empty string for invariant culture
2022-02-28 17:29:11 +01:00
Corné HoskamandGitHub aba4f02bad (umbraCollab) Check media Parent for permissions when setting correct MediaType (#11858) 2022-02-28 16:29:23 +01:00
patrickdemooij9andGitHub a27da0fced Content app for dictionary items (#11553) 2022-02-28 15:01:18 +01:00
0c7ef06031 V9: Fix missing site identifier (#12040)
* Add SiteIdentifierService

* Use SiteIdentifierService in TelemetryService

* Use SiteIdentifierService when installing

* Remove timeout

* Use TryGetOrCreateSiteIdentifier in TelemetryService

* Add site identifier to dashboard url

* Fix and add tests

* Don't accept empty guid as valid site identifier

* Fix dashboard controller

* Fix site id query parameter

* Use Optionsmonitor onchange

Co-authored-by: nikolajlauridsen <nel@umbraco.dk>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-02-28 13:59:39 +01:00
MehmetandGitHub e6d6dce3a8 Fix mssing image caption in RTE (#11807) 2022-02-28 13:17:17 +01:00
Blake IrwinandGitHub 24d2dcf363 Fix JsonConfigManipulator causing upgrades to fail when appsettings.json files are not present (#11222) 2022-02-28 10:52:03 +01:00
Scott BradyandGitHub 25ea5cdb1b Prevent non-backoffice auth schemes being overridden (#11630) 2022-02-28 10:40:51 +01:00
Bjarne FyrstenborgandGitHub cf3d697bc5 Update RemoveTreeController to use TController (#11795) 2022-02-28 09:36:35 +01:00
Bjarne FyrstenborgandGitHub 6fd45f653c Media entry editor listview children (#12061) 2022-02-27 17:46:40 +01:00
Bjarne FyrstenborgandSebastiaan Janssen d97d0040c2 Remove unused variable 2022-02-27 16:57:57 +01:00
Bjarne FyrstenborgandSebastiaan Janssen aa62417916 Update mapping so we know if it is blueprint on first save before content has been created 2022-02-27 16:57:57 +01:00
Bjarne FyrstenborgandSebastiaan Janssen 62e3286c71 Vary localization based on content or blueprint 2022-02-27 16:57:57 +01:00
dependabot[bot]andSebastiaan Janssen d433991b61 Bump karma from 4.4.1 to 6.3.14 in /src/Umbraco.Web.UI.Client
Bumps [karma](https://github.com/karma-runner/karma) from 4.4.1 to 6.3.14.
- [Release notes](https://github.com/karma-runner/karma/releases)
- [Changelog](https://github.com/karma-runner/karma/blob/master/CHANGELOG.md)
- [Commits](https://github.com/karma-runner/karma/compare/v4.4.1...v6.3.14)

---
updated-dependencies:
- dependency-name: karma
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-27 12:29:42 +01:00
dependabot[bot]andSebastiaan Janssen bc24efa812 Bump url-parse from 1.5.7 to 1.5.10 in /src/Umbraco.Web.UI.Client
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.7 to 1.5.10.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.7...1.5.10)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-27 11:58:35 +01:00
Bjarne FyrstenborgandGitHub 6d52a2f3da Babel adjustments (#12044) 2022-02-27 11:56:10 +01:00
ChadandGitHub 66961d18dc Prevent DatabaseServerMessenger handle leak (#12013) 2022-02-27 11:43:35 +01:00
patrickdemooij9andGitHub fa0027d4f1 Added property way of seeing composition (#11811) 2022-02-27 10:09:35 +01:00
Blake IrwinandGitHub a24b099831 V9: Fix Umbraco:CMS:Global:SqlWriteLockTimeOut setting value ignored (#11492) 2022-02-27 09:52:07 +01:00
Bjarne FyrstenborgandGitHub 6860d0b27d Filter block cards and show empty message (#11729) 2022-02-27 09:36:43 +01:00
AndyBootandGitHub dad1c190fc Settings Dashboard Umbraco.tv References (#12034) 2022-02-27 09:31:22 +01:00
Arkadiusz BielandSebastiaan Janssen 36faff6917 add null check for case when index is not implementing LuceneDirectoryIndexOptions 2022-02-27 09:19:23 +01:00
ChadandGitHub 7ddddb7500 Support async in TreeController (#12016) 2022-02-27 09:16:33 +01:00
Jason ElkinandGitHub b56f0f7062 Make views compatible with Nullable Reference Types (#11370) 2022-02-27 09:10:08 +01:00
Bartlomiej KomendarczukandMichael Latouche 9121879e1a 11921: Update CultureName after language change 2022-02-26 03:53:52 +01:00
Bjarne FyrstenborgandGitHub 69132faffa Set current selected master template in tree (#12042)
* Set current selected master template in tree

* Adjust styling for tree icon

* Code adjustment
2022-02-26 02:48:02 +01:00
Paul JohnsonandGitHub 4a6c409a1f Explicitly close release signal file. (#12057) 2022-02-25 10:56:45 +00:00
Paul JohnsonandGitHub 4351ce6ee4 Further changes requested during review of #12049 (#12053) 2022-02-25 08:22:37 +00:00
vsilvarandMichael Latouche 1b56a260b8 Allow configuring TinyMCE scripting sanitization 2022-02-25 01:38:54 +01:00
Paul JohnsonandGitHub de4b3af28f Resolve various points related to deficiencies in FileSystemMainDomLock (#12052)
* Resolve various points related to deficiencies in FileSystemMainDomLock

See GH #12049

* Increasing backoff time for retry when deleting lock release signal file
However reducing max tries, really hoping this never actually happens
and if it does, failing to boot ASAP seems reasonable.
2022-02-24 14:38:33 +00:00
Bjarke BergandGitHub 321d5b49bd Fix docfx namespace (#12048)
* Temp hack for building docs on pr

* Fix namespace

* revert hack to build docs on pr
2022-02-24 12:40:06 +01:00
Bjarke BergandGitHub 366d7c0fce Update docfx dependency (#12046)
* Update docfx

* temp commit to test out azure pipeline

* Rollback temp fix
2022-02-24 11:30:20 +01:00
Paul JohnsonandGitHub 860c8e8ae2 Filesystem based MainDomLock & extract interface for MainDomKey generation (#12037)
* Extract MainDomKey generation to its own class to ease customization.

Also add discriminator config value to GlobalSettings for advanced users.
Prevents a mandatory custom implementation, should be good enough for
the vast majority of use cases.

* Prevent duplicate runs of ScheduledPublishing during slot swap.

* Add filesystem based MainDomLock
2022-02-24 10:17:34 +00:00
Patrick de MooijandNathan Woulfe 15bedfbc76 11777: Remove sorting because we need the correct order 2022-02-24 07:46:58 +10:00
Chad CurrieandNathan Woulfe fe0ec420eb Add using statements for IDisposables 2022-02-24 06:46:11 +10:00
Sebastiaan Janssen 860792ff3f Revert "UmbEditorHeaders hide-icon on "true"" #11661
This reverts commit ff6a8d781d.
2022-02-23 14:38:47 +01:00
Bjarne FyrstenborgandSebastiaan Janssen 169d07b0cc Grant access to get all content types so content templates can be moved to content section 2022-02-23 14:13:07 +01:00
dependabot[bot]andSebastiaan Janssen 8c5a5af41b Bump follow-redirects in /src/Umbraco.Web.UI.Client
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-23 10:17:39 +01:00
Sebastiaan JanssenandGitHub 411fb0c120 Update CODE_OF_CONDUCT.md 2022-02-23 09:56:21 +01:00
Sebastiaan JanssenandGitHub 27c60277f2 Update CODE_OF_CONDUCT.md 2022-02-23 09:25:40 +01:00
Nikolaj GeisleandGitHub dafd7f298d Merge pull request #12024 from umbraco/v9/feature/add_user_group_tests
Add user group tests
2022-02-22 10:48:40 +01:00
Paul JohnsonandGitHub e67da0b198 Ignore certificate errors for KeepAlive task. (#12019) 2022-02-22 09:49:56 +01:00
4f0a837e20 V9: Fix login timeout (#12029)
* Turn SlidingExpiration off and only renew cookie of not RemainingSeconds request

Also adds the TicketExpiresClaim before validating the the security stamp, otherwise the claim won't be merged and "dissappear", leading to the user being instantly logged out

Also only EnsureValidSessionId if not RemainingSeconds request, otherwise the session will always be valid, since the remaining seconds request renews it.

* Don't ignore SessionIdClaimType and Cookiepath when merging claims

Besides what the comment used to state these claims are only issued when logging in, leading you to be logged out once the claims are merged, furthermore when we check the session ID we verify that you session has not expired.

* Manually specify Issued and Expires when renewing token

If we don't we lose 30 minutes of our ExpireTimeSpan every time the principal refreshes

* Re-add ignored claims

And use MergeAllClaims on refreshing principal instead.

* EnsureValidSessionId before updating IssuedUtc

* Fix comment

* Update src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs

Co-authored-by: nikolajlauridsen <nel@umbraco.dk>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-02-22 08:29:27 +01:00
dependabot[bot]andSebastiaan Janssen 864fd2a45a Bump url-parse from 1.5.3 to 1.5.7 in /src/Umbraco.Web.UI.Client
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.3 to 1.5.7.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.3...1.5.7)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-21 11:32:51 +01:00
Daniël KnippersandSebastiaan Janssen 8877c9c73d Fix Empty Recycle Bin view not found on Linux 2022-02-21 11:32:02 +01:00
Sebastiaan Janssen ab870347ed Merge branch 'v9/dev' into v9/contrib
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/pages/login.less
2022-02-21 10:06:08 +01:00
Robert FosterandGitHub 06f2408ebc Fix for #11977 - Content Name auto-numbering correction (#11996) 2022-02-21 10:20:08 +10:00
Mark DrakeandNathan Woulfe a53d28310f new stick top position for rte toolbar if tabs are present, issue #11870 2022-02-21 10:19:10 +10:00
LantzifyandNathan Woulfe 9568d0cd6d Added name & color description for umbAvatar 2022-02-21 10:02:41 +10:00
Anders BjernerandNathan Woulfe aba5edce02 Fixed issue in umbeditorheader.directive.js examples 2022-02-21 08:25:04 +10:00
Paul Johnson 791e4f92a1 Quick fix for PrefixLocalCallsWithThis 2022-02-18 11:41:27 +00:00
Nikolaj Geisle 566648de3f Add user group tests 2022-02-18 09:26:47 +01:00
Mole a13e66d559 Merge branch 'v9/9.3' into v9/dev
# Conflicts:
#	build/templates/UmbracoPackage/.template.config/template.json
#	build/templates/UmbracoProject/.template.config/template.json
#	src/Directory.Build.props
2022-02-17 15:09:55 +01:00
Bjarke BergandGitHub 3a269ed329 Fix issue with migration from 8 and added support for 8.18 migration (#12020)
* Fixed issue with migration from 8, because the external login table is changed in 9.3, but added in 9.0. we need to use the legacy models when running 9.0 migrations

* Support migration from 8.18 (final state)
2022-02-17 15:06:25 +01:00
Ronald BarendseandMole 7f762df952 Fix DataValueEditor incorrectly serializing JSON value (#12015)
* Remove JSON serialization

* Skip JSON serialization of strings
2022-02-17 13:34:48 +01:00
Ronald BarendseandGitHub 27a3431e76 Fix DataValueEditor incorrectly serializing JSON value (#12015)
* Remove JSON serialization

* Skip JSON serialization of strings
2022-02-17 13:33:45 +01:00
Mole 7cc495dc65 Bump version to 9.3.1 2022-02-17 13:20:53 +01:00
Bjarke BergandMole b581a45e05 Fix for rooted UNC paths (#11982) 2022-02-17 13:17:53 +01:00
Bjarke BergandGitHub 76fc3f8201 Fix for rooted UNC paths (#11982) 2022-02-17 10:59:35 +00:00
555b4fadf1 Lookup plugin lang / icons folders in a case insensitive way (#11985)
* Use lowercase lang folder name for packages lang files

Use lowercase lang folder name for packages lang files to be consistent with Umbraco's casing

* Case insensitive `lang` folder lookup

* Comment grammar

* Setup lower case appl_plugin icons folder path

* Update Constants-SystemDirectories.cs

* Check both casings for the AppPluginIcons path

* Fixed spelling mistake + avoid multiple Exists checks

* Add IsCaseSensitiveFileSystem to IHostingEnvironment

* Add IsCaseSensitiveFileSystem to AspNetCoreHostingEnvironment

* Undo last changes

* Undo last changed

* Add FileSystemUtility

* Only perform second iconPath if OS is case sensitive

* Undo changes

* Undo changes

* Remove filesystem utils file

* Added HostingEnvironmentExtensions.cs

* Use IsCaseSensitiveFileSystem extension method

* Use the Umbraco.Extensions namespace

* Simplify IsCaseSensitiveFileSystem

* Better naming

* Use PluginIcons

* Remove unused using statement

* Delete HostingEnvironmentExtensions.cs

* Update IconService.cs

* Comment clarity

* Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs

Co-authored-by: Paul Johnson <pmj+github@ologn.co.uk>

Co-authored-by: Paul Johnson <pmj+github@ologn.co.uk>
Co-authored-by: Michael Latouche <michael@crossingpaths.be>
2022-02-17 00:04:41 +01:00
Søren KottalandMichael Latouche 79817f5463 Makes group name wider to fit long group names 2022-02-16 02:20:21 +01:00
LantzifyandNathan Woulfe ff6a8d781d UmbEditorHeaders hide-icon on "true" 2022-02-16 09:43:43 +10:00
Bjarne FyrstenborgandGitHub 789a908a31 Adjust z-index of notifications (#11634)
* Store notification z-index in variable

* Only show unordered list if any notifications

* Move notificaion layers to same parent as overlays so layers can be controlled via z-index
2022-02-16 09:27:13 +10:00
Søren KottalandNathan Woulfe 336f2c2d5b Makes the template column i NC config expandable 2022-02-16 09:17:59 +10:00
JeavonandNathan Woulfe 8b6c844a55 Exclude the Umbraco Log files from UmbracoProject template gitignore 2022-02-16 07:57:06 +10:00
f1c83fbd80 Only select ItemIdFieldName in PublishedContentQuery.Search to improve performance (#11950)
* Fix code styling

* Only select ItemIdFieldName and fix exception when variation context is null

* Save hashset instead of making on on each request (indirectly)

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-02-15 16:46:20 +01:00
Bjarke Berg 0f4020dc7b Merge remote-tracking branch 'origin/v8/dev' into v9/dev
# Conflicts:
#	build/NuSpecs/UmbracoCms.Web.nuspec
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
#	src/Umbraco.Examine/Umbraco.Examine.csproj
#	src/Umbraco.Tests/Umbraco.Tests.csproj
#	src/Umbraco.Web.UI.Client/src/assets/img/application/umbraco_logo_large_blue.svg
#	src/Umbraco.Web.UI.Client/src/assets/img/application/umbraco_logomark_white.svg
#	src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js
#	src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less
#	src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html
#	src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js
#	src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js
#	src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html
#	src/Umbraco.Web/Editors/BackOfficeServerVariables.cs
#	src/Umbraco.Web/Umbraco.Web.csproj
2022-02-15 14:10:17 +01:00
62fa1695df Add config to hide backoffice logo (#11999)
* Added config to hide backoffice logo

* rename to hideBackofficeLogo

* hide on mobile

* add hideBackofficeLogo

* implement hideBackofficeLogo + toggle on click

* Updated c# syntax

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
2022-02-15 10:48:52 +01:00
Andy ButlandandGitHub de2668a621 Added section for promoted packages in the back-office. (#11947)
* Added section for promoted packages in the back-office.

* Updates from PR review.
2022-02-14 17:41:12 +01:00
Paul Johnson a34e278a40 Fix whitespace in sln 2022-02-14 14:46:37 +00:00
Paul Johnson 461043bd82 Change web projects TargetFrameworkMoniker to 4.5.2 to stop VS 2022 err 2022-02-14 14:43:33 +00:00
Sebastiaan JanssenandNathan Woulfe 7f9b143e1a Fix items found in PVS-Studio analysis 2022-02-14 08:08:42 +10:00
Paul JohnsonandGitHub 91c4c77676 Switch a lot of warnings to suggestions until we are able to resolve. (#11974)
* Switch a lot of warnings to suggestions until we are able to resolve.

* Make stylecop respect more csharp_style rules

e.g. csharp_using_directive_placement

* Added cheatsheet

* Drop sorting requirements for using directives.
2022-02-11 16:24:53 +00:00
Erik-Jan WestendorpandNathan Woulfe d1f1817744 Translate "Create dictionary item" to Dutch 2022-02-11 08:43:34 +10:00
Paul JohnsonandGitHub 8393a5ba04 Merge pull request #11952 from mattbrailsford/patch-7
Don't hold static UmbracoContext reference
2022-02-10 18:10:01 +00:00
Paul JohnsonandGitHub cf410ab91e Attempt to make app local icu setup less problematic. (#11961)
* Attempt to make app local icu setup less problematic.

Prevents issues for windows build agent -> linux app server.

On Windows version is split at first '.' e.g. 68.2.0.9 ->  icuuc68.dll
https://github.com/dotnet/runtime/blob/205f70e/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Windows.cs

On Linux we are looking for libicuuc.so.68.2.0.9
https://github.com/dotnet/runtime/blob/205f70e/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs

On macos we don't have a native library in a shiny nuget package so hope
folks are building on their macs or setting the rid until we have a
better solution.

* Combine elements
2022-02-10 12:03:35 +00:00
df6c8a4ecc refactor setTitle in MediaPicker.Controller.js (#11428)
* refactor setTitle in MediaPicker.Controller.js

* adding vm.activeTab

Co-authored-by: inada <inada@2p.com.sa>
2022-02-10 09:20:18 +10:00
Bjarne FyrstenborgandNathan Woulfe be6e0879e4 Align tags vertically in content type property component 2022-02-10 08:52:39 +10:00
Bjarne FyrstenborgandNathan Woulfe 731b06a987 Use pointer cursor pointer for keyboard shortcuts overview 2022-02-10 08:04:38 +10:00
Mark DrakeandGitHub 6f89bf2a45 fixes umbraco logo appearance on backoffice login page, issue #11830 (#11900)
* fixes umbraco logo appearance on backoffice login page, issue #11830

* gives umbraco logo minimum width of 30px, issue #11830
2022-02-10 08:00:57 +10:00
Callum WhyteandGitHub 4eda091fdb Lookup backoffice section name in route locationTitle (#11816)
* Lookup backoffice section name in route locationTitle

* Getting original title from $rootScope
2022-02-10 07:56:59 +10:00
nikolajlauridsen b63aa4b19c Merge remote-tracking branch 'origin/v9/9.3' into v9/dev
# Conflicts:
#	build/templates/UmbracoPackage/.template.config/template.json
#	build/templates/UmbracoProject/.template.config/template.json
#	src/Directory.Build.props
2022-02-09 13:31:49 +01:00
Matt BrailsfordandGitHub 8c780573f4 Don't hold static UmbracoContext reference
Fixes #11951 by changing `UmbracoContext` to be an expression rather than being a static setter thus ensuring the current umbraco context is always returned, rather than a cached version from initial execution
2022-02-08 09:41:35 +00:00
Niels LyngsøandGitHub 0b0182f550 Apply the Umbraco logo to BackOffice (#11949)
* adding logo as text in replacement of umbraco_logo_white, this will enable existing configuration to continue to work and minimise the breaking change of this PR.

* adjust logo position to fit with logged-in logomark. Allowing for the logo and customised logo to be very wide.

* adding logomark in topbar of backoffice

* login box style

* correction of shadow

* Logo modal, to display more information about the product including linking to the website

* rename to modal

* stop hidden when mouse is out

* Version line without Umbraco

* focus link and use blur as the indication for closing.

* correcting to rgba

* focus and click outside needs a little help to work well

* use @zindexUmbOverlay to ensure right depth going forward.

* adding large logo svg

* append ;

* tidy logo svg file
2022-02-08 10:35:06 +01:00
Bjarke Berg 158f4d29f6 Bump version to 9.4.0-rc 2022-02-04 13:02:19 +01:00
Bjarke BergandGitHub 139c5791a5 Merge pull request #11926 from umbraco/v9/bugfix/replicas-write-to-server-table
Don't write to server table if not using ElectedServerRoleAccessor
2022-02-01 14:53:37 +01:00
Bjarke BergandGitHub 27ef97bf34 Merge pull request #11924 from umbraco/v9/feature/override-sitename-from-config
Allow setting the site name from appsettings
2022-02-01 14:38:39 +01:00
Bjarke BergandGitHub d6f968eee4 Merge pull request #11874 from umbraco/v9/feature/play-nice-with-ServiceBasedControllerActivator
Prevent issues for those who wish use ServiceBasedControllerActivator.
2022-02-01 14:34:37 +01:00
Sebastiaan Janssen a6ce7bcdf6 Merge branch 'v8/dev' into v8/8.18 2022-02-01 11:36:26 +01:00
Sebastiaan Janssen 0200434227 Merge branch 'v8/contrib' into v8/dev 2022-02-01 11:09:02 +01:00
Kenn JacobsenandGitHub 9a5bcf290d Retain mculture when clicking results from global search (#7192) 2022-02-01 01:50:22 +01:00
Sebastiaan Janssen ec183e481c Bump version to 8.18.0-rc 2022-01-31 15:47:51 +01:00
Sebastiaan Janssen 924a819baa Upgrade ClientDependency to 1.9.10 2022-01-31 15:39:34 +01:00
3af6645ad8 Fix URL with culture (#11886)
* Ignore casing when comparing default culture

* Ignore casing in GetAssignedWithCulture

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2022-01-31 15:02:25 +01:00
Mole 824bc2ac28 Fix word salad in comment 2022-01-31 14:58:45 +01:00
Mole febdac5713 Fix tests 2022-01-31 14:46:25 +01:00
Mole 22dd3a214c Stop TouchServerTask is registered role accessor is not elected 2022-01-31 14:39:29 +01:00
Sebastiaan Janssen 4382868f82 Upgrade Examine to v1.2.2 2022-01-31 14:18:55 +01:00
Sebastiaan Janssen 5c019a1557 Merge branch 'v8/contrib' into v8/dev 2022-01-31 14:15:03 +01:00
Mole bdcb5d859e Get site name from appsettings if possible 2022-01-31 10:12:02 +01:00
Paul Johnson 1810cec80a Simplify registration for ambiguous ctor
ActivatorUtilitiesConstructor respected.
2022-01-31 08:39:33 +00:00
Bjarne FyrstenborgandGitHub 472cc716c4 Accept zip as extension in local package installer (#11109)
* Update ngf-pattern and accept

* Restructure less

* Remove ng-invalid again
2022-01-31 02:40:15 +01:00
andreymkarandashovandMichael Latouche 3167c8b213 encode group name to avoid the issue 2022-01-31 01:35:03 +01:00
Arul PrabakaranandGitHub 2abf455abf Updated border radius of .umb-notifications__notification to match button radius (#11745) 2022-01-31 09:08:51 +10:00
Bjarne FyrstenborgandGitHub f2450fb539 Constant icon blueprint (#11812)
* Add constant for blueprint icon

* Use constant for folder icon

* Create empty array of IEntitySlim as the GetAll() method returns IEnumerable<IEntitySlim>

* Use constant
2022-01-31 08:34:37 +10:00
c0dfb33916 Fix importing DocType if parent folder already exists (#11885)
* Don't try to create parent folder if it already exists

* Fix typo

Co-authored-by: Elitsa Marinovska <elm@umbraco.dk>
2022-01-28 12:30:44 +01:00
Lennard FonteijnandMichael Latouche 477697a70c Removed if-check to allow empty values from blocks 2022-01-28 03:43:53 +01:00
Bjarke BergandGitHub 5de36f5669 Merge pull request #11916 from umbraco/v9/feature/merge_v8_27012022
V9/feature/merge v8 27012022
2022-01-27 17:38:32 +01:00
a71529a058 V9/feature/merge v8 27012022 (#11915)
* Add allowlist for HelpPage

* Use current request for emails (#11775)

* Use current request for emails

* Fix tests

* Bump version to 8.17.2

* Use Umbraco Path instead of constant

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
2022-01-27 17:37:32 +01:00
Bjarke Berg deff17e622 Merge remote-tracking branch 'origin/v8/dev' into v9/dev
# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/ConfigsExtensions.cs
#	src/Umbraco.Core/Constants-AppSettings.cs
#	src/Umbraco.Core/Sync/ApplicationUrlHelper.cs
#	src/Umbraco.Web.UI/web.Template.config
#	src/Umbraco.Web/Editors/AuthenticationController.cs
#	src/Umbraco.Web/Editors/HelpController.cs
#	src/Umbraco.Web/Editors/UsersController.cs
2022-01-27 17:35:47 +01:00
Bjarke Berg 8c822c59ac Merge remote-tracking branch 'origin/v8/8.17' into v8/dev
# Conflicts:
#	src/Umbraco.Core/Sync/ApplicationUrlHelper.cs
#	src/Umbraco.Web/Editors/AuthenticationController.cs
#	src/Umbraco.Web/Editors/UsersController.cs
2022-01-27 16:04:50 +01:00
Bjarke Berg c79380191b Use Umbraco Path instead of constant 2022-01-27 15:57:22 +01:00
Bjarke Berg a779803763 Bump version to 8.17.2 2022-01-27 13:18:21 +01:00
MoleandBjarke Berg 1b98f8985e Use current request for emails (#11775)
* Use current request for emails

* Fix tests
2022-01-27 12:59:45 +01:00
MoleandGitHub 56d54d441f Merge pull request #11907 from umbraco/v8/feature/allowlist-for-help-page
V8: Add allowlist for HelpPage
2022-01-26 13:07:40 +01:00
Mole 9dbe2d211c Add allowlist for HelpPage 2022-01-26 10:57:49 +01:00
LantzifyandSebastiaan Janssen aae8694618 Added description to entityResource.getSafeAlias 2022-01-21 10:56:04 +01:00
Bjarne FyrstenborgandSebastiaan Janssen 6a95ff366f Ensure query is not null before using Trim() 2022-01-21 10:54:29 +01:00
dependabot[bot]andSebastiaan Janssen c3b619fbbf Bump follow-redirects in /src/Umbraco.Web.UI.Client
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.4 to 1.14.7.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.4...v1.14.7)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-21 10:50:35 +01:00
Paul Johnson 366a8ba565 Prevent issues for those who wish use ServiceBasedControllerActivator.
Adds registration for CurrentUserController which has ambiguous constructors.
Register our controllers so that issues are visible during dev/test.
2022-01-18 00:40:39 +00:00
Søren KottalandSebastiaan Janssen 50da4a77de Adds support for simple markdown in property descriptions, and extended property descriptions (#11628)
* Adds support for simple markdown in property descriptions, and extended descriptions

* removes max-width for property descriptions (doesn't make sense to limit these IMO)

(cherry picked from commit 8393fdecfb)
2021-12-14 14:28:02 +01:00
326 changed files with 8043 additions and 3327 deletions
-43
View File
@@ -306,48 +306,6 @@ dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
##########################################
# StyleCop Field Naming Rules
# Naming rules for fields follow the StyleCop analyzers
# This does not override any rules using disallowed_style above
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
##########################################
# All constant fields must be PascalCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
# All static readonly fields must be PascalCase
# Ajusted to ignore private fields.
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
# No non-private instance fields are allowed
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
# Local variables must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent
# This rule should never fire. However, it's included for at least two purposes:
# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
@@ -357,7 +315,6 @@ dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_chec
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
##########################################
# Other Naming Rules
##########################################
+2 -1
View File
@@ -57,6 +57,7 @@ Or alternatively, you can reach out directly to any of the team members behind t
* Sebastiaan Janssen (He, Him - Languages spoken: English, Dutch, Danish(Read)) [sebastiaan@umbraco.com](mailto:sebastiaan@umbraco.com)
* Ilham Boulghallat (She, Her - Languages spoken: English, French, Arabic) [ilham@umbraco.com](mailto:ilham@umbraco.com)
* Arnold Visser (He, Him - Languages spoken: English, Dutch) [arnold@umbraco.com](mailto:arnold@umbraco.com)
* Emma Burstow (She, Her - Languages spoken: English) [ema@umbraco.com](mailto:ema@umbraco.com)
The review process is done with full respect for the privacy and security of the reporter of any incident.
@@ -89,4 +90,4 @@ Consequence: A permanent ban from any sort of public interaction within the comm
## Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
This Code of Conduct will be maintained and reviewed by the team listed above.
This Code of Conduct will be maintained and reviewed by the team listed above.
+82
View File
@@ -0,0 +1,82 @@
is_global = true
##########################################
# StyleCopAnalyzers Settings
##########################################
# All constant fields must be PascalCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
# All static readonly fields must be PascalCase
# Ajusted to ignore private fields.
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
# No non-private instance fields are allowed
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
# Local variables must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
##########################################
# StyleCopAnalyzers rule severity
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
##########################################
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.ReadabilityRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.NamingRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = suggestion
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = suggestion
dotnet_diagnostic.SA1636.severity = none # SA1636: File header copyright text should match
dotnet_diagnostic.SA1101.severity = none # PrefixLocalCallsWithThis - stylecop appears to be ignoring dotnet_style_qualification_for_*
dotnet_diagnostic.SA1503.severity = warning # BracesMustNotBeOmitted
dotnet_diagnostic.SA1117.severity = warning # ParametersMustBeOnSameLineOrSeparateLines
dotnet_diagnostic.SA1116.severity = warning # SplitParametersMustStartOnLineAfterDeclaration
dotnet_diagnostic.SA1122.severity = warning # UseStringEmptyForEmptyStrings
dotnet_diagnostic.SA1028.severity = warning # CodeMustNotContainTrailingWhitespace
dotnet_diagnostic.SA1500.severity = warning # BracesForMultiLineStatementsMustNotShareLine
dotnet_diagnostic.SA1401.severity = warning # FieldsMustBePrivate
dotnet_diagnostic.SA1519.severity = warning # BracesMustNotBeOmittedFromMultiLineChildStatement
dotnet_diagnostic.SA1111.severity = warning # ClosingParenthesisMustBeOnLineOfLastParameter
dotnet_diagnostic.SA1520.severity = warning # UseBracesConsistently
dotnet_diagnostic.SA1407.severity = warning # ArithmeticExpressionsMustDeclarePrecedence
dotnet_diagnostic.SA1400.severity = warning # AccessModifierMustBeDeclared
dotnet_diagnostic.SA1119.severity = warning # StatementMustNotUseUnnecessaryParenthesis
dotnet_diagnostic.SA1649.severity = warning # FileNameMustMatchTypeName
dotnet_diagnostic.SA1121.severity = warning # UseBuiltInTypeAlias
dotnet_diagnostic.SA1132.severity = warning # DoNotCombineFields
dotnet_diagnostic.SA1134.severity = warning # AttributesMustNotShareLine
dotnet_diagnostic.SA1106.severity = warning # CodeMustNotContainEmptyStatements
dotnet_diagnostic.SA1312.severity = warning # VariableNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1303.severity = warning # ConstFieldNamesMustBeginWithUpperCaseLetter
dotnet_diagnostic.SA1310.severity = warning # FieldNamesMustNotContainUnderscore
dotnet_diagnostic.SA1130.severity = warning # UseLambdaSyntax
dotnet_diagnostic.SA1405.severity = warning # DebugAssertMustProvideMessageText
dotnet_diagnostic.SA1205.severity = warning # PartialElementsMustDeclareAccess
dotnet_diagnostic.SA1306.severity = warning # FieldNamesMustBeginWithLowerCaseLetter
dotnet_diagnostic.SA1209.severity = warning # UsingAliasDirectivesMustBePlacedAfterOtherUsingDirectives
dotnet_diagnostic.SA1216.severity = warning # UsingStaticDirectivesMustBePlacedAtTheCorrectLocation
dotnet_diagnostic.SA1133.severity = warning # DoNotCombineAttributes
dotnet_diagnostic.SA1135.severity = warning # UsingDirectivesMustBeQualified
+1 -7
View File
@@ -2,12 +2,6 @@
<Project>
<!-- Package references and additional files which are consumed by all projects -->
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="all" IsImplicitlyDefined="true" />
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.406" PrivateAssets="all" IsImplicitlyDefined="true" />
</ItemGroup>
<!-- Specify rule set for all projects -->
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)codeanalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
+1 -1
View File
@@ -538,7 +538,7 @@ stages:
inputs:
targetType: inline
script: |
choco install docfx --version=2.58.5 -y
choco install docfx --version=2.59.0 -y
if ($lastexitcode -ne 0){
throw ("Error installing DocFX")
}
@@ -24,7 +24,7 @@
"version": {
"type": "parameter",
"datatype": "string",
"defaultValue": "9.3.0",
"defaultValue": "9.4.2",
"description": "The version of Umbraco to load using NuGet",
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
},
+4 -1
View File
@@ -461,6 +461,9 @@ $RECYCLE.BIN/
# Dont commit Umbraco TEMP folder containing Examine Indexes, NuCache etc
**/umbraco/Data/TEMP/
# Umbraco log files
**/umbraco/Logs/
# Dont commit files that are generated and cached from the default ImageSharp location
**/umbraco/mediacache/
@@ -484,4 +487,4 @@ $RECYCLE.BIN/
# Umbraco Static Assets of Backoffice
# Nuget package Umbraco.Cms.StaticAssets will copy them in during dotnet build
**/wwwroot/umbraco/
**/wwwroot/umbraco/
@@ -57,7 +57,7 @@
"version": {
"type": "parameter",
"datatype": "string",
"defaultValue": "9.3.0",
"defaultValue": "9.4.2",
"description": "The version of Umbraco to load using NuGet",
"replaces": "UMBRACO_VERSION_FROM_TEMPLATE"
},
@@ -12,9 +12,13 @@
</ItemGroup>
<!-- Force windows to use ICU. Otherwise Windows 10 2019H1+ will do it, but older windows 10 and most if not all winodws servers will run NLS -->
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
<RuntimeHostConfigurationOption Include="System.Globalization.AppLocalIcu" Value="68.2" />
<ItemGroup>
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
<RuntimeHostConfigurationOption
Condition="$(RuntimeIdentifier.StartsWith('linux')) Or $(RuntimeIdentifier.StartsWith('win')) Or ('$(RuntimeIdentifier)' == '' And !$([MSBuild]::IsOSPlatform('osx')))"
Include="System.Globalization.AppLocalIcu"
Value="68.2.0.9" />
</ItemGroup>
<Import Project="..\PackageTestSiteName\build\PackageTestSiteName.targets" Condition="'$(PackageTestSiteName)' != ''" />
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="CodeAnalysis" ToolsVersion="16.0">
<Rules AnalyzerId="Roslynator.CSharp.Analyzers" RuleNamespace="Roslynator.CSharp">
<Rule Id="RCS1090" Action="None" />
</Rules>
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
<Rule Id="SA1101" Action="None" />
<Rule Id="SA1200" Action="None" />
<Rule Id="SA1308" Action="None" />
<Rule Id="SA1309" Action="None" />
<Rule Id="SA1311" Action="None" />
<Rule Id="SA1402" Action="None" />
<Rule Id="SA1413" Action="None" />
<Rule Id="SA1611" Action="None" />
<Rule Id="SA1615" Action="None" />
<Rule Id="SA1629" Action="None" />
</Rules>
</RuleSet>
@@ -15,8 +15,8 @@
<div class="level{{item.level}}"><span class="xref">{{item.name.0.value}}</span></div>
</div>
{{/inheritance.0}}
<h6><strong>{{__global.namespace}}</strong>:{{namespace}}</h6>
<h6><strong>{{__global.assembly}}</strong>:{{assemblies.0}}.dll</h6>
<h6><strong>{{__global.namespace}}</strong>: {{{namespace.specName.0.value}}}</h6>
<h6><strong>{{__global.assembly}}</strong>: {{assemblies.0}}.dll</h6>
<h5 id="{{id}}_syntax">{{__global.syntax}}</h5>
<div class="codewrapper">
<pre><code class="lang-{{_lang}} hljs">{{syntax.content.0.value}}</code></pre>
+5 -5
View File
@@ -1,12 +1,12 @@
<Project>
<Project>
<!-- Enable multi-level merging -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
<PropertyGroup>
<Version>9.3.0</Version>
<AssemblyVersion>9.3.0</AssemblyVersion>
<InformationalVersion>9.3.0</InformationalVersion>
<FileVersion>9.3.0</FileVersion>
<Version>9.4.2</Version>
<AssemblyVersion>9.4.2</AssemblyVersion>
<InformationalVersion>9.4.2</InformationalVersion>
<FileVersion>9.4.2</FileVersion>
<LangVersion Condition="'$(LangVersion)' == ''">9.0</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<Company>Umbraco CMS</Company>
+3
View File
@@ -3,6 +3,7 @@
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Deploy.Core.Configuration.DebugConfiguration;
using Umbraco.Deploy.Core.Configuration.DeployConfiguration;
using Umbraco.Deploy.Core.Configuration.DeployProjectConfiguration;
using Umbraco.Forms.Core.Configuration;
@@ -127,6 +128,8 @@ namespace JsonSchema
public DeploySettings Settings { get; set; }
public DeployProjectConfig Project { get; set; }
public DebugSettings Debug { get; set; }
}
}
}
+2 -2
View File
@@ -10,8 +10,8 @@
<PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="NJsonSchema" Version="10.5.2" />
<PackageReference Include="System.Xml.XPath.XmlDocument" Version="4.3.0" />
<PackageReference Include="Umbraco.Deploy.Core" Version="9.2.0" />
<PackageReference Include="Umbraco.Forms.Core" Version="9.2.0" />
<PackageReference Include="Umbraco.Deploy.Core" Version="9.3.0" />
<PackageReference Include="Umbraco.Forms.Core" Version="9.3.0" />
</ItemGroup>
<ItemGroup>
@@ -46,6 +46,11 @@ namespace Umbraco.Cms.Core.Cache
{
var payloads = Deserialize(json);
Refresh(payloads);
}
public override void Refresh(JsonPayload[] payloads)
{
foreach (var payload in payloads)
{
foreach (var alias in GetCacheKeysForAlias(payload.Alias))
@@ -55,11 +60,13 @@ namespace Umbraco.Cms.Core.Cache
if (macroRepoCache)
{
macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey<IMacro, int>(payload.Id));
macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey<IMacro, string>(payload.Alias)); // Repository caching of macro definition by alias
}
}
base.Refresh(json);
base.Refresh(payloads);
}
#endregion
#region Json
@@ -156,6 +156,9 @@ namespace Umbraco.Cms.Core.Configuration.Models
internal const bool StaticShowDeprecatedPropertyEditors = false;
internal const string StaticLoginBackgroundImage = "assets/img/login.jpg";
internal const string StaticLoginLogoImage = "assets/img/application/umbraco_logo_white.svg";
internal const bool StaticHideBackOfficeLogo = false;
internal const bool StaticDisableDeleteWhenReferenced = false;
internal const bool StaticDisableUnpublishWhenReferenced = false;
/// <summary>
/// Gets or sets a value for the content notification settings.
@@ -219,6 +222,24 @@ namespace Umbraco.Cms.Core.Configuration.Models
[DefaultValue(StaticLoginLogoImage)]
public string LoginLogoImage { get; set; } = StaticLoginLogoImage;
/// <summary>
/// Gets or sets a value indicating whether to hide the backoffice umbraco logo or not.
/// </summary>
[DefaultValue(StaticHideBackOfficeLogo)]
public bool HideBackOfficeLogo { get; set; } = StaticHideBackOfficeLogo;
/// <summary>
/// Gets or sets a value indicating whether to disable the deletion of items referenced by other items.
/// </summary>
[DefaultValue(StaticDisableDeleteWhenReferenced)]
public bool DisableDeleteWhenReferenced { get; set; } = StaticDisableDeleteWhenReferenced;
/// <summary>
/// Gets or sets a value indicating whether to disable the unpublishing of items referenced by other items.
/// </summary>
[DefaultValue(StaticDisableUnpublishWhenReferenced)]
public bool DisableUnpublishWhenReferenced { get; set; } = StaticDisableUnpublishWhenReferenced;
/// <summary>
/// Get or sets the model representing the global content version cleanup policy
/// </summary>
@@ -29,6 +29,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
internal const string StaticNoNodesViewPath = "~/umbraco/UmbracoWebsite/NoNodes.cshtml";
internal const string StaticSqlWriteLockTimeOut = "00:00:05";
internal const bool StaticSanitizeTinyMce = false;
internal const int StaticMainDomReleaseSignalPollingInterval = 2000;
/// <summary>
/// Gets or sets a value for the reserved URLs (must end with a comma).
@@ -137,6 +138,26 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// </summary>
public string MainDomLock { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value to discriminate MainDom boundaries.
/// <para>
/// Generally the default should suffice but useful for advanced scenarios e.g. azure deployment slot based zero downtime deployments.
/// </para>
/// </summary>
public string MainDomKeyDiscriminator { get; set; } = string.Empty;
/// <summary>
/// Gets or sets the duration (in milliseconds) for which the MainDomLock release signal polling task should sleep.
/// </summary>
/// <remarks>
/// Doesn't apply to MainDomSemaphoreLock.
/// <para>
/// The default value is 2000ms.
/// </para>
/// </remarks>
[DefaultValue(StaticMainDomReleaseSignalPollingInterval)]
public int MainDomReleaseSignalPollingInterval { get; set; } = StaticMainDomReleaseSignalPollingInterval;
/// <summary>
/// Gets or sets the telemetry ID.
/// </summary>
@@ -174,18 +195,18 @@ namespace Umbraco.Cms.Core.Configuration.Models
public bool IsPickupDirectoryLocationConfigured => !string.IsNullOrWhiteSpace(Smtp?.PickupDirectoryLocation);
/// <summary>
/// Gets a value indicating whether TinyMCE scripting sanitization should be applied.
/// Gets or sets a value indicating whether TinyMCE scripting sanitization should be applied.
/// </summary>
[DefaultValue(StaticSanitizeTinyMce)]
public bool SanitizeTinyMce => StaticSanitizeTinyMce;
public bool SanitizeTinyMce { get; set; } = StaticSanitizeTinyMce;
/// <summary>
/// Gets a value representing the time in milliseconds to lock the database for a write operation.
/// An int value representing the time in milliseconds to lock the database for a write operation
/// </summary>
/// <remarks>
/// The default value is 5000 milliseconds.
/// </remarks>
[DefaultValue(StaticSqlWriteLockTimeOut)]
public TimeSpan SqlWriteLockTimeOut { get; } = TimeSpan.Parse(StaticSqlWriteLockTimeOut);
public TimeSpan SqlWriteLockTimeOut { get; set; } = TimeSpan.Parse(StaticSqlWriteLockTimeOut);
}
}
@@ -22,7 +22,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// <summary>
/// Gets or sets a value for the location of temporary files.
/// </summary>
[DefaultValue(StaticLocalTempStorageLocation)]
[DefaultValue(StaticLocalTempStorageLocation)]
public LocalTempStorage LocalTempStorageLocation { get; set; } = Enum<LocalTempStorage>.Parse(StaticLocalTempStorageLocation);
/// <summary>
@@ -31,5 +31,10 @@ namespace Umbraco.Cms.Core.Configuration.Models
/// <value><c>true</c> if [debug mode]; otherwise, <c>false</c>.</value>
[DefaultValue(StaticDebug)]
public bool Debug { get; set; } = StaticDebug;
/// <summary>
/// Gets or sets a value specifying the name of the site.
/// </summary>
public string SiteName { get; set; }
}
}
@@ -49,6 +49,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
if (!ModelsMode.IsAuto())
{
_flagOutOfDateModels = false;
return;
}
_flagOutOfDateModels = value;
@@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
[UmbracoOptions(Constants.Configuration.ConfigRichTextEditor)]
public class RichTextEditorSettings
{
internal const string StaticValidElements = "+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*]";
internal const string StaticValidElements = "+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption";
internal const string StaticInvalidElements = "font";
private static readonly string[] s_default_plugins = new[]
+19
View File
@@ -0,0 +1,19 @@
namespace Umbraco.Cms.Core
{
/// <summary>
/// Defines constants.
/// </summary>
public static partial class Constants
{
/// <summary>
/// Defines constants for named http clients.
/// </summary>
public static class HttpClients
{
/// <summary>
/// Name for http client which ignores certificate errors.
/// </summary>
public const string IgnoreCertificateErrors = "Umbraco:HttpClients:IgnoreCertificateErrors";
}
}
}
+6 -1
View File
@@ -1,4 +1,4 @@
namespace Umbraco.Cms.Core
namespace Umbraco.Cms.Core
{
public static partial class Constants
{
@@ -9,6 +9,11 @@
/// </summary>
public const string DefaultIcon = Content;
/// <summary>
/// System blueprint icon
/// </summary>
public const string Blueprint = "icon-blueprint";
/// <summary>
/// System content icon
/// </summary>
@@ -1,3 +1,5 @@
using System;
namespace Umbraco.Cms.Core
{
public static partial class Constants
@@ -42,7 +44,13 @@ namespace Umbraco.Cms.Core
public const string Install = "~/install";
public const string AppPlugins = "/App_Plugins";
[Obsolete("Use PluginIcons instead")]
public static string AppPluginIcons => "/Backoffice/Icons";
public const string PluginIcons = "/backoffice/icons";
public const string CreatedPackages = "/created-packages";
@@ -0,0 +1,32 @@
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Models.Membership;
namespace Umbraco.Cms.Core.ContentApps
{
internal class DictionaryContentAppFactory : IContentAppFactory
{
private const int Weight = -100;
private ContentApp _dictionaryApp;
public ContentApp GetContentAppFor(object source, IEnumerable<IReadOnlyUserGroup> userGroups)
{
switch (source)
{
case IDictionaryItem _:
return _dictionaryApp ??= new ContentApp
{
Alias = "dictionaryContent",
Name = "Content",
Icon = "icon-document",
View = "views/dictionary/views/content/content.html",
Weight = Weight
};
default:
return null;
}
}
}
}
@@ -46,7 +46,8 @@ namespace Umbraco.Cms.Core.DependencyInjection
.Append<ContentTypeListViewContentAppFactory>()
.Append<ContentTypePermissionsContentAppFactory>()
.Append<ContentTypeTemplatesContentAppFactory>()
.Append<MemberEditorContentAppFactory>();
.Append<MemberEditorContentAppFactory>()
.Append<DictionaryContentAppFactory>();
// all built-in finders in the correct order,
// devs can then modify this list on application startup
@@ -262,6 +262,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddSingleton<IValueEditorCache, ValueEditorCache>();
// Register telemetry service used to gather data about installed packages
Services.AddUnique<ISiteIdentifierService, SiteIdentifierService>();
Services.AddUnique<ITelemetryService, TelemetryService>();
// Register a noop IHtmlSanitizer to be replaced
@@ -33,7 +33,8 @@ namespace Umbraco.Cms.Core.Events
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName,
true,
Constants.ObjectTypes.Document,
Constants.ObjectTypes.Document);
Constants.ObjectTypes.Document,
false);
_relationService.Save(relationType);
}
@@ -132,7 +132,7 @@ namespace Umbraco.Extensions
}
}
verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
return true;
}
@@ -626,7 +626,6 @@ namespace Umbraco.Extensions
if (type == typeof(sbyte)) return XmlConvert.ToString((sbyte)value);
if (type == typeof(short)) return XmlConvert.ToString((short)value);
if (type == typeof(TimeSpan)) return XmlConvert.ToString((TimeSpan)value);
if (type == typeof(bool)) return XmlConvert.ToString((bool)value);
if (type == typeof(uint)) return XmlConvert.ToString((uint)value);
if (type == typeof(ulong)) return XmlConvert.ToString((ulong)value);
if (type == typeof(ushort)) return XmlConvert.ToString((ushort)value);
@@ -1,10 +1,13 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Install.Models;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Web.Common.DependencyInjection;
namespace Umbraco.Cms.Core.Install.InstallSteps
{
@@ -13,31 +16,29 @@ namespace Umbraco.Cms.Core.Install.InstallSteps
PerformsAppRestart = false)]
public class TelemetryIdentifierStep : InstallSetupStep<object>
{
private readonly ILogger<TelemetryIdentifierStep> _logger;
private readonly IOptions<GlobalSettings> _globalSettings;
private readonly IConfigManipulator _configManipulator;
private readonly ISiteIdentifierService _siteIdentifierService;
public TelemetryIdentifierStep(ILogger<TelemetryIdentifierStep> logger, IOptions<GlobalSettings> globalSettings, IConfigManipulator configManipulator)
public TelemetryIdentifierStep(
IOptions<GlobalSettings> globalSettings,
ISiteIdentifierService siteIdentifierService)
{
_logger = logger;
_globalSettings = globalSettings;
_configManipulator = configManipulator;
_siteIdentifierService = siteIdentifierService;
}
[Obsolete("Use constructor that takes GlobalSettings and ISiteIdentifierService")]
public TelemetryIdentifierStep(
ILogger<TelemetryIdentifierStep> logger,
IOptions<GlobalSettings> globalSettings,
IConfigManipulator configManipulator)
: this(globalSettings, StaticServiceProvider.Instance.GetRequiredService<ISiteIdentifierService>())
{
}
public override Task<InstallSetupResult> ExecuteAsync(object model)
{
// Generate GUID
var telemetrySiteIdentifier = Guid.NewGuid();
try
{
_configManipulator.SetGlobalId(telemetrySiteIdentifier.ToString());
}
catch (Exception ex)
{
_logger.LogError(ex, "Couldn't update config files with a telemetry site identifier");
}
_siteIdentifierService.TryCreateSiteIdentifier(out _);
return Task.FromResult<InstallSetupResult>(null);
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
@@ -70,6 +70,10 @@ namespace Umbraco.Cms.Core.Manifest
partA = "contentType";
partB = contentType.Alias;
break;
case IDictionaryItem _:
partA = "dictionary";
partB = "*"; //Not really a different type for dictionary items
break;
default:
return null;
+1 -1
View File
@@ -237,7 +237,7 @@ namespace Umbraco.Cms.Core.Models
public bool IsCulturePublished(string culture)
// just check _publishInfos
// a non-available culture could not become published anyways
=> _publishInfos != null && _publishInfos.ContainsKey(culture);
=> !culture.IsNullOrWhiteSpace() && _publishInfos != null && _publishInfos.ContainsKey(culture);
/// <inheritdoc />
public bool IsCultureEdited(string culture)
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -17,6 +17,7 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
{
Notifications = new List<BackOfficeNotification>();
Translations = new List<DictionaryTranslationDisplay>();
ContentApps = new List<ContentApp>();
}
/// <inheritdoc />
@@ -37,5 +38,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "translations")]
public List<DictionaryTranslationDisplay> Translations { get; private set; }
/// <summary>
/// Apps for the dictionary item
/// </summary>
[DataMember(Name = "apps")]
public List<ContentApp> ContentApps { get; private set; }
}
}
@@ -1,17 +1,34 @@
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models.ContentEditing
{
[DataContract(Name = "historyCleanup", Namespace = "")]
public class HistoryCleanup
public class HistoryCleanup : BeingDirtyBase
{
private bool _preventCleanup;
private int? _keepAllVersionsNewerThanDays;
private int? _keepLatestVersionPerDayForDays;
[DataMember(Name = "preventCleanup")]
public bool PreventCleanup { get; set; }
public bool PreventCleanup
{
get => _preventCleanup;
set => SetPropertyValueAndDetectChanges(value, ref _preventCleanup, nameof(PreventCleanup));
}
[DataMember(Name = "keepAllVersionsNewerThanDays")]
public int? KeepAllVersionsNewerThanDays { get; set; }
public int? KeepAllVersionsNewerThanDays
{
get => _keepAllVersionsNewerThanDays;
set => SetPropertyValueAndDetectChanges(value, ref _keepAllVersionsNewerThanDays, nameof(KeepAllVersionsNewerThanDays));
}
[DataMember(Name = "keepLatestVersionPerDayForDays")]
public int? KeepLatestVersionPerDayForDays { get; set; }
public int? KeepLatestVersionPerDayForDays
{
get => _keepLatestVersionPerDayForDays;
set => SetPropertyValueAndDetectChanges(value, ref _keepLatestVersionPerDayForDays, nameof(KeepLatestVersionPerDayForDays));
}
}
}
@@ -55,5 +55,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "notifications")]
public List<BackOfficeNotification> Notifications { get; private set; }
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember(Name = "isDependency", IsRequired = true)]
public bool IsDependency { get; set; }
}
}
@@ -23,5 +23,11 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
/// </summary>
[DataMember(Name = "childObjectType", IsRequired = false)]
public Guid? ChildObjectType { get; set; }
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember(Name = "isDependency", IsRequired = true)]
public bool IsDependency { get; set; }
}
}
+10 -1
View File
@@ -96,7 +96,13 @@ namespace Umbraco.Cms.Core.Models
}
}
public HistoryCleanup HistoryCleanup { get; set; }
private HistoryCleanup _historyCleanup;
public HistoryCleanup HistoryCleanup
{
get => _historyCleanup;
set => SetPropertyValueAndDetectChanges(value, ref _historyCleanup, nameof(HistoryCleanup));
}
/// <summary>
/// Determines if AllowedTemplates contains templateId
@@ -162,5 +168,8 @@ namespace Umbraco.Cms.Core.Models
/// <inheritdoc />
IContentType IContentType.DeepCloneWithResetIdentities(string newAlias) =>
(IContentType)DeepCloneWithResetIdentities(newAlias);
/// <inheritdoc/>
public override bool IsDirty() => base.IsDirty() || HistoryCleanup.IsDirty();
}
}
+9
View File
@@ -4,6 +4,15 @@ using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models
{
public interface IRelationTypeWithIsDependency : IRelationType
{
/// <summary>
/// Gets or sets a boolean indicating whether the RelationType should be returned in "Used by"-queries.
/// </summary>
[DataMember]
bool IsDependency { get; set; }
}
public interface IRelationType : IEntity, IRememberBeingDirty
{
/// <summary>
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.ContentApps;
@@ -48,6 +48,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
}
public IEnumerable<ContentApp> GetContentApps(IUmbracoEntity source)
{
return GetContentAppsForEntity(source);
}
public IEnumerable<ContentApp> GetContentAppsForEntity(IEntity source)
{
var apps = _contentAppDefinitions.GetContentAppsFor(source).ToArray();
@@ -133,7 +133,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
if (target is IContentTypeWithHistoryCleanup targetWithHistoryCleanup)
{
targetWithHistoryCleanup.HistoryCleanup = source.HistoryCleanup;
MapHistoryCleanup(source, targetWithHistoryCleanup);
}
target.AllowedTemplates = source.AllowedTemplates
@@ -147,6 +147,34 @@ namespace Umbraco.Cms.Core.Models.Mapping
: _fileService.GetTemplate(source.DefaultTemplate));
}
private static void MapHistoryCleanup(DocumentTypeSave source, IContentTypeWithHistoryCleanup target)
{
// If source history cleanup is null we don't have to map all properties
if (source.HistoryCleanup is null)
{
target.HistoryCleanup = null;
return;
}
// We need to reset the dirty properties, because it is otherwise true, just because the json serializer has set properties
target.HistoryCleanup.ResetDirtyProperties(false);
if (target.HistoryCleanup.PreventCleanup != source.HistoryCleanup.PreventCleanup)
{
target.HistoryCleanup.PreventCleanup = source.HistoryCleanup.PreventCleanup;
}
if (target.HistoryCleanup.KeepAllVersionsNewerThanDays != source.HistoryCleanup.KeepAllVersionsNewerThanDays)
{
target.HistoryCleanup.KeepAllVersionsNewerThanDays = source.HistoryCleanup.KeepAllVersionsNewerThanDays;
}
if (target.HistoryCleanup.KeepLatestVersionPerDayForDays !=
source.HistoryCleanup.KeepLatestVersionPerDayForDays)
{
target.HistoryCleanup.KeepLatestVersionPerDayForDays = source.HistoryCleanup.KeepLatestVersionPerDayForDays;
}
}
// no MapAll - take care
private void Map(MediaTypeSave source, IMediaType target, MapperContext context)
{
@@ -196,7 +224,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.AllowCultureVariant = source.VariesByCulture();
target.AllowSegmentVariant = source.VariesBySegment();
target.ContentApps = _commonMapper.GetContentApps(source);
target.ContentApps = _commonMapper.GetContentAppsForEntity(source);
//sync templates
target.AllowedTemplates = context.MapEnumerable<ITemplate, EntityBasic>(source.AllowedTemplates);
@@ -328,7 +356,10 @@ namespace Umbraco.Cms.Core.Models.Mapping
if (source.GroupId > 0)
{
target.PropertyGroupId = new Lazy<int>(() => source.GroupId, false);
if (target.PropertyGroupId?.Value != source.GroupId)
{
target.PropertyGroupId = new Lazy<int>(() => source.GroupId, false);
}
}
target.Alias = source.Alias;
@@ -523,7 +554,15 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.Thumbnail = source.Thumbnail;
target.AllowedAsRoot = source.AllowAsRoot;
target.AllowedContentTypes = source.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i));
bool allowedContentTypesUnchanged = target.AllowedContentTypes.Select(x => x.Id.Value)
.SequenceEqual(source.AllowedContentTypes);
if (allowedContentTypesUnchanged is false)
{
target.AllowedContentTypes = source.AllowedContentTypes.Select((t, i) => new ContentTypeSort(t, i));
}
if (!(target is IMemberType))
{
@@ -574,13 +613,21 @@ namespace Umbraco.Cms.Core.Models.Mapping
// ensure no duplicate alias, then assign the group properties collection
EnsureUniqueAliases(destProperties);
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
if (destGroup.PropertyTypes.SupportsPublishing != isPublishing || destGroup.PropertyTypes.SequenceEqual(destProperties) is false)
{
destGroup.PropertyTypes = new PropertyTypeCollection(isPublishing, destProperties);
}
destGroups.Add(destGroup);
}
// ensure no duplicate name, then assign the groups collection
EnsureUniqueAliases(destGroups);
target.PropertyGroups = new PropertyGroupCollection(destGroups);
if (target.PropertyGroups.SequenceEqual(destGroups) is false)
{
target.PropertyGroups = new PropertyGroupCollection(destGroups);
}
// because the property groups collection was rebuilt, there is no need to remove
// the old groups - they are just gone and will be cleared by the repository
@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models.ContentEditing;
using Umbraco.Cms.Core.Services;
@@ -14,12 +15,20 @@ namespace Umbraco.Cms.Core.Models.Mapping
public class DictionaryMapDefinition : IMapDefinition
{
private readonly ILocalizationService _localizationService;
private readonly CommonMapper _commonMapper;
[Obsolete("Use the constructor with the CommonMapper")]
public DictionaryMapDefinition(ILocalizationService localizationService)
{
_localizationService = localizationService;
}
public DictionaryMapDefinition(ILocalizationService localizationService, CommonMapper commonMapper)
{
_localizationService = localizationService;
_commonMapper = commonMapper;
}
public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<IDictionaryItem, EntityBasic>((source, context) => new EntityBasic(), Map);
@@ -44,6 +53,10 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.Name = source.ItemKey;
target.ParentId = source.ParentId ?? Guid.Empty;
target.Udi = Udi.Create(Constants.UdiEntityType.DictionaryItem, source.Key);
if (_commonMapper != null)
{
target.ContentApps.AddRange(_commonMapper.GetContentAppsForEntity(source));
}
// build up the path to make it possible to set active item in tree
// TODO: check if there is a better way
@@ -30,6 +30,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.ChildObjectType = source.ChildObjectType;
target.Id = source.Id;
target.IsBidirectional = source.IsBidirectional;
if (source is IRelationTypeWithIsDependency sourceWithIsDependency)
{
target.IsDependency = sourceWithIsDependency.IsDependency;
}
target.Key = source.Key;
target.Name = source.Name;
target.Alias = source.Alias;
@@ -74,6 +79,11 @@ namespace Umbraco.Cms.Core.Models.Mapping
target.ChildObjectType = source.ChildObjectType;
target.Id = source.Id.TryConvertTo<int>().Result;
target.IsBidirectional = source.IsBidirectional;
if (target is IRelationTypeWithIsDependency targetWithIsDependency)
{
targetWithIsDependency.IsDependency = source.IsDependency;
}
target.Key = source.Key;
target.Name = source.Name;
target.ParentObjectType = source.ParentObjectType;
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models
{
[DataContract(Name = "relationItem", Namespace = "")]
public class RelationItem
{
[DataMember(Name = "id")]
public int NodeId { get; set; }
[DataMember(Name = "key")]
public Guid NodeKey { get; set; }
[DataMember(Name = "name")]
public string NodeName { get; set; }
[DataMember(Name = "type")]
public string NodeType { get; set; }
[DataMember(Name = "udi")]
public Udi NodeUdi => Udi.Create(NodeType, NodeKey);
[DataMember(Name = "icon")]
public string ContentTypeIcon { get; set; }
[DataMember(Name = "alias")]
public string ContentTypeAlias { get; set; }
[DataMember(Name = "contentTypeName")]
public string ContentTypeName { get; set; }
[DataMember(Name = "relationTypeName")]
public string RelationTypeName { get; set; }
[DataMember(Name = "relationTypeIsBidirectional")]
public bool RelationTypeIsBidirectional { get; set; }
[DataMember(Name = "relationTypeIsDependency")]
public bool RelationTypeIsDependency { get; set; }
}
}
+17 -2
View File
@@ -9,20 +9,28 @@ namespace Umbraco.Cms.Core.Models
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class RelationType : EntityBase, IRelationType
public class RelationType : EntityBase, IRelationType, IRelationTypeWithIsDependency
{
private string _name;
private string _alias;
private bool _isBidirectional;
private bool _isDependency;
private Guid? _parentObjectType;
private Guid? _childObjectType;
public RelationType(string alias, string name)
: this(name: name, alias: alias, false, null, null)
: this(name: name, alias: alias, false, null, null, false)
{
}
[Obsolete("Use ctor with isDependency parameter")]
public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType)
:this(name,alias,isBidrectional, parentObjectType, childObjectType, false)
{
}
public RelationType(string name, string alias, bool isBidrectional, Guid? parentObjectType, Guid? childObjectType, bool isDependency)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(name));
@@ -32,6 +40,7 @@ namespace Umbraco.Cms.Core.Models
_name = name;
_alias = alias;
_isBidirectional = isBidrectional;
_isDependency = isDependency;
_parentObjectType = parentObjectType;
_childObjectType = childObjectType;
}
@@ -88,5 +97,11 @@ namespace Umbraco.Cms.Core.Models
set => SetPropertyValueAndDetectChanges(value, ref _childObjectType, nameof(ChildObjectType));
}
public bool IsDependency
{
get => _isDependency;
set => SetPropertyValueAndDetectChanges(value, ref _isDependency, nameof(IsDependency));
}
}
}
@@ -65,6 +65,11 @@ namespace Umbraco.Cms.Core
/// All languages.
/// </summary>
public const int Languages = -340;
/// <summary>
/// ScheduledPublishing job.
/// </summary>
public const int ScheduledPublishing = -341;
}
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Persistence.Repositories
{
[Obsolete("This interface will be merged with IMacroRepository in Umbraco 11")]
public interface IMacroWithAliasRepository : IMacroRepository
{
IMacro GetByAlias(string alias);
IEnumerable<IMacro> GetAllByAlias(string[] aliases);
}
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Persistence.Repositories
{
public interface ITrackedReferencesRepository
{
/// <summary>
/// Gets a page of items which are in relation with the current item.
/// Basically, shows the items which depend on the current item.
/// </summary>
/// <param name="id">The identifier of the entity to retrieve relations for.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <param name="totalRecords">The total count of the items with reference to the current item.</param>
/// <returns>An enumerable list of <see cref="RelationItem"/> objects.</returns>
IEnumerable<RelationItem> GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords);
/// <summary>
/// Gets a page of items used in any kind of relation from selected integer ids.
/// </summary>
/// <param name="ids">The identifiers of the entities to check for relations.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <param name="totalRecords">The total count of the items in any kind of relation.</param>
/// <returns>An enumerable list of <see cref="RelationItem"/> objects.</returns>
IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords);
/// <summary>
/// Gets a page of the descending items that have any references, given a parent id.
/// </summary>
/// <param name="parentId">The unique identifier of the parent to retrieve descendants for.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <param name="totalRecords">The total count of descending items.</param>
/// <returns>An enumerable list of <see cref="RelationItem"/> objects.</returns>
IEnumerable<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords);
}
}
@@ -6,7 +6,6 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Editors;
@@ -158,17 +157,15 @@ namespace Umbraco.Cms.Core.PropertyEditors
/// <exception cref="System.ArgumentOutOfRangeException">ValueType was out of range.</exception>
internal Attempt<object> TryConvertValueToCrlType(object value)
{
// Ensure empty string values are converted to null
if (value is string s && string.IsNullOrWhiteSpace(s))
// Ensure empty string and JSON values are converted to null
if (value is string stringValue && string.IsNullOrWhiteSpace(stringValue))
{
value = null;
}
// Ensure JSON is serialized properly (without indentation or converted to null when empty)
if (value is not null && ValueType.InvariantEquals(ValueTypes.Json))
else if (value is not string && ValueType.InvariantEquals(ValueTypes.Json))
{
// Only serialize value when it's not already a string
var jsonValue = _jsonSerializer.Serialize(value);
if (jsonValue.DetectIsEmptyJson())
{
value = null;
@@ -1,5 +1,5 @@
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
@@ -28,5 +28,17 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors
DefaultConfiguration.Add("minNumber",0 );
DefaultConfiguration.Add("maxNumber", 0);
}
protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create<MultipleContentPickerParameterEditor.MultipleContentPickerParamateterValueEditor>(Attribute);
internal class MultipleContentPickerParamateterValueEditor : MultiplePickerParamateterValueEditorBase
{
public MultipleContentPickerParamateterValueEditor(ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper, DataEditorAttribute attribute, IEntityService entityService) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute, entityService)
{
}
public override string UdiEntityType { get; } = Constants.UdiEntityType.Document;
public override UmbracoObjectTypes UmbracoObjectType { get; } = UmbracoObjectTypes.Document;
}
}
}
@@ -1,5 +1,9 @@
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Hosting;
using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Editors;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
@@ -26,5 +30,17 @@ namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors
{
DefaultConfiguration.Add("multiPicker", "1");
}
protected override IDataValueEditor CreateValueEditor() => DataValueEditorFactory.Create<MultipleMediaPickerPropertyValueEditor>(Attribute);
internal class MultipleMediaPickerPropertyValueEditor : MultiplePickerParamateterValueEditorBase
{
public MultipleMediaPickerPropertyValueEditor(ILocalizedTextService localizedTextService, IShortStringHelper shortStringHelper, IJsonSerializer jsonSerializer, IIOHelper ioHelper, DataEditorAttribute attribute, IEntityService entityService) : base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute, entityService)
{
}
public override string UdiEntityType { get; } = Constants.UdiEntityType.Media;
public override UmbracoObjectTypes UmbracoObjectType { get; } = UmbracoObjectTypes.Media;
}
}
}
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Editors;
using Umbraco.Cms.Core.Serialization;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
namespace Umbraco.Cms.Core.PropertyEditors.ParameterEditors
{
internal abstract class MultiplePickerParamateterValueEditorBase : DataValueEditor, IDataValueReference
{
private readonly IEntityService _entityService;
public MultiplePickerParamateterValueEditorBase(
ILocalizedTextService localizedTextService,
IShortStringHelper shortStringHelper,
IJsonSerializer jsonSerializer,
IIOHelper ioHelper,
DataEditorAttribute attribute,
IEntityService entityService)
: base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute)
{
_entityService = entityService;
}
public abstract string UdiEntityType { get; }
public abstract UmbracoObjectTypes UmbracoObjectType { get; }
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
{
var asString = value is string str ? str : value?.ToString();
if (string.IsNullOrEmpty(asString))
{
yield break;
}
foreach (var udiStr in asString.Split(','))
{
if (UdiParser.TryParse(udiStr, out Udi udi))
{
yield return new UmbracoEntityReference(udi);
}
// this is needed to support the legacy case when the multiple media picker parameter editor stores ints not udis
if (int.TryParse(udiStr, out var id))
{
Attempt<Guid> guidAttempt = _entityService.GetKey(id, UmbracoObjectType);
Guid guid = guidAttempt.Success ? guidAttempt.Result : Guid.Empty;
if (guid != Guid.Empty)
{
yield return new UmbracoEntityReference(new GuidUdi(Constants.UdiEntityType.Media, guid));
}
}
}
}
}
}
@@ -134,8 +134,13 @@ namespace Umbraco.Cms.Core.Routing
return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture);
}
internal UrlInfo GetUrlFromRoute(string route, IUmbracoContext umbracoContext, int id, Uri current,
UrlMode mode, string culture)
internal UrlInfo GetUrlFromRoute(
string route,
IUmbracoContext umbracoContext,
int id,
Uri current,
UrlMode mode,
string culture)
{
if (string.IsNullOrWhiteSpace(route))
{
@@ -149,12 +154,12 @@ namespace Umbraco.Cms.Core.Routing
// route is /<path> or <domainRootId>/<path>
var pos = route.IndexOf('/');
var path = pos == 0 ? route : route.Substring(pos);
var domainUri = pos == 0
DomainAndUri domainUri = pos == 0
? null
: DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture), current, culture);
var defaultCulture = _localizationService.GetDefaultLanguageIsoCode();
if (domainUri is not null || culture == defaultCulture || string.IsNullOrEmpty(culture))
if (domainUri is not null || string.IsNullOrEmpty(culture) || culture.Equals(defaultCulture, StringComparison.InvariantCultureIgnoreCase))
{
var url = AssembleUrl(domainUri, path, current, mode).ToString();
return UrlInfo.Url(url, culture);
@@ -269,16 +269,10 @@ namespace Umbraco.Extensions
if (!pcr.HasPublishedContent())
{
var logMsg = nameof(DetectCollisionAsync) +
const string logMsg = nameof(DetectCollisionAsync) +
" did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}";
if (pcr.IgnorePublishedContentCollisions)
{
logger.LogDebug(logMsg, url, uri, culture);
}
else
{
logger.LogDebug(logMsg, url, uri, culture);
}
logger.LogDebug(logMsg, url, uri, culture);
var urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture);
return Attempt.Succeed(urlInfo);
@@ -0,0 +1,13 @@
namespace Umbraco.Cms.Core.Runtime
{
/// <summary>
/// Defines a class which can generate a distinct key for a MainDom boundary.
/// </summary>
public interface IMainDomKeyGenerator
{
/// <summary>
/// Returns a key that signifies a MainDom boundary.
/// </summary>
string GenerateKey();
}
}
+2 -2
View File
@@ -87,7 +87,7 @@ namespace Umbraco.Cms.Core.Runtime
if (_isMainDom.HasValue == false)
{
throw new InvalidOperationException("Register called when MainDom has not been acquired");
throw new InvalidOperationException("Register called before IsMainDom has been established");
}
else if (_isMainDom == false)
{
@@ -225,7 +225,7 @@ namespace Umbraco.Cms.Core.Runtime
{
if (!_isMainDom.HasValue)
{
throw new InvalidOperationException("MainDom has not been acquired yet");
throw new InvalidOperationException("IsMainDom has not been established yet");
}
return _isMainDom.Value;
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Security.Cryptography;
using System.Text;
@@ -144,7 +144,7 @@ namespace Umbraco.Cms.Core.Security
var saltBytes = Convert.FromBase64String(salt);
byte[] inArray;
var hashAlgorithm = GetHashAlgorithm(algorithmType);
using var hashAlgorithm = GetHashAlgorithm(algorithmType);
var algorithm = hashAlgorithm as KeyedHashAlgorithm;
if (algorithm != null)
{
@@ -209,11 +209,21 @@ namespace Umbraco.Cms.Core.Security
{
// This is for the v6-v8 hashing algorithm
if (algorithm.InvariantEquals(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName))
{
return true;
}
// Default validation value for old machine keys (switched to HMACSHA256 aspnet 4 https://docs.microsoft.com/en-us/aspnet/whitepapers/aspnet4/breaking-changes)
if (algorithm.InvariantEquals("SHA1"))
{
return true;
}
// This is for the <= v4 hashing algorithm
if (IsLegacySHA1Algorithm(algorithm))
{
return true;
}
return false;
}
@@ -227,7 +237,7 @@ namespace Umbraco.Cms.Core.Security
/// <returns>The encoded password.</returns>
private string HashLegacySHA1Password(string password)
{
var hashAlgorithm = GetLegacySHA1Algorithm(password);
using var hashAlgorithm = GetLegacySHA1Algorithm(password);
var hash = Convert.ToBase64String(hashAlgorithm.ComputeHash(Encoding.Unicode.GetBytes(password)));
return hash;
}
+1 -8
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
@@ -17,13 +17,6 @@ namespace Umbraco.Cms.Core.Services
/// <returns>An <see cref="IMacro"/> object</returns>
IMacro GetByAlias(string alias);
///// <summary>
///// Gets a list all available <see cref="IMacro"/> objects
///// </summary>
///// <param name="aliases">Optional array of aliases to limit the results</param>
///// <returns>An enumerable list of <see cref="IMacro"/> objects</returns>
//IEnumerable<IMacro> GetAll(params string[] aliases);
IEnumerable<IMacro> GetAll();
IEnumerable<IMacro> GetAll(params int[] ids);
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Services
{
[Obsolete("This interface will be merged with IMacroService in Umbraco 11")]
public interface IMacroWithAliasService : IMacroService
{
/// <summary>
/// Gets a list of available <see cref="IMacro"/> objects by alias.
/// </summary>
/// <param name="aliases">Optional array of aliases to limit the results</param>
/// <returns>An enumerable list of <see cref="IMacro"/> objects</returns>
IEnumerable<IMacro> GetAll(params string[] aliases);
}
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
@@ -215,7 +215,7 @@ namespace Umbraco.Cms.Core.Services
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalChildren"></param>
/// <returns></returns>
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes);
/// <summary>
@@ -225,7 +225,7 @@ namespace Umbraco.Cms.Core.Services
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalChildren"></param>
/// <returns></returns>
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/></returns>
IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes);
/// <summary>
@@ -0,0 +1,38 @@
using Umbraco.Cms.Core.Models;
namespace Umbraco.Cms.Core.Services
{
public interface ITrackedReferencesService
{
/// <summary>
/// Gets a paged result of items which are in relation with the current item.
/// Basically, shows the items which depend on the current item.
/// </summary>
/// <param name="id">The identifier of the entity to retrieve relations for.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <returns>A paged result of <see cref="RelationItem"/> objects.</returns>
PagedResult<RelationItem> GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency);
/// <summary>
/// Gets a paged result of the descending items that have any references, given a parent id.
/// </summary>
/// <param name="parentId">The unique identifier of the parent to retrieve descendants for.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <returns>A paged result of <see cref="RelationItem"/> objects.</returns>
PagedResult<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency);
/// <summary>
/// Gets a paged result of items used in any kind of relation from selected integer ids.
/// </summary>
/// <param name="ids">The identifiers of the entities to check for relations.</param>
/// <param name="pageIndex">The page index.</param>
/// <param name="pageSize">The page size.</param>
/// <param name="filterMustBeIsDependency">A boolean indicating whether to filter only the RelationTypes which are dependencies (isDependency field is set to true).</param>
/// <returns>A paged result of <see cref="RelationItem"/> objects.</returns>
PagedResult<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency);
}
}
+6 -9
View File
@@ -1,3 +1,4 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
@@ -5,18 +6,14 @@ namespace Umbraco.Cms.Core
{
public static class StaticApplicationLogging
{
private static ILoggerFactory _loggerFactory;
private static ILoggerFactory s_loggerFactory;
public static void Initialize(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public static void Initialize(ILoggerFactory loggerFactory) => s_loggerFactory = loggerFactory;
public static ILogger<object> Logger => CreateLogger<object>();
public static ILogger<T> CreateLogger<T>()
{
return _loggerFactory?.CreateLogger<T>() ?? NullLoggerFactory.Instance.CreateLogger<T>();
}
public static ILogger<T> CreateLogger<T>() => s_loggerFactory?.CreateLogger<T>() ?? NullLoggerFactory.Instance.CreateLogger<T>();
public static ILogger CreateLogger(Type type) => s_loggerFactory?.CreateLogger(type) ?? NullLogger.Instance;
}
}
@@ -0,0 +1,31 @@
using System;
namespace Umbraco.Cms.Core.Telemetry
{
/// <summary>
/// Used to get and create the site identifier
/// </summary>
public interface ISiteIdentifierService
{
/// <summary>
/// Tries to get the site identifier
/// </summary>
/// <returns>True if success.</returns>
bool TryGetSiteIdentifier(out Guid siteIdentifier);
/// <summary>
/// Creates the site identifier and writes it to config.
/// </summary>
/// <param name="createdGuid">asd.</param>
/// <returns>True if success.</returns>
bool TryCreateSiteIdentifier(out Guid createdGuid);
/// <summary>
/// Tries to get the site identifier or otherwise create it if it doesn't exist.
/// </summary>
/// <param name="siteIdentifier">The out parameter for the existing or create site identifier.</param>
/// <returns>True if success.</returns>
bool TryGetOrCreateSiteIdentifier(out Guid siteIdentifier);
}
}
@@ -0,0 +1,81 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Cms.Core.Telemetry
{
/// <inheritdoc />
internal class SiteIdentifierService : ISiteIdentifierService
{
private GlobalSettings _globalSettings;
private readonly IConfigManipulator _configManipulator;
private readonly ILogger<SiteIdentifierService> _logger;
public SiteIdentifierService(
IOptionsMonitor<GlobalSettings> optionsMonitor,
IConfigManipulator configManipulator,
ILogger<SiteIdentifierService> logger)
{
_globalSettings = optionsMonitor.CurrentValue;
optionsMonitor.OnChange(globalSettings => _globalSettings = globalSettings);
_configManipulator = configManipulator;
_logger = logger;
}
/// <inheritdoc/>
public bool TryGetSiteIdentifier(out Guid siteIdentifier)
{
// Parse telemetry string as a GUID & verify its a GUID and not some random string
// since users may have messed with or decided to empty the app setting or put in something random
if (Guid.TryParse(_globalSettings.Id, out var parsedTelemetryId) is false
|| parsedTelemetryId == Guid.Empty)
{
siteIdentifier = Guid.Empty;
return false;
}
siteIdentifier = parsedTelemetryId;
return true;
}
/// <inheritdoc/>
public bool TryGetOrCreateSiteIdentifier(out Guid siteIdentifier)
{
if (TryGetSiteIdentifier(out Guid existingId))
{
siteIdentifier = existingId;
return true;
}
if (TryCreateSiteIdentifier(out Guid createdId))
{
siteIdentifier = createdId;
return true;
}
siteIdentifier = Guid.Empty;
return false;
}
/// <inheritdoc/>
public bool TryCreateSiteIdentifier(out Guid createdGuid)
{
createdGuid = Guid.NewGuid();
try
{
_configManipulator.SetGlobalId(createdGuid.ToString());
}
catch (Exception ex)
{
_logger.LogError(ex, "Couldn't update config files with a telemetry site identifier");
createdGuid = Guid.Empty;
return false;
}
return true;
}
}
}
+8 -24
View File
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Telemetry.Models;
using Umbraco.Extensions;
@@ -12,27 +10,27 @@ namespace Umbraco.Cms.Core.Telemetry
/// <inheritdoc/>
internal class TelemetryService : ITelemetryService
{
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
private readonly IManifestParser _manifestParser;
private readonly IUmbracoVersion _umbracoVersion;
private readonly ISiteIdentifierService _siteIdentifierService;
/// <summary>
/// Initializes a new instance of the <see cref="TelemetryService"/> class.
/// </summary>
public TelemetryService(
IOptionsMonitor<GlobalSettings> globalSettings,
IManifestParser manifestParser,
IUmbracoVersion umbracoVersion)
IUmbracoVersion umbracoVersion,
ISiteIdentifierService siteIdentifierService)
{
_manifestParser = manifestParser;
_umbracoVersion = umbracoVersion;
_globalSettings = globalSettings;
_siteIdentifierService = siteIdentifierService;
}
/// <inheritdoc/>
public bool TryGetTelemetryReportData(out TelemetryReportData telemetryReportData)
{
if (TryGetTelemetryId(out Guid telemetryId) is false)
if (_siteIdentifierService.TryGetOrCreateSiteIdentifier(out Guid telemetryId) is false)
{
telemetryReportData = null;
return false;
@@ -42,28 +40,14 @@ namespace Umbraco.Cms.Core.Telemetry
{
Id = telemetryId,
Version = _umbracoVersion.SemanticVersion.ToSemanticStringWithoutBuild(),
Packages = GetPackageTelemetry()
Packages = GetPackageTelemetry(),
};
return true;
}
private bool TryGetTelemetryId(out Guid telemetryId)
{
// Parse telemetry string as a GUID & verify its a GUID and not some random string
// since users may have messed with or decided to empty the app setting or put in something random
if (Guid.TryParse(_globalSettings.CurrentValue.Id, out var parsedTelemetryId) is false)
{
telemetryId = Guid.Empty;
return false;
}
telemetryId = parsedTelemetryId;
return true;
}
private IEnumerable<PackageTelemetry> GetPackageTelemetry()
{
List<PackageTelemetry> packages = new ();
List<PackageTelemetry> packages = new();
IEnumerable<PackageManifest> manifests = _manifestParser.GetManifests();
foreach (PackageManifest manifest in manifests)
@@ -76,7 +60,7 @@ namespace Umbraco.Cms.Core.Telemetry
packages.Add(new PackageTelemetry
{
Name = manifest.PackageName,
Version = manifest.Version ?? string.Empty
Version = manifest.Version ?? string.Empty,
});
}
@@ -27,7 +27,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
IOptionsMonitor<LuceneDirectoryIndexOptions> indexOptions)
{
_hostingEnvironment = hostingEnvironment;
_indexOptions = indexOptions.Get(index.Name);
if (indexOptions != null)
{
_indexOptions = indexOptions.Get(index.Name);
}
Index = index;
Logger = logger;
}
@@ -35,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
public LuceneIndex Index { get; }
public ILogger<LuceneIndexDiagnostics> Logger { get; }
public Attempt<string> IsHealthy()
{
@@ -72,12 +76,12 @@ namespace Umbraco.Cms.Infrastructure.Examine
{
d[nameof(LuceneDirectoryIndexOptions.DirectoryFactory)] = _indexOptions.DirectoryFactory.GetType();
}
if (_indexOptions.IndexDeletionPolicy != null)
{
d[nameof(LuceneDirectoryIndexOptions.IndexDeletionPolicy)] = _indexOptions.IndexDeletionPolicy.GetType();
}
}
}
return d;
@@ -2,26 +2,45 @@ using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Cms.Core.Configuration;
using Umbraco.Cms.Web.Common.DependencyInjection;
namespace Umbraco.Cms.Core.Configuration
{
public class JsonConfigManipulator : IConfigManipulator
{
private readonly IConfiguration _configuration;
private readonly ILogger<JsonConfigManipulator> _logger;
private readonly object _locker = new object();
public JsonConfigManipulator(IConfiguration configuration) => _configuration = configuration;
[Obsolete]
public JsonConfigManipulator(IConfiguration configuration)
: this(configuration, StaticServiceProvider.Instance.GetRequiredService<ILogger<JsonConfigManipulator>>())
{ }
public string UmbracoConnectionPath { get; } = $"ConnectionStrings:{ Cms.Core.Constants.System.UmbracoConnectionName}";
public JsonConfigManipulator(
IConfiguration configuration,
ILogger<JsonConfigManipulator> logger)
{
_configuration = configuration;
_logger = logger;
}
public string UmbracoConnectionPath { get; } = $"ConnectionStrings:{Cms.Core.Constants.System.UmbracoConnectionName}";
public void RemoveConnectionString()
{
var provider = GetJsonConfigurationProvider(UmbracoConnectionPath);
var json = GetJson(provider);
if (json is null)
{
_logger.LogWarning("Failed to remove connection string from JSON configuration.");
return;
}
RemoveJsonKey(json, UmbracoConnectionPath);
@@ -33,6 +52,11 @@ namespace Umbraco.Cms.Core.Configuration
var provider = GetJsonConfigurationProvider();
var json = GetJson(provider);
if (json is null)
{
_logger.LogWarning("Failed to save connection string in JSON configuration.");
return;
}
var item = GetConnectionItem(connectionString, providerName);
@@ -47,6 +71,11 @@ namespace Umbraco.Cms.Core.Configuration
var provider = GetJsonConfigurationProvider();
var json = GetJson(provider);
if (json is null)
{
_logger.LogWarning("Failed to save configuration key \"{Key}\" in JSON configuration.", key);
return;
}
JToken token = json;
foreach (var propertyName in key.Split(new[] { ':' }))
@@ -73,6 +102,11 @@ namespace Umbraco.Cms.Core.Configuration
var provider = GetJsonConfigurationProvider();
var json = GetJson(provider);
if (json is null)
{
_logger.LogWarning("Failed to save enabled/disabled state for redirect URL tracking in JSON configuration.");
return;
}
var item = GetDisableRedirectUrlItem(disable);
@@ -86,6 +120,11 @@ namespace Umbraco.Cms.Core.Configuration
var provider = GetJsonConfigurationProvider();
var json = GetJson(provider);
if (json is null)
{
_logger.LogWarning("Failed to save global identifier in JSON configuration.");
return;
}
var item = GetGlobalIdItem(id);
@@ -170,13 +209,20 @@ namespace Umbraco.Cms.Core.Configuration
{
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
using (var sw = new StreamWriter(jsonFilePath, false))
using (var jsonTextWriter = new JsonTextWriter(sw)
try
{
Formatting = Formatting.Indented,
})
using (var sw = new StreamWriter(jsonFilePath, false))
using (var jsonTextWriter = new JsonTextWriter(sw)
{
Formatting = Formatting.Indented,
})
{
json?.WriteTo(jsonTextWriter);
}
}
catch (IOException exception)
{
json?.WriteTo(jsonTextWriter);
_logger.LogWarning(exception, "JSON configuration could not be written: {path}", jsonFilePath);
}
}
}
@@ -186,19 +232,25 @@ namespace Umbraco.Cms.Core.Configuration
{
lock (_locker)
{
if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider)
if (provider.Source.FileProvider is not PhysicalFileProvider physicalFileProvider)
{
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
var serializer = new JsonSerializer();
using (var sr = new StreamReader(jsonFilePath))
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize<JObject>(jsonTextReader);
}
return null;
}
return null;
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
try
{
var serializer = new JsonSerializer();
using var sr = new StreamReader(jsonFilePath);
using var jsonTextReader = new JsonTextReader(sr);
return serializer.Deserialize<JObject>(jsonTextReader);
}
catch (IOException exception)
{
_logger.LogWarning(exception, "JSON configuration could not be read: {path}", jsonFilePath);
return null;
}
}
}
@@ -218,6 +218,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
private static IUmbracoBuilder AddMainDom(this IUmbracoBuilder builder)
{
builder.Services.AddSingleton<IMainDomKeyGenerator, DefaultMainDomKeyGenerator>();
builder.Services.AddSingleton<IMainDomLock>(factory =>
{
var globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>();
@@ -229,15 +230,20 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
var loggerFactory = factory.GetRequiredService<ILoggerFactory>();
var npocoMappers = factory.GetRequiredService<NPocoMapperCollection>();
var mainDomKeyGenerator = factory.GetRequiredService<IMainDomKeyGenerator>();
if (globalSettings.Value.MainDomLock == "FileSystemMainDomLock")
{
return new FileSystemMainDomLock(loggerFactory.CreateLogger<FileSystemMainDomLock>(), mainDomKeyGenerator, hostingEnvironment, factory.GetRequiredService<IOptionsMonitor<GlobalSettings>>());
}
return globalSettings.Value.MainDomLock.Equals("SqlMainDomLock") || isWindows == false
? (IMainDomLock)new SqlMainDomLock(
loggerFactory.CreateLogger<SqlMainDomLock>(),
loggerFactory,
globalSettings,
connectionStrings,
dbCreator,
hostingEnvironment,
mainDomKeyGenerator,
databaseSchemaCreatorFactory,
npocoMappers)
: new MainDomSemaphoreLock(loggerFactory.CreateLogger<MainDomSemaphoreLock>(), hostingEnvironment);
@@ -1,3 +1,4 @@
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -49,7 +50,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
ILogger<PhysicalFileSystem> logger = factory.GetRequiredService<ILogger<PhysicalFileSystem>>();
GlobalSettings globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>().Value;
var rootPath = hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPhysicalRootPath);
var rootPath = Path.IsPathRooted(globalSettings.UmbracoMediaPhysicalRootPath) ? globalSettings.UmbracoMediaPhysicalRootPath : hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPhysicalRootPath);
var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath);
return new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, rootPath, rootUrl);
});
@@ -1,7 +1,10 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Install.InstallSteps;
using Umbraco.Cms.Core.Install.Models;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Infrastructure.Install;
using Umbraco.Cms.Infrastructure.Install.InstallSteps;
using Umbraco.Extensions;
@@ -19,7 +22,12 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddScoped<InstallSetupStep, NewInstallStep>();
builder.Services.AddScoped<InstallSetupStep, UpgradeStep>();
builder.Services.AddScoped<InstallSetupStep, FilePermissionsStep>();
builder.Services.AddScoped<InstallSetupStep, TelemetryIdentifierStep>();
builder.Services.AddScoped<InstallSetupStep, TelemetryIdentifierStep>(provider =>
{
return new TelemetryIdentifierStep(
provider.GetRequiredService<IOptions<GlobalSettings>>(),
provider.GetRequiredService<ISiteIdentifierService>());
});
builder.Services.AddScoped<InstallSetupStep, DatabaseConfigureStep>();
builder.Services.AddScoped<InstallSetupStep, DatabaseInstallStep>();
builder.Services.AddScoped<InstallSetupStep, DatabaseUpgradeStep>();
@@ -1,7 +1,6 @@
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
using Umbraco.Extensions;
@@ -47,6 +46,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddUnique<IPublicAccessRepository, PublicAccessRepository>();
builder.Services.AddUnique<IRedirectUrlRepository, RedirectUrlRepository>();
builder.Services.AddUnique<IRelationRepository, RelationRepository>();
builder.Services.AddUnique<ITrackedReferencesRepository, TrackedReferencesRepository>();
builder.Services.AddUnique<IRelationTypeRepository, RelationTypeRepository>();
builder.Services.AddUnique<IServerRegistrationRepository, ServerRegistrationRepository>();
builder.Services.AddUnique<ITagRepository, TagRepository>();
@@ -18,9 +18,9 @@ using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Implement;
using Umbraco.Cms.Infrastructure.Packaging;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
using Umbraco.Cms.Infrastructure.Services.Implement;
using Umbraco.Cms.Infrastructure.Templates;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.DependencyInjection
@@ -63,6 +63,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddUnique<IServerRegistrationService, ServerRegistrationService>();
builder.Services.AddUnique<IEntityService, EntityService>();
builder.Services.AddUnique<IRelationService, RelationService>();
builder.Services.AddUnique<ITrackedReferencesService, TrackedReferencesService>();
builder.Services.AddUnique<IMacroService, MacroService>();
builder.Services.AddUnique<IMemberTypeService, MemberTypeService>();
builder.Services.AddUnique<IMemberGroupService, MemberGroupService>();
@@ -91,6 +92,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddUnique<ICreatedPackagesRepository, CreatedPackageSchemaRepository>();
builder.Services.AddSingleton<PackageDataInstallation>();
builder.Services.AddUnique<IPackageInstallation, PackageInstallation>();
builder.Services.AddUnique<IHtmlMacroParameterParser, HtmlMacroParameterParser>();
return builder;
}
@@ -123,7 +125,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
var pluginLangFolders = appPlugins.Exists == false
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
: appPlugins.GetDirectories()
.SelectMany(x => x.GetDirectories("Lang", SearchOption.AllDirectories))
// Check for both Lang & lang to support case sensitive file systems.
.SelectMany(x => x.GetDirectories("?ang", SearchOption.AllDirectories).Where(x => x.Name.InvariantEquals("lang")))
.SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly))
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false));
@@ -65,7 +65,7 @@ namespace Umbraco.Cms.Core.Events
var documentObjectType = Constants.ObjectTypes.Document;
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType, false);
_relationService.Save(relationType);
}
@@ -123,7 +123,7 @@ namespace Umbraco.Cms.Core.Events
{
var documentObjectType = Constants.ObjectTypes.Document;
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType, false);
_relationService.Save(relationType);
}
@@ -32,7 +32,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
IContentVersionService service,
IMainDom mainDom,
IServerRoleAccessor serverRoleAccessor)
: base(TimeSpan.FromHours(1), TimeSpan.FromMinutes(3))
: base(logger, TimeSpan.FromHours(1), TimeSpan.FromMinutes(3))
{
_runtimeState = runtimeState;
_logger = logger;
@@ -61,6 +61,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
IProfilingLogger profilingLogger,
ICronTabParser cronTabParser)
: base(
logger,
healthChecksSettings.Value.Notification.Period,
healthChecksSettings.Value.GetNotificationDelay(cronTabParser, DateTime.Now, DefaultDelay))
{
@@ -7,6 +7,7 @@ using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Logging;
@@ -48,7 +49,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
IProfilingLogger profilingLogger,
IServerRoleAccessor serverRegistrar,
IHttpClientFactory httpClientFactory)
: base(TimeSpan.FromMinutes(5), DefaultDelay)
: base(logger, TimeSpan.FromMinutes(5), DefaultDelay)
{
_hostingEnvironment = hostingEnvironment;
_mainDom = mainDom;
@@ -99,7 +100,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
try
{
var request = new HttpRequestMessage(HttpMethod.Get, keepAlivePingUrl);
HttpClient httpClient = _httpClientFactory.CreateClient();
HttpClient httpClient = _httpClientFactory.CreateClient(Constants.HttpClients.IgnoreCertificateErrors);
_ = await httpClient.SendAsync(request);
}
catch (Exception ex)
@@ -48,7 +48,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
IScopeProvider scopeProvider,
ILogger<LogScrubber> logger,
IProfilingLogger profilingLogger)
: base(TimeSpan.FromHours(4), DefaultDelay)
: base(logger, TimeSpan.FromHours(4), DefaultDelay)
{
_mainDom = mainDom;
_serverRegistrar = serverRegistrar;
@@ -5,6 +5,8 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
namespace Umbraco.Cms.Infrastructure.HostedServices
{
@@ -21,25 +23,39 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
/// </summary>
protected static readonly TimeSpan DefaultDelay = TimeSpan.FromMinutes(3);
private readonly TimeSpan _period;
private readonly ILogger _logger;
private TimeSpan _period;
private readonly TimeSpan _delay;
private Timer _timer;
private bool _disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="RecurringHostedServiceBase"/> class.
/// </summary>
/// <param name="period">Timepsan representing how often the task should recur.</param>
/// <param name="delay">Timespan represeting the initial delay after application start-up before the first run of the task occurs.</param>
protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay)
/// <param name="logger">Logger.</param>
/// <param name="period">Timespan representing how often the task should recur.</param>
/// <param name="delay">Timespan representing the initial delay after application start-up before the first run of the task occurs.</param>
protected RecurringHostedServiceBase(ILogger logger, TimeSpan period, TimeSpan delay)
{
_logger = logger;
_period = period;
_delay = delay;
}
// Scheduled for removal in V11
[Obsolete("Please use constructor that takes an ILogger instead")]
protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay)
: this(null, period, delay)
{ }
/// <inheritdoc/>
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(ExecuteAsync, null, (int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds);
using (!ExecutionContext.IsFlowSuppressed() ? (IDisposable)ExecutionContext.SuppressFlow() : null)
{
_timer = new Timer(ExecuteAsync, null, (int)_delay.TotalMilliseconds, (int)_period.TotalMilliseconds);
}
return Task.CompletedTask;
}
@@ -60,6 +76,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
// Hat-tip: https://stackoverflow.com/a/14207615/489433
await PerformExecuteAsync(state);
}
catch (Exception ex)
{
ILogger logger = _logger ?? StaticApplicationLogging.CreateLogger(GetType());
logger.LogError(ex, "Unhandled exception in recurring hosted service.");
}
finally
{
// Resume now that the task is complete - Note we use period in both because we don't want to execute again after the delay.
@@ -73,11 +94,29 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
/// <inheritdoc/>
public Task StopAsync(CancellationToken cancellationToken)
{
_period = Timeout.InfiniteTimeSpan;
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
_timer?.Dispose();
}
_disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose() => _timer?.Dispose();
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
public ReportSiteTask(
ILogger<ReportSiteTask> logger,
ITelemetryService telemetryService)
: base(TimeSpan.FromDays(1), TimeSpan.FromMinutes(1))
: base(logger, TimeSpan.FromDays(1), TimeSpan.FromMinutes(1))
{
_logger = logger;
_telemetryService = telemetryService;
@@ -59,9 +59,6 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
// Send data to LIVE telemetry
s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
// Set a low timeout - no need to use a larger default timeout for this POST request
s_httpClient.Timeout = new TimeSpan(0, 0, 1);
#if DEBUG
// Send data to DEBUG telemetry service
s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
@@ -5,13 +5,15 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Runtime;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Web.Common.DependencyInjection;
namespace Umbraco.Cms.Infrastructure.HostedServices
{
@@ -27,20 +29,16 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
private readonly IMainDom _mainDom;
private readonly IRuntimeState _runtimeState;
private readonly IServerMessenger _serverMessenger;
private readonly IScopeProvider _scopeProvider;
private readonly IServerRoleAccessor _serverRegistrar;
private readonly IUmbracoContextFactory _umbracoContextFactory;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledPublishing"/> class.
/// </summary>
/// <param name="runtimeState">Representation of the state of the Umbraco runtime.</param>
/// <param name="mainDom">Representation of the main application domain.</param>
/// <param name="serverRegistrar">Provider of server registrations to the distributed cache.</param>
/// <param name="contentService">Service for handling content operations.</param>
/// <param name="umbracoContextFactory">Service for creating and managing Umbraco context.</param>
/// <param name="logger">The typed logger.</param>
/// <param name="serverMessenger">Service broadcasting cache notifications to registered servers.</param>
/// <param name="backofficeSecurityFactory">Creates and manages <see cref="IBackOfficeSecurity"/> instances.</param>
// Note: Ignoring the two version notice rule as this class should probably be internal.
// We don't expect anyone downstream to be instantiating a HostedService
[Obsolete("This constructor will be removed in version 10, please use an alternative constructor.")]
public ScheduledPublishing(
IRuntimeState runtimeState,
IMainDom mainDom,
@@ -49,7 +47,31 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
IUmbracoContextFactory umbracoContextFactory,
ILogger<ScheduledPublishing> logger,
IServerMessenger serverMessenger)
: base(TimeSpan.FromMinutes(1), DefaultDelay)
: this(
runtimeState,
mainDom,
serverRegistrar,
contentService,
umbracoContextFactory,
logger,
serverMessenger,
StaticServiceProvider.Instance.GetRequiredService<IScopeProvider>())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledPublishing"/> class.
/// </summary>
public ScheduledPublishing(
IRuntimeState runtimeState,
IMainDom mainDom,
IServerRoleAccessor serverRegistrar,
IContentService contentService,
IUmbracoContextFactory umbracoContextFactory,
ILogger<ScheduledPublishing> logger,
IServerMessenger serverMessenger,
IScopeProvider scopeProvider)
: base(logger, TimeSpan.FromMinutes(1), DefaultDelay)
{
_runtimeState = runtimeState;
_mainDom = mainDom;
@@ -58,6 +80,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
_umbracoContextFactory = umbracoContextFactory;
_logger = logger;
_serverMessenger = serverMessenger;
_scopeProvider = scopeProvider;
}
public override Task PerformExecuteAsync(object state)
@@ -93,8 +116,6 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
try
{
// We don't need an explicit scope here because PerformScheduledPublish creates it's own scope
// so it's safe as it will create it's own ambient scope.
// Ensure we run with an UmbracoContext, because this will run in a background task,
// and developers may be using the UmbracoContext in the event handlers.
@@ -105,6 +126,14 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
// - and we should definitively *not* have to flush it here (should be auto)
using UmbracoContextReference contextReference = _umbracoContextFactory.EnsureUmbracoContext();
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
/* We used to assume that there will never be two instances running concurrently where (IsMainDom && ServerRole == SchedulingPublisher)
* However this is possible during an azure deployment slot swap for the SchedulingPublisher instance when trying to achieve zero downtime deployments.
* If we take a distributed write lock, we are certain that the multiple instances of the job will not run in parallel.
* It's possible that during the swapping process we may run this job more frequently than intended but this is not of great concern and it's
* only until the old SchedulingPublisher shuts down. */
scope.EagerWriteLock(Constants.Locks.ScheduledPublishing);
try
{
// Run
@@ -20,6 +20,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
private readonly IRuntimeState _runtimeState;
private readonly IServerMessenger _messenger;
private readonly ILogger<InstructionProcessTask> _logger;
private bool _disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="InstructionProcessTask"/> class.
@@ -29,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
/// <param name="logger">The typed logger.</param>
/// <param name="globalSettings">The configuration for global settings.</param>
public InstructionProcessTask(IRuntimeState runtimeState, IServerMessenger messenger, ILogger<InstructionProcessTask> logger, IOptions<GlobalSettings> globalSettings)
: base(globalSettings.Value.DatabaseServerMessenger.TimeBetweenSyncOperations, TimeSpan.FromMinutes(1))
: base(logger, globalSettings.Value.DatabaseServerMessenger.TimeBetweenSyncOperations, TimeSpan.FromMinutes(1))
{
_runtimeState = runtimeState;
_messenger = messenger;
@@ -54,5 +55,20 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
return Task.CompletedTask;
}
protected override void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing && _messenger is IDisposable disposable)
{
disposable.Dispose();
}
_disposedValue = true;
}
base.Dispose(disposing);
}
}
}
@@ -2,13 +2,17 @@
// See LICENSE for more details.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Hosting;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Web.Common.DependencyInjection;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
@@ -22,6 +26,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
private readonly IServerRegistrationService _serverRegistrationService;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ILogger<TouchServerTask> _logger;
private readonly IServerRoleAccessor _serverRoleAccessor;
private readonly GlobalSettings _globalSettings;
/// <summary>
@@ -37,14 +42,33 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
IServerRegistrationService serverRegistrationService,
IHostingEnvironment hostingEnvironment,
ILogger<TouchServerTask> logger,
IOptions<GlobalSettings> globalSettings)
: base(globalSettings.Value.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15))
IOptions<GlobalSettings> globalSettings,
IServerRoleAccessor serverRoleAccessor)
: base(logger, globalSettings.Value.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15))
{
_runtimeState = runtimeState;
_serverRegistrationService = serverRegistrationService ?? throw new ArgumentNullException(nameof(serverRegistrationService));
_hostingEnvironment = hostingEnvironment;
_logger = logger;
_globalSettings = globalSettings.Value;
_serverRoleAccessor = serverRoleAccessor;
}
[Obsolete("Use constructor that takes an IServerRoleAccessor")]
public TouchServerTask(
IRuntimeState runtimeState,
IServerRegistrationService serverRegistrationService,
IHostingEnvironment hostingEnvironment,
ILogger<TouchServerTask> logger,
IOptions<GlobalSettings> globalSettings)
: this(
runtimeState,
serverRegistrationService,
hostingEnvironment,
logger,
globalSettings,
StaticServiceProvider.Instance.GetRequiredService<IServerRoleAccessor>())
{
}
public override Task PerformExecuteAsync(object state)
@@ -54,6 +78,14 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
return Task.CompletedTask;
}
// If the IServerRoleAccessor has been changed away from ElectedServerRoleAccessor this task no longer makes sense,
// since all it's used for is to allow the ElectedServerRoleAccessor
// to figure out what role a given server has, so we just stop this task.
if (_serverRoleAccessor is not ElectedServerRoleAccessor)
{
return StopAsync(CancellationToken.None);
}
var serverAddress = _hostingEnvironment.ApplicationMainUrl?.ToString();
if (serverAddress.IsNullOrWhiteSpace())
{
@@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
/// <param name="mainDom">Representation of the main application domain.</param>
/// <param name="logger">The typed logger.</param>
public TempFileCleanup(IIOHelper ioHelper, IMainDom mainDom, ILogger<TempFileCleanup> logger)
: base(TimeSpan.FromMinutes(60), DefaultDelay)
: base(logger, TimeSpan.FromMinutes(60), DefaultDelay)
{
_ioHelper = ioHelper;
_mainDom = mainDom;
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Xml.XPath;
using Examine.Search;
@@ -8,31 +8,46 @@ using Umbraco.Cms.Core.Xml;
namespace Umbraco.Cms.Core
{
/// <summary>
/// Query methods used for accessing strongly typed content in templates
/// Query methods used for accessing strongly typed content in templates.
/// </summary>
public interface IPublishedContentQuery
{
IPublishedContent Content(int id);
IPublishedContent Content(Guid id);
IPublishedContent Content(Udi id);
IPublishedContent Content(object id);
IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars);
IEnumerable<IPublishedContent> Content(IEnumerable<int> ids);
IEnumerable<IPublishedContent> Content(IEnumerable<Guid> ids);
IEnumerable<IPublishedContent> Content(IEnumerable<object> ids);
IEnumerable<IPublishedContent> ContentAtXPath(string xpath, params XPathVariable[] vars);
IEnumerable<IPublishedContent> ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars);
IEnumerable<IPublishedContent> ContentAtRoot();
IPublishedContent Media(int id);
IPublishedContent Media(Guid id);
IPublishedContent Media(Udi id);
IPublishedContent Media(object id);
IEnumerable<IPublishedContent> Media(IEnumerable<int> ids);
IEnumerable<IPublishedContent> Media(IEnumerable<object> ids);
IEnumerable<IPublishedContent> Media(IEnumerable<Guid> ids);
IEnumerable<IPublishedContent> MediaAtRoot();
/// <summary>
@@ -44,7 +59,7 @@ namespace Umbraco.Cms.Core
/// <param name="totalRecords">The total amount of records.</param>
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
/// <param name="loadedFields">The fields to load in the results of the search (defaults to all fields loaded).</param>
/// <param name="loadedFields">This parameter is no longer used, because the results are loaded from the published snapshot using the single item ID field.</param>
/// <returns>
/// The search results.
/// </returns>
@@ -175,6 +175,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.Domains, Name = "Domains" });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.KeyValues, Name = "KeyValues" });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.Languages, Name = "Languages" });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.ScheduledPublishing, Name = "ScheduledPublishing" });
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.MainDom, Name = "MainDom" });
}
@@ -420,21 +421,21 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
private void CreateRelationTypeData()
{
var relationType = new RelationTypeDto { Id = 1, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = true, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName };
var relationType = new RelationTypeDto { Id = 1, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = true, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName, IsDependency = false};
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
relationType = new RelationTypeDto { Id = 2, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName };
relationType = new RelationTypeDto { Id = 2, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Document, ParentObjectType = Cms.Core.Constants.ObjectTypes.Document, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName, IsDependency = false };
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
relationType = new RelationTypeDto { Id = 3, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Media, ParentObjectType = Cms.Core.Constants.ObjectTypes.Media, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName };
relationType = new RelationTypeDto { Id = 3, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, ChildObjectType = Cms.Core.Constants.ObjectTypes.Media, ParentObjectType = Cms.Core.Constants.ObjectTypes.Media, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName, IsDependency = false };
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
relationType = new RelationTypeDto { Id = 4, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaName };
relationType = new RelationTypeDto { Id = 4, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedMediaName, IsDependency = true };
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
relationType = new RelationTypeDto { Id = 5, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentName };
relationType = new RelationTypeDto { Id = 5, Alias = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = null, ParentObjectType = null, Dual = false, Name = Cms.Core.Constants.Conventions.RelationTypes.RelatedDocumentName, IsDependency = true };
relationType.UniqueId = CreateUniqueRelationTypeId(relationType.Alias, relationType.Name);
_database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType);
}
@@ -16,6 +16,7 @@ using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_1_0;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_2_0;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_3_0;
using Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
@@ -242,6 +243,9 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
// to 8.17.0
To<AddPropertyTypeGroupColumns>("{153865E9-7332-4C2A-9F9D-F20AEE078EC7}");
// Hack to support migration from 8.18
To<NoopMigration>("{03482BB0-CF13-475C-845E-ECB8319DBE3C}");
// This should be safe to execute again. We need it with a new name to ensure updates from all the following has executed this step.
// - 8.15.0 RC - Current state: {4695D0C9-0729-4976-985B-048D503665D8}
// - 8.15.0 Final - Current state: {5C424554-A32D-4852-8ED1-A13508187901}
@@ -277,6 +281,9 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
To<UpdateExternalLoginToUseKeyInsteadOfId>("{CA7A1D9D-C9D4-4914-BC0A-459E7B9C3C8C}");
To<AddTwoFactorLoginTable>("{0828F206-DCF7-4F73-ABBB-6792275532EB}");
// TO 9.4.0
To<AddScheduledPublishingLock>("{DBBA1EA0-25A1-4863-90FB-5D306FB6F1E1}");
To<UpdateRelationTypesToHandleDependencies>("{DED98755-4059-41BB-ADBD-3FEAB12D1D7B}");
}
}
}
@@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
@@ -20,14 +24,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
{
// Before adding these indexes we need to remove duplicate data.
// Get all logins by latest
var logins = Database.Fetch<ExternalLoginDto>()
var logins = Database.Fetch<ExternalLoginTokenTable.LegacyExternalLoginDto>()
.OrderByDescending(x => x.CreateDate)
.ToList();
var toDelete = new List<int>();
// used to track duplicates so they can be removed
var keys = new HashSet<(string, string)>();
foreach(ExternalLoginDto login in logins)
foreach(ExternalLoginTokenTable.LegacyExternalLoginDto login in logins)
{
if (!keys.Add((login.ProviderKey, login.LoginProvider)))
{
@@ -37,16 +41,16 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
}
if (toDelete.Count > 0)
{
Database.DeleteMany<ExternalLoginDto>().Where(x => toDelete.Contains(x.Id)).Execute();
}
Database.DeleteMany<ExternalLoginTokenTable.LegacyExternalLoginDto>().Where(x => toDelete.Contains(x.Id)).Execute();
}
var indexName1 = "IX_" + ExternalLoginDto.TableName + "_LoginProvider";
var indexName1 = "IX_" + ExternalLoginTokenTable.LegacyExternalLoginDto.TableName + "_LoginProvider";
if (!IndexExists(indexName1))
{
Create
.Index(indexName1)
.OnTable(ExternalLoginDto.TableName)
.OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName)
.OnColumn("loginProvider")
.Ascending()
.WithOptions()
@@ -56,13 +60,13 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
.Do();
}
var indexName2 = "IX_" + ExternalLoginDto.TableName + "_ProviderKey";
var indexName2 = "IX_" + ExternalLoginTokenTable.LegacyExternalLoginDto.TableName + "_ProviderKey";
if (!IndexExists(indexName2))
{
Create
.Index(indexName2)
.OnTable(ExternalLoginDto.TableName)
.OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName)
.OnColumn("loginProvider").Ascending()
.OnColumn("providerKey").Ascending()
.WithOptions()
@@ -70,5 +74,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
.Do();
}
}
}
}
@@ -14,29 +14,29 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
protected override void Migrate()
{
var indexName1 = "IX_" + ExternalLoginDto.TableName + "_LoginProvider";
var indexName2 = "IX_" + ExternalLoginDto.TableName + "_ProviderKey";
var indexName1 = "IX_" + ExternalLoginTokenTable.LegacyExternalLoginDto.TableName + "_LoginProvider";
var indexName2 = "IX_" + ExternalLoginTokenTable.LegacyExternalLoginDto.TableName + "_ProviderKey";
if (IndexExists(indexName1))
{
// drop it since the previous migration index was wrong, and we
// need to modify a column that belons to it
Delete.Index(indexName1).OnTable(ExternalLoginDto.TableName).Do();
Delete.Index(indexName1).OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName).Do();
}
if (IndexExists(indexName2))
{
// drop since it's using a column we're about to modify
Delete.Index(indexName2).OnTable(ExternalLoginDto.TableName).Do();
Delete.Index(indexName2).OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName).Do();
}
// then fixup the length of the loginProvider column
AlterColumn<ExternalLoginDto>(ExternalLoginDto.TableName, "loginProvider");
AlterColumn<ExternalLoginTokenTable.LegacyExternalLoginDto>(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName, "loginProvider");
// create it with the correct definition
Create
.Index(indexName1)
.OnTable(ExternalLoginDto.TableName)
.OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName)
.OnColumn("loginProvider").Ascending()
.OnColumn("userId").Ascending()
.WithOptions()
@@ -48,9 +48,9 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
// re-create the original
Create
.Index(indexName2)
.OnTable(ExternalLoginDto.TableName)
.OnTable(ExternalLoginTokenTable.LegacyExternalLoginDto.TableName)
.OnColumn("loginProvider").Ascending()
.OnColumn("providerKey").Ascending()
.OnColumn("providerKey").Ascending()
.WithOptions()
.NonClustered()
.Do();
@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using NPoco;
using Umbraco.Cms.Core;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
{
public class ExternalLoginTokenTable : MigrationBase
{
public ExternalLoginTokenTable(IMigrationContext context)
@@ -13,7 +17,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
}
/// <summary>
/// Adds new External Login token table
/// Adds new External Login token table
/// </summary>
protected override void Migrate()
{
@@ -25,5 +29,53 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
Create.Table<ExternalLoginTokenDto>().Do();
}
[TableName(TableName)]
[ExplicitColumns]
[PrimaryKey("Id")]
internal class LegacyExternalLoginDto
{
public const string TableName = Constants.DatabaseSchema.Tables.ExternalLogin;
[Column("id")] [PrimaryKeyColumn] public int Id { get; set; }
[Obsolete(
"This only exists to ensure you can upgrade using external logins from umbraco version where this was used to the new where it is not used")]
[Column("userId")]
public int? UserId { get; set; }
/// <summary>
/// Used to store the name of the provider (i.e. Facebook, Google)
/// </summary>
[Column("loginProvider")]
[Length(400)]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.UniqueNonClustered, ForColumns = "loginProvider,userOrMemberKey",
Name = "IX_" + TableName + "_LoginProvider")]
public string LoginProvider { get; set; }
/// <summary>
/// Stores the key the provider uses to lookup the login
/// </summary>
[Column("providerKey")]
[Length(4000)]
[NullSetting(NullSetting = NullSettings.NotNull)]
[Index(IndexTypes.NonClustered, ForColumns = "loginProvider,providerKey",
Name = "IX_" + TableName + "_ProviderKey")]
public string ProviderKey { get; set; }
[Column("createDate")]
[Constraint(Default = SystemMethods.CurrentDateTime)]
public DateTime CreateDate { get; set; }
/// <summary>
/// Used to store any arbitrary data for the user and external provider - like user tokens returned from the provider
/// </summary>
[Column("userData")]
[NullSetting(NullSetting = NullSettings.Null)]
[SpecialDbType(SpecialDbTypes.NTEXT)]
public string UserData { get; set; }
}
}
}
@@ -0,0 +1,15 @@
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0
{
internal class AddScheduledPublishingLock : MigrationBase
{
public AddScheduledPublishingLock(IMigrationContext context)
: base(context)
{
}
protected override void Migrate() =>
Database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Cms.Core.Constants.Locks.ScheduledPublishing, Name = "ScheduledPublishing" });
}
}
@@ -0,0 +1,34 @@
using System.Linq;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_4_0
{
internal class UpdateRelationTypesToHandleDependencies : MigrationBase
{
public UpdateRelationTypesToHandleDependencies(IMigrationContext context)
: base(context)
{
}
protected override void Migrate()
{
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
AddColumnIfNotExists<RelationTypeDto>(columns, "isDependency");
var aliasesWithDependencies = new[]
{
Core.Constants.Conventions.RelationTypes.RelatedDocumentAlias,
Core.Constants.Conventions.RelationTypes.RelatedMediaAlias
};
Database.Execute(
Sql()
.Update<RelationTypeDto>(u => u.Set(x => x.IsDependency, true))
.WhereIn<RelationTypeDto>(x => x.Alias, aliasesWithDependencies));
}
}
}
@@ -575,21 +575,22 @@ namespace Umbraco.Cms.Infrastructure.Packaging
{
var importedFolders = new Dictionary<string, int>();
var trackEntityContainersInstalled = new List<EntityContainer>();
foreach (var documentType in unsortedDocumentTypes)
foreach (XElement documentType in unsortedDocumentTypes)
{
var foldersAttribute = documentType.Attribute("Folders");
var infoElement = documentType.Element("Info");
XAttribute foldersAttribute = documentType.Attribute("Folders");
XElement infoElement = documentType.Element("Info");
if (foldersAttribute != null && infoElement != null
//don't import any folder if this is a child doc type - the parent doc type will need to
//exist which contains it's folders
// don't import any folder if this is a child doc type - the parent doc type will need to
// exist which contains it's folders
&& ((string)infoElement.Element("Master")).IsNullOrWhiteSpace())
{
var alias = documentType.Element("Info").Element("Alias").Value;
var folders = foldersAttribute.Value.Split(Constants.CharArrays.ForwardSlash);
var folderKeysAttribute = documentType.Attribute("FolderKeys");
XAttribute folderKeysAttribute = documentType.Attribute("FolderKeys");
var folderKeys = Array.Empty<Guid>();
Guid[] folderKeys = Array.Empty<Guid>();
if (folderKeysAttribute != null)
{
folderKeys = folderKeysAttribute.Value.Split(Constants.CharArrays.ForwardSlash).Select(x=>Guid.Parse(x)).ToArray();
@@ -597,22 +598,22 @@ namespace Umbraco.Cms.Infrastructure.Packaging
var rootFolder = WebUtility.UrlDecode(folders[0]);
EntityContainer current;
EntityContainer current = null;
Guid? rootFolderKey = null;
if (folderKeys.Length == folders.Length && folderKeys.Length > 0)
{
rootFolderKey = folderKeys[0];
current = _contentTypeService.GetContainer(rootFolderKey.Value);
}
else
{
//level 1 = root level folders, there can only be one with the same name
current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault();
}
// The folder might already exist, but with a different key, so check if it exists, even if there is a key.
// Level 1 = root level folders, there can only be one with the same name
current ??= _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault();
if (current == null)
{
var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolderKey ?? Guid.NewGuid(), rootFolder);
Attempt<OperationResult<OperationResultType, EntityContainer>> tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolderKey ?? Guid.NewGuid(), rootFolder);
if (tryCreateFolder == false)
{
_logger.LogError(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder);
@@ -644,7 +645,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging
private EntityContainer CreateContentTypeChildFolder(string folderName, Guid folderKey, IUmbracoEntity current)
{
var children = _entityService.GetChildren(current.Id).ToArray();
var found = children.Any(x => x.Name.InvariantEquals(folderName) ||x.Key.Equals(folderKey));
var found = children.Any(x => x.Name.InvariantEquals(folderName) || x.Key.Equals(folderKey));
if (found)
{
var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id;
@@ -40,5 +40,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos
[Length(100)]
[Index(IndexTypes.UniqueNonClustered, Name = "IX_umbracoRelationType_alias")]
public string Alias { get; set; }
[Constraint(Default = "0")]
[Column("isDependency")]
public bool IsDependency { get; set; }
}
}
@@ -9,7 +9,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Factories
public static IRelationType BuildEntity(RelationTypeDto dto)
{
var entity = new RelationType(dto.Name, dto.Alias, dto.Dual, dto.ParentObjectType, dto.ChildObjectType);
var entity = new RelationType(dto.Name, dto.Alias, dto.Dual, dto.ParentObjectType, dto.ChildObjectType, dto.IsDependency);
try
{
@@ -30,11 +30,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Factories
public static RelationTypeDto BuildDto(IRelationType entity)
{
var isDependency = false;
if (entity is IRelationTypeWithIsDependency relationTypeWithIsDependency)
{
isDependency = relationTypeWithIsDependency.IsDependency;
}
var dto = new RelationTypeDto
{
Alias = entity.Alias,
ChildObjectType = entity.ChildObjectType,
Dual = entity.IsBidirectional,
IsDependency = isDependency,
Name = entity.Name,
ParentObjectType = entity.ParentObjectType,
UniqueId = entity.Key
@@ -47,6 +53,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Factories
return dto;
}
#endregion
}
}
@@ -22,6 +22,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Mappers
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.Alias), nameof(RelationTypeDto.Alias));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.ChildObjectType), nameof(RelationTypeDto.ChildObjectType));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.IsBidirectional), nameof(RelationTypeDto.Dual));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.IsDependency), nameof(RelationTypeDto.IsDependency));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.Name), nameof(RelationTypeDto.Name));
DefineMap<RelationType, RelationTypeDto>(nameof(RelationType.ParentObjectType), nameof(RelationTypeDto.ParentObjectType));
}
@@ -72,7 +72,27 @@ namespace Umbraco.Extensions
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> WhereIn<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> field, Sql<ISqlContext> values)
{
return sql.WhereIn(field, values, false);
return WhereIn(sql, field, values, false, null);
}
public static Sql<ISqlContext> WhereIn<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> field, Sql<ISqlContext> values, string tableAlias)
{
return sql.WhereIn(field, values, false, tableAlias);
}
public static Sql<ISqlContext> WhereLike<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> fieldSelector, Sql<ISqlContext> valuesSql)
{
var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector);
sql.Where(fieldName + " LIKE (" + valuesSql.SQL + ")", valuesSql.Arguments);
return sql;
}
public static Sql<ISqlContext> WhereLike<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> fieldSelector, string likeValue)
{
var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector);
sql.Where(fieldName + " LIKE ('" + likeValue + "')");
return sql;
}
/// <summary>
@@ -130,7 +150,12 @@ namespace Umbraco.Extensions
private static Sql<ISqlContext> WhereIn<T>(this Sql<ISqlContext> sql, Expression<Func<T, object>> fieldSelector, Sql valuesSql, bool not)
{
var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector);
return WhereIn(sql, fieldSelector, valuesSql, not, null);
}
private static Sql<ISqlContext> WhereIn<T>(this Sql<ISqlContext> sql, Expression<Func<T, object>> fieldSelector, Sql valuesSql, bool not, string tableAlias)
{
var fieldName = sql.SqlContext.SqlSyntax.GetFieldName(fieldSelector, tableAlias);
sql.Where(fieldName + (not ? " NOT" : "") +" IN (" + valuesSql.SQL + ")", valuesSql.Arguments);
return sql;
}
@@ -252,7 +277,7 @@ namespace Umbraco.Extensions
/// <returns>The Sql statement.</returns>
public static Sql<ISqlContext> OrderByDescending<TDto>(this Sql<ISqlContext> sql, Expression<Func<TDto, object>> field)
{
return sql.OrderBy("(" + sql.SqlContext.SqlSyntax.GetFieldName(field) + ") DESC");
return sql.OrderByDescending(sql.SqlContext.SqlSyntax.GetFieldName(field));
}
/// <summary>
@@ -268,7 +293,7 @@ namespace Umbraco.Extensions
var columns = fields.Length == 0
? sql.GetColumns<TDto>(withAlias: false)
: fields.Select(x => sqlSyntax.GetFieldName(x)).ToArray();
return sql.OrderBy(columns.Select(x => x + " DESC"));
return sql.OrderByDescending(columns);
}
/// <summary>
@@ -634,6 +659,12 @@ namespace Umbraco.Extensions
return sql;
}
public static Sql<ISqlContext> SelectDistinct(this Sql<ISqlContext> sql, params object[] columns)
{
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));
return sql;
}
//this.Append("SELECT " + string.Join(", ", columns), new object[0]);
/// <summary>
@@ -468,7 +468,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Querying
case nameof(StringExtensions.InvariantStartsWith):
case nameof(StringExtensions.InvariantEndsWith):
case nameof(StringExtensions.InvariantContains):
case nameof(StringExtensions.InvariantEquals):
case nameof(StringExtensions.InvariantEquals):
string compareValue;
@@ -629,7 +629,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Querying
if (m.Arguments.Count == 2)
{
var n1 = Visit(m.Arguments[0]);
var f = m.Arguments[2];
var f = m.Arguments[1];
if (!(f is Expression<Func<string, string>> fl))
throw new NotSupportedException("Expression is not a proper lambda.");
var ff = fl.Compile();
@@ -18,14 +18,16 @@ using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
{
internal class MacroRepository : EntityRepositoryBase<int, IMacro>, IMacroRepository
internal class MacroRepository : EntityRepositoryBase<int, IMacro>, IMacroWithAliasRepository
{
private readonly IShortStringHelper _shortStringHelper;
private readonly IRepositoryCachePolicy<IMacro, string> _macroByAliasCachePolicy;
public MacroRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<MacroRepository> logger, IShortStringHelper shortStringHelper)
: base(scopeAccessor, cache, logger)
{
_shortStringHelper = shortStringHelper;
_macroByAliasCachePolicy = new DefaultRepositoryCachePolicy<IMacro, string>(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
}
protected override IMacro PerformGet(int id)
@@ -68,6 +70,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
return Get(id) != null;
}
public IMacro GetByAlias(string alias)
{
return _macroByAliasCachePolicy.Get(alias, PerformGetByAlias, PerformGetAllByAlias);
}
public IEnumerable<IMacro> GetAllByAlias(string[] aliases)
{
if (aliases.Any() is false)
{
return base.GetMany();
}
return _macroByAliasCachePolicy.GetAll(aliases, PerformGetAllByAlias);
}
private IMacro PerformGetByAlias(string alias)
{
var query = Query<IMacro>().Where(x => x.Alias.Equals(alias));
return PerformGetByQuery(query).FirstOrDefault();
}
private IEnumerable<IMacro> PerformGetAllByAlias(params string[] aliases)
{
if (aliases.Any() is false)
{
return base.GetMany();
}
var query = Query<IMacro>().Where(x => aliases.Contains(x.Alias));
return PerformGetByQuery(query);
}
protected override IEnumerable<IMacro> PerformGetAll(params int[] ids)
{
return ids.Length > 0 ? ids.Select(Get) : GetAllNoIds();
@@ -60,14 +60,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
var urlHash = url.GenerateHash<SHA1>();
Sql<ISqlContext> sql = GetBaseQuery(false)
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash &&
(x.Culture == culture.ToLower() || x.Culture == string.Empty))
(x.Culture == culture.ToLower() || x.Culture == null || x.Culture == string.Empty))
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc);
List<RedirectUrlDto> dtos = Database.Fetch<RedirectUrlDto>(sql);
RedirectUrlDto dto = dtos.FirstOrDefault(f => f.Culture == culture.ToLower());
if (dto == null)
{
dto = dtos.FirstOrDefault(f => f.Culture == string.Empty);
dto = dtos.FirstOrDefault(f => string.IsNullOrWhiteSpace(f.Culture));
}
return dto == null ? null : Map(dto);
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core;
@@ -171,6 +172,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
}
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
{
return GetPagedParentEntitiesByChildId(childId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes);
}
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int childId, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
{
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
@@ -184,10 +190,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.Where<RelationDto>(rel => rel.ChildId == childId);
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ParentId == childId || node.NodeId != childId);
if (relationTypes != null && relationTypes.Any())
{
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
}
});
}
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
{
return GetPagedChildEntitiesByParentId(parentId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes);
}
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
{
// var contentObjectTypes = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media, Constants.ObjectTypes.Member }
// we could pass in the contentObjectTypes so that the entity repository sql is configured to do full entity lookups so that we get the full data
@@ -201,6 +217,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.Where<RelationDto>(rel => rel.ParentId == parentId);
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ChildId == parentId || node.NodeId != parentId);
if (relationTypes != null && relationTypes.Any())
{
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
}
});
}
@@ -399,4 +420,40 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
sql.OrderByDescending(orderBy);
}
}
internal class RelationItemDto
{
[Column(Name = "nodeId")]
public int ChildNodeId { get; set; }
[Column(Name = "nodeKey")]
public Guid ChildNodeKey { get; set; }
[Column(Name = "nodeName")]
public string ChildNodeName { get; set; }
[Column(Name = "nodeObjectType")]
public Guid ChildNodeObjectType { get; set; }
[Column(Name = "contentTypeIcon")]
public string ChildContentTypeIcon { get; set; }
[Column(Name = "contentTypeAlias")]
public string ChildContentTypeAlias { get; set; }
[Column(Name = "contentTypeName")]
public string ChildContentTypeName { get; set; }
[Column(Name = "relationTypeName")]
public string RelationTypeName { get; set; }
[Column(Name = "relationTypeAlias")]
public string RelationTypeAlias { get; set; }
[Column(Name = "relationTypeIsDependency")]
public bool RelationTypeIsDependency { get; set; }
[Column(Name = "relationTypeIsBidirectional")]
public bool RelationTypeIsBidirectional { get; set; }
}
}

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