Compare commits

...

115 Commits

Author SHA1 Message Date
Sebastiaan Janssen add2f57397 Bump version 2015-08-21 13:30:29 +02:00
Shannon cd3c17d73f Obsoletes AllowAll on MemberAuthorizeAttribute 2015-08-21 12:15:45 +02:00
Shannon fe6d783222 auths member controllers 2015-08-21 12:11:22 +02:00
Shannon fc8b0f1822 Fixes: U4-6934 v7.3.0-beta3 ListView sorting by updateDate throws exception 2015-08-21 12:02:51 +02:00
Shannon 6102c970c8 ensures single tab displays show tab 2015-08-20 19:10:03 +02:00
Shannon 29903058a9 Fixes: U4-6937 Update all web.configs in all subfolders to use MVC4 / Razor 3, U4-6938 While upgrading, delete dlls that shouldn't be there after upgrade 2015-08-20 18:41:39 +02:00
Shannon b3940dea1b Fixes: U4-6982 TAG Group not working as it's expected, when there's more than 2 sets of tag group 2015-08-20 16:52:07 +02:00
Shannon 3dac630d05 Fixes: U4-6998 User language drop down is incorrect when default language is used 2015-08-20 16:07:46 +02:00
Shannon 10108d75b8 Fixes: U4-6953 DatePicker (with time?) seems to be misbehaving since 7.2.8 in Firefox 2015-08-20 15:22:10 +02:00
Shannon 24cb64579b make sure the time is not there for a non time picker when the pre-value is empty. 2015-08-20 12:48:33 +02:00
Shannon 86239cc777 fixes date time picker if the format is cleared from pre-values, it will revert to default. 2015-08-20 12:45:42 +02:00
Shannon 685aea1728 Fixes: U4-6879 Validation issues when creating new content and cancelling save event. 2015-08-20 11:50:32 +02:00
Shannon eb4e041682 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-20 10:25:53 +02:00
Shannon 477aa754b3 uses non deprected methods 2015-08-20 10:14:07 +02:00
Shannon Deminick 48bff0aa6c Merge pull request #770 from bjarnef/dev-v7-missing-danish-translations
Add missing Danish translations
2015-08-20 10:12:26 +02:00
Bjarne Fyrstenborg 4ea9fec9e2 Add missing Danish translations
Added missing keys in da.xml and removed some duplicate keys.
2015-08-20 09:56:16 +02:00
Sebastiaan Janssen f3a965841b U4-6043 Error parsing package.manifest value Path line 0 position 0
Added a unit test and updated the logic as it was doing the wrong thing
#U4-6043 Fixed
2015-08-19 19:11:20 +02:00
Sebastiaan Janssen 08ddb5f4c4 Update NuGet dependency for Examine 2015-08-19 18:47:27 +02:00
Sebastiaan Janssen 4ec63ffd0d U4-6939 'html code' button is missing in the Rich Text Editor]
#U4-6939 Fixed
2015-08-19 18:40:04 +02:00
Sebastiaan Janssen 4cb3e82972 U4-6926 HTML tag in nodeName renders as HTML in tree
#U4-6926 Fixed
2015-08-19 18:33:11 +02:00
Shannon 0a20741f39 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-19 18:22:26 +02:00
Shannon 4575748f16 adds TODO and formatting 2015-08-19 18:22:06 +02:00
Sebastiaan Janssen 028ee831b8 U4-6043 Error parsing package.manifest value Path line 0 position 0
#U4-6043 Fixed
2015-08-19 18:11:18 +02:00
Sebastiaan Janssen 54981cd57c Merge pull request #764 from Aaen/U4-6977
U4-6977: Updated the Our screenshot to show off the new design
2015-08-19 16:02:10 +02:00
Shannon Deminick 0aab8cacaa Merge pull request #767 from leekelleher/U4-6986
U4-6986 PublishedContentExtensions.FirstChild() throws exception
2015-08-19 15:41:15 +02:00
leekelleher 15b4d33e00 U4-6986 PublishedContentExtensions.FirstChild() throws exception
Changes the return value to use `.FirstOrDefault()` instead of `.First()`.
Includes unit-test.
2015-08-19 13:57:58 +01:00
Shannon Deminick 367ba5cddf Merge pull request #766 from bjarnef/dev-v7-minor-translations
Localize text in help dialog
2015-08-19 10:40:37 +02:00
Dennis Aaen ff64efbcdf Fix: Corrected the height of the image of the new ourumbraco 2015-08-19 10:00:39 +02:00
bjarnef 5bc0c499d8 Localize text in help dialog
Add localization for text in help dialog, create and delete dialogs and
some minor parts of the member area.
2015-08-19 02:03:54 +02:00
Dennis Aaen f667318b58 U4-6977: Updated the Our screenshot to show off the new design 2015-08-18 15:14:51 +02:00
Shannon 87c9d15ad5 removes autocomplete="off" from login 2015-08-18 10:44:45 +02:00
Shannon 127736d188 updates to latest examine 2015-08-13 15:30:28 +02:00
Shannon 139ab8d4df Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-13 15:19:26 +02:00
Shannon 4abb8035b8 Changes the shutdown time for the logger to be 1 second 2015-08-13 15:19:16 +02:00
Shannon 0ca1807d64 Adds DelegateExtensions + Tests which is used for retrying and is used with the Examine Index Sync 2015-08-13 15:19:04 +02:00
Shannon Deminick 5ae90d201c Merge pull request #762 from Jeavon/RefreshByJson
Publicise methods and subclasses in MediaCacheRefresher
2015-08-13 14:59:23 +02:00
Jeavon Leopold 6c416009d7 Make DeserializeFromJsonPayload in MediaCacheRefresher and associated sub classes public so that it's possible to easily retrieve the data in custom code 2015-08-13 13:52:21 +01:00
Sebastiaan Janssen 50da9bcb50 Should help with U4-4049 Some images fail when uploading multiple media files 2015-08-13 12:26:00 +02:00
Shannon 9e6a2d8a7c Fixing: U4-6942 During upgrade MUST make sure that the new Property Type GUIDs are consistent ... just needs some testing now; 2015-08-11 15:56:48 +02:00
Shannon cfb84cae44 removes license txt that doesn't make any sense. 2015-08-11 12:23:15 +02:00
Shannon 4239833713 updates NotificationService 2015-08-11 12:12:34 +02:00
Shannon 4fa1a3948e updates how versions are sorted before being sent. 2015-08-11 11:52:06 +02:00
Shannon Deminick 1b78e854f0 Merge pull request #759 from umbraco/U4-5180
Fix: U4-5180 Notifications email highlighting is incorrect. Ensured t…
2015-08-11 11:50:57 +02:00
Shannon Deminick c62a2362d2 Merge pull request #761 from umbraco/U4-6808
U4-6808 Bug in umbRequestHelper service causing exceptions to end up …
2015-08-11 11:48:01 +02:00
Shannon Deminick eb1855fcb7 Merge pull request #760 from umbraco/U4-6798
U4-6798 Rollback Dialog Displays Incorrect Author.
2015-08-11 11:44:10 +02:00
Shannon d4863239cd adds null check to query builder 2015-08-11 10:40:18 +02:00
Shannon 4b7ddb9c15 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-10 18:06:50 +02:00
Shannon e5bb35b335 Updates to latest Examine 2015-08-10 18:06:23 +02:00
Claus 7eabd09b3a Ignoring test causing CI to fail occasionally. 2015-08-10 15:11:08 +02:00
Shannon 087f0c0ede updates internals visible for courier. 2015-08-10 13:09:48 +02:00
Shannon db9d6d9109 Makes the getter for InternalSearcher lazy, fixes up the LocalTempStorageDirectory so that it properly deals with both directories the same, including lock files. Fixes up the LocalTempStorageIndexer with a ton more error checks and retry attempts to acquire locks if it cannot do so during initialization. This has proven to work seamlessly with my load tests now including the functionality for Sync. Need to test LocalOnly with the tests now too. Also, this should work if an index gets corrupted in temp storage for some odd reason (shouldn't do now btw). More testing required but this so far is showing to be much more stable especially with the new changes in Examine core. 2015-08-07 00:06:56 +02:00
Shannon 700e180522 Updates ensuring db connection so that it's not waiting arbitrarily to exit. 2015-08-07 00:02:35 +02:00
Claus 8584997f5f U4-6808 Bug in umbRequestHelper service causing exceptions to end up in backoffice. 2015-08-06 16:03:12 +02:00
Claus 4710b3b641 U4-6798 Rollback Dialog Displays Incorrect Author. 2015-08-06 14:53:56 +02:00
Claus 227e2489c1 Fix: U4-5180 Notifications email highlighting is incorrect. Ensured the versions of content used in comparison is sorted correctly so the highlighting is based on previous version and not the base version of the content. 2015-08-06 10:32:41 +02:00
Shannon 91a5b19378 Fixes: U4-6891 Can't get to the "Rebuild" button when Lucene index folder is empty as well as how the polling works. 2015-08-05 18:32:59 +02:00
Shannon 4c20441c4e Fixes: U4-6915 Txt Starter Kit install fails on 7.3.0 beta3 2015-08-05 17:17:49 +02:00
Shannon edfe1e6d54 Fixes: U4-2793 There doesn't seem to be a way to un-assign a template from a content item with the new API. Fixes issue with content type cache clearing when templates are deleted. Fixes the legacy packager to use the new template service to delete templates which does it in a much more graceful way, otherwise we need to do a try catch. 2015-08-05 16:30:21 +02:00
Shannon 7869f77704 Fixes up deleting templates, fixes up legacy template code to ensure things are saved when they are updated 2015-08-05 15:00:28 +02:00
Shannon 34fd405bca Downgrades AutoMapper to the version before the assembly was signed (breaking!), we're stuck on < 3.1 because Automapper decided to sign an assembly in a minor version, nothing we can do till v8 2015-08-05 13:35:42 +02:00
Shannon f03ee9f908 Fixes routing tests and reverts to simplified routing logic based on the Language ISO code returned from the IDomain object. 2015-08-05 12:10:13 +02:00
Shannon b053638204 Fixes: U4-6866 getDescendants of template, N+1 issues for template repo (U4-5618 Repository level performance enhancements - remaining N+1 queries) 2015-08-05 11:50:20 +02:00
Shannon d790194682 Fixes: U4-6918 App_Plugins folder needs to be created properly on app start 2015-08-04 17:36:03 +02:00
Shannon 3fec2b9756 uses the LanguageIsoCode returned from the domain object during routing so it doesn't need to be re-looked up. 2015-08-04 15:53:08 +02:00
Shannon bc7a31d791 Updates IDomain to include language ISO code so we can save a lookup 2015-08-04 15:42:44 +02:00
Shannon 05d84c6673 updates the operation status to return the event messages used when raising events 2015-08-04 14:52:34 +02:00
Shannon f6645e3928 Fixes: U4-6917 ContentTypeService.GetContentType( GUID) throws exception 2015-08-04 14:05:57 +02:00
Shannon 50b00b12b0 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-04 13:23:57 +02:00
Shannon 569371ffff Fixes resize panel U4-5308 Umbraco 7 Content Tree Width not resizable or scrollable 2015-08-04 13:22:22 +02:00
Per Ploug Krogslund f7b72b225a Grid: auto-insert layout, row and editors if only 1 is available
When the grid loads and only one layout is available, it will be
inserted automatically.

When a row is inserted and a cell only has a single editor allowed, it
will be inserted.

These are chained so when an empty grid editor is loaded it will
preload layout + controllers if only one option is possible.

Also fixed the cog icon being visible, even without settings available

Adjusted a bit of styling for TinyMCE editors as they are invisible if
no focus + content entered.
2015-08-04 12:25:33 +02:00
Shannon d1bed54cf1 Fixes tabdrop memory leak U4-6907 Bootstrap tab drop has memory leaks, fixes tab/header to use the correct bootstrap tab api, simplifies the header code dramatically and speeds up processing. Fixes mem leak with delayedMouseLeave.directive 2015-08-04 11:59:57 +02:00
Shannon ba868887d4 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-08-03 12:50:18 +02:00
Shannon 3a7853dcbe makes sure the bottom bar buttons are moved when resizing the left pane and that their position stays relative during tab changing. 2015-08-02 11:32:43 +02:00
Shannon 60af7deacd ensures more unbinding is happening 2015-08-02 11:32:14 +02:00
Shannon 49bc3151b0 Fixes the detectfold processing and tab changing events, creates new umbTabs directive to manage the bootstrap tab changing events so other directives can easily subscribe to the changes. updats all editors to use them. 2015-08-02 11:13:26 +02:00
Shannon d6b7bae38a reverts tabs back to use only bootstrap tabs (we can change that at a later date now that we have the code), have fixed up a lot of the detect-fold logic so that it doesn't run an insane amount of calculations, however now we need to properly fix the bootstrap tab event bindings. 2015-08-01 17:33:55 +02:00
Sebastiaan Janssen 715cc22722 Revert "Correct trees.config"
This reverts commit 88b23e09b6.
2015-08-01 15:11:47 +02:00
Sebastiaan Janssen 88b23e09b6 Correct trees.config 2015-08-01 15:05:01 +02:00
Shannon 612484c0b6 ensures more jquery event listeners are unbound 2015-07-31 12:52:29 +02:00
Shannon 0e4974babe Creates windowResizeListener directive... so there's only a single listener for this and things can register/unregister as required from it and the callbacks will always execute in a $digest. Updates the detect-fold and navResize to use this, it is much snappier now with less overhead. 2015-07-31 12:34:02 +02:00
Sebastiaan Janssen 5fdd7d8bd6 Merge branch 'bjarnef-dev-v7-U4-6898' into dev-v7 2015-07-31 11:29:58 +02:00
Sebastiaan Janssen b5c87ccfb7 Merge branch 'dev-v7-U4-6898' of https://github.com/bjarnef/Umbraco-CMS into bjarnef-dev-v7-U4-6898 2015-07-31 11:29:51 +02:00
Sebastiaan Janssen 2efe6ae7a4 Merge pull request #754 from bjarnef/dev-v7-U4-6179
Highlight recycle bin tree node
2015-07-31 11:20:44 +02:00
Shannon 5775984730 have new tab implementation working with backwards compat with bootstrap tabs. For our own editors, we will use the new tabs, this gives us more control and should be a bit quicker, perhaps in v8 we can remove bootstrap tabs entirely. 2015-07-31 10:50:33 +02:00
Sebastiaan Janssen cbab70fd7c Should finally fix this test that apparently relies on super strict timings 2015-07-30 19:28:33 +02:00
Shannon cfa2a30006 Changes how tabs work ... to no longer have to rely on bootstrap tabs and the hacky timing work-arounds, tabs are actually quite easy and now give us much more control.
Have made this change as backwards compatable (some things are deprecated though). Will update the other editors now.
2015-07-30 18:44:15 +02:00
bjarnef 09a3b0d155 Highlight recycle bin tree node
Ensure recycle bin tree node is highlighted when chosen.
2015-07-30 18:36:22 +02:00
Sebastiaan Janssen ca9cd77ded Fix log type 2015-07-30 18:14:29 +02:00
Sebastiaan Janssen 812601217a Merge branch 'pete2' into dev-v7 2015-07-30 18:03:09 +02:00
Pete Duncanson 3a6ec5ef0c Add a log message to stop database start up code from swallowing exception messages, now stash them in the logs to give developers a chance ;) 2015-07-30 18:01:24 +02:00
Rasmus Eeg Møller 4197a7654f U4-6902 - Changed from internal to public
Just like the ContentFinderResolver.cs
2015-07-30 17:59:18 +02:00
Sebastiaan Janssen 3133014650 Removes more references to FixedDisplayModes 2015-07-30 17:39:07 +02:00
Sebastiaan Janssen 4bce945946 Remove stray FixedDisplayModes reference 2015-07-30 17:19:43 +02:00
Sebastiaan Janssen 2d15c9c01c Use the new views config transform 2015-07-30 17:08:40 +02:00
Sebastiaan Janssen 58e648828b Fix web.config and adds a transform for NuGet 2015-07-30 17:08:40 +02:00
Sebastiaan Janssen d87b268a40 Update to MVC5 2015-07-30 17:08:39 +02:00
bjarnef 5174645059 Make tree nodes consistent
Make tree nodes have same position from the left regardless of the arrow
to expand the children.
2015-07-30 16:04:54 +02:00
Shannon e4286bc35b Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-07-30 15:13:51 +02:00
Shannon cf4d13da31 fixes backward compat issue 2015-07-30 15:13:39 +02:00
Shannon Deminick a89b064e29 Merge pull request #751 from rasmuseeg/patch-1
U4-6902 - Changed from internal to public
2015-07-30 15:04:23 +02:00
Rasmus Eeg Møller 1e41973eb5 U4-6902 - Changed from internal to public
Just like the ContentFinderResolver.cs
2015-07-30 14:54:10 +02:00
Shannon b4e496fceb Fixes backwards compat issue in 7.3 with TagQuery property on UmbracoHelper. see: http://issues.umbraco.org/issue/U4-6899 2015-07-30 14:03:17 +02:00
Shannon bb94fc7d71 Ensures notifications are shown for drag/drop uploads 2015-07-30 11:35:00 +02:00
Shannon 959378c456 Fixes file cleanup with blue imp uploads 2015-07-29 16:14:09 +02:00
Shannon 787e128d5c wires up event messages for media editor and tree (haven't done the drag/drop media upload though) 2015-07-29 16:00:06 +02:00
Shannon b7912a7be7 fixes build 2015-07-29 15:22:22 +02:00
Shannon 0bc7aa6ba7 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2015-07-29 15:18:34 +02:00
Shannon 367da39bbb wires up UI to show notifications for delete cancellation 2015-07-29 15:18:23 +02:00
Shannon 6d8ffb903f Ensures events for deleting, trashing and unpublishing are done using the event messages, updates UI to support showing these messages in the list view, Since we cannot change the
method results on a service, we've created a new temporary service that we will explicitly implement with an extension method to get it. This is temporary until v8 where we will just replace
the original methods on the service with these methods and then remove this class.
2015-07-29 15:12:12 +02:00
Shannon 0e992f897f Removes standalone classes, we can replace these with what we've got in the LinqPad provider 2015-07-29 15:07:55 +02:00
Shannon 3f19878c18 changes member controller to use long overload 2015-07-29 15:07:32 +02:00
Stephan 308f64a7e2 doc BatchedDatabaseServerMessenger startup 2015-07-29 11:22:12 +02:00
Shannon 1eab92b299 updates prog bars in examine mgt dashboard to be like other umb prog bars 2015-07-27 18:58:14 +02:00
Shannon 7a8b8f138f Fixes list view search debounce and adds a ui indication that search is in progress. 2015-07-27 18:48:41 +02:00
Shannon 926ea54c39 Developers can cancel an event with the CancelOperation method of the event with a custom message which will be used in place of the normal default cancellation message 2015-07-27 18:04:20 +02:00
225 changed files with 4189 additions and 2886 deletions
+6 -5
View File
@@ -15,9 +15,8 @@
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="Microsoft.AspNet.Mvc" version="[4.0.40804.0,6.0.0)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.3,6.0.0)" />
<dependency id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
<dependency id="Microsoft.Net.Http" version="[2.2.29, 3.0.0)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1, 3.0.0)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[3.0.1, 4.0.0)" />
@@ -30,13 +29,15 @@
<dependency id="MySql.Data" version="[6.9.7, 7.0.0)" />
<dependency id="xmlrpcnet" version="[2.5.0, 3.0.0)" />
<dependency id="ClientDependency" version="[1.8.4, 2.0.0)" />
<dependency id="ClientDependency-Mvc" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 3.1.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.8, 7.0.0)" />
<dependency id="Examine" version="[0.1.66, 1.0.0)" />
<dependency id="Examine" version="[0.1.68, 1.0.0)" />
<dependency id="ImageProcessor" version="[1.9.5, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.3.1, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 2.0.0)" />
<dependency id="Microsoft.AspNet.WebHelpers" version="[3.2.3, 4.0.0)" />
<dependency id="Microsoft.AspNet.WebPages.Data" version="[3.2.3, 4.0.0)" />
</dependencies>
</metadata>
<files>
+1
View File
@@ -40,6 +40,7 @@
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
<file src="build\**" target="build" />
</files>
</package>
@@ -0,0 +1,37 @@
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(name)"
xdt:Transform="SetAttributes(type)">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(name)"
xdt:Transform="SetAttributes(type)" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(name)"
xdt:Transform="SetAttributes(type)" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(factoryType)"
xdt:Transform="SetAttributes(factoryType)" />
</system.web.webPages.razor>
<system.web>
<pages
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
xdt:Locator="Match(pageParserFilterType,pageBaseType,userControlBaseType)"
xdt:Transform="SetAttributes(pageParserFilterType,pageBaseType,userControlBaseType)">
<controls>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc"
xdt:Locator="Match(namespace)"
xdt:Transform="SetAttributes(assembly)" />
</controls>
</pages>
</system.web>
</configuration>
@@ -51,6 +51,8 @@
<system.web xdt:Transform="InsertIfMissing" />
<system.web>
<siteMap xdt:Transform="Remove" />
<httpRuntime xdt:Transform="InsertIfMissing" />
<httpRuntime targetFramework="4.5" xdt:Transform="SetAttributes(targetFramework)" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
+1
View File
@@ -58,6 +58,7 @@ if ($project) {
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.3.0
beta2
RC
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.3.0")]
[assembly: AssemblyInformationalVersion("7.3.0-beta2")]
[assembly: AssemblyInformationalVersion("7.3.0-RC")]
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "beta2"; } }
public static string CurrentComment { get { return "RC"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
+11 -6
View File
@@ -159,15 +159,16 @@ namespace Umbraco.Core
/// <returns></returns>
protected virtual ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory)
{
//default transient factory
var msgFactory = new TransientMessagesFactory();
return new ServiceContext(
new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(dbFactory),
new FileUnitOfWorkProvider(),
new PublishingStrategy(),
new PublishingStrategy(msgFactory, ProfilingLogger.Logger),
ApplicationCache,
ProfilingLogger.Logger,
//default transient factory
new TransientMessagesFactory());
msgFactory);
}
/// <summary>
@@ -379,14 +380,19 @@ namespace Umbraco.Core
if (ApplicationContext.IsConfigured == false) return;
if (ApplicationContext.DatabaseContext.IsDatabaseConfigured == false) return;
//try now
if (ApplicationContext.DatabaseContext.CanConnect)
return;
var currentTry = 0;
while (currentTry < 5)
{
//first wait, then retry
Thread.Sleep(1000);
if (ApplicationContext.DatabaseContext.CanConnect)
break;
//wait and retry
Thread.Sleep(1000);
currentTry++;
}
@@ -394,7 +400,6 @@ namespace Umbraco.Core
{
throw new UmbracoStartupFailedException("Umbraco cannot start. A connection string is configured but the Umbraco cannot connect to the database.");
}
}
/// <summary>
+44
View File
@@ -0,0 +1,44 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace Umbraco.Core
{
public static class DelegateExtensions
{
public static Attempt<T> RetryUntilSuccessOrTimeout<T>(this Func<Attempt<T>> task, TimeSpan timeout, TimeSpan pause)
{
if (pause.TotalMilliseconds < 0)
{
throw new ArgumentException("pause must be >= 0 milliseconds");
}
var stopwatch = Stopwatch.StartNew();
do
{
var result = task();
if (result) { return result; }
Thread.Sleep((int)pause.TotalMilliseconds);
}
while (stopwatch.Elapsed < timeout);
return Attempt<T>.Fail();
}
public static Attempt<T> RetryUntilSuccessOrMaxAttempts<T>(this Func<int, Attempt<T>> task, int totalAttempts, TimeSpan pause)
{
if (pause.TotalMilliseconds < 0)
{
throw new ArgumentException("pause must be >= 0 milliseconds");
}
int attempts = 0;
do
{
attempts++;
var result = task(attempts);
if (result) { return result; }
Thread.Sleep((int)pause.TotalMilliseconds);
}
while (attempts < totalAttempts);
return Attempt<T>.Fail();
}
}
}
@@ -63,6 +63,17 @@ namespace Umbraco.Core.Events
}
}
/// <summary>
/// if this instance supports cancellation, this will set Cancel to true with an affiliated cancellation message
/// </summary>
/// <param name="cancelationMessage"></param>
public void CancelOperation(EventMessage cancelationMessage)
{
Cancel = true;
cancelationMessage.IsDefaultEventMessage = true;
Messages.Add(cancelationMessage);
}
/// <summary>
/// Returns the EventMessages object which is used to add messages to the message collection for this event
/// </summary>
+57 -7
View File
@@ -4,12 +4,56 @@ namespace Umbraco.Core.Events
{
public class DeleteEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
{
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
public DeleteEventArgs(IEnumerable<TEntity> eventObject, bool canCancel) : base(eventObject, canCancel)
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
/// <param name="eventMessages"></param>
public DeleteEventArgs(IEnumerable<TEntity> eventObject, bool canCancel, EventMessages eventMessages) : base(eventObject, canCancel, eventMessages)
{
MediaFilesToDelete = new List<string>();
}
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="eventMessages"></param>
public DeleteEventArgs(IEnumerable<TEntity> eventObject, EventMessages eventMessages) : base(eventObject, eventMessages)
{
MediaFilesToDelete = new List<string>();
}
/// <summary>
/// Constructor accepting a single entity instance
/// </summary>
/// <param name="eventObject"></param>
/// <param name="eventMessages"></param>
public DeleteEventArgs(TEntity eventObject, EventMessages eventMessages)
: base(new List<TEntity> { eventObject }, eventMessages)
{
MediaFilesToDelete = new List<string>();
}
/// <summary>
/// Constructor accepting a single entity instance
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
/// <param name="eventMessages"></param>
public DeleteEventArgs(TEntity eventObject, bool canCancel, EventMessages eventMessages)
: base(new List<TEntity> { eventObject }, canCancel, eventMessages)
{
MediaFilesToDelete = new List<string>();
}
/// <summary>
/// Constructor accepting multiple entities that are used in the delete operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
public DeleteEventArgs(IEnumerable<TEntity> eventObject, bool canCancel) : base(eventObject, canCancel)
{
MediaFilesToDelete = new List<string>();
}
@@ -60,7 +104,13 @@ namespace Umbraco.Core.Events
public class DeleteEventArgs : CancellableEventArgs
{
public DeleteEventArgs(int id, bool canCancel)
public DeleteEventArgs(int id, bool canCancel, EventMessages eventMessages)
: base(canCancel, eventMessages)
{
Id = id;
}
public DeleteEventArgs(int id, bool canCancel)
: base(canCancel)
{
Id = id;
+1 -52
View File
@@ -8,34 +8,6 @@ namespace Umbraco.Core.Events
/// </summary>
public static class EventExtensions
{
/// <summary>
/// Raises the event and checks if a cancelation has occurred, if so it checks if any messages have been
/// added to the event messages collection and if not it adds a default cancelation message
/// </summary>
/// <typeparam name="TSender"></typeparam>
/// <typeparam name="TArgs"></typeparam>
/// <param name="eventHandler"></param>
/// <param name="evtMsgs"></param>
/// <param name="args"></param>
/// <param name="sender"></param>
/// <returns></returns>
public static bool IsRaisedEventCancelled<TSender, TArgs>(
this TypedEventHandler<TSender, TArgs> eventHandler,
EventMessages evtMsgs,
Func<EventMessages, TArgs> args,
TSender sender)
where TArgs : CancellableEventArgs
{
var evtArgs = args(evtMsgs);
if (eventHandler != null)
{
eventHandler(sender, evtArgs);
}
return evtArgs.Cancel;
}
/// <summary>
/// Raises the event and returns a boolean value indicating if the event was cancelled
/// </summary>
@@ -56,30 +28,7 @@ namespace Umbraco.Core.Events
return args.Cancel;
}
/// <summary>
/// Raises the event with EventMessages
/// </summary>
/// <typeparam name="TSender"></typeparam>
/// <typeparam name="TArgs"></typeparam>
/// <param name="eventHandler"></param>
/// <param name="evtMsgs"></param>
/// <param name="args"></param>
/// <param name="sender"></param>
public static void RaiseEvent<TSender, TArgs>(
this TypedEventHandler<TSender, TArgs> eventHandler,
EventMessages evtMsgs,
Func<EventMessages, TArgs> args,
TSender sender)
where TArgs : EventArgs
{
if (eventHandler != null)
{
var evtArgs = args(evtMsgs);
eventHandler(sender, evtArgs);
}
}
/// <summary>
/// Raises the event
/// </summary>
+5
View File
@@ -18,5 +18,10 @@ namespace Umbraco.Core.Events
public string Category { get; private set; }
public string Message { get; private set; }
public EventMessageType MessageType { get; private set; }
/// <summary>
/// This is used to track if this message should be used as a default message so that Umbraco doesn't also append it's own default messages
/// </summary>
internal bool IsDefaultEventMessage { get; set; }
}
}
+43
View File
@@ -6,6 +6,49 @@ namespace Umbraco.Core.Events
{
public class MoveEventArgs<TEntity> : CancellableObjectEventArgs<TEntity>
{
/// <summary>
/// Constructor accepting a collection of MoveEventInfo objects
/// </summary>
/// <param name="canCancel"></param>
/// <param name="eventMessages"></param>
/// <param name="moveInfo">
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
/// </param>
public MoveEventArgs(bool canCancel, EventMessages eventMessages, params MoveEventInfo<TEntity>[] moveInfo)
: base(default(TEntity), canCancel, eventMessages)
{
if (moveInfo.FirstOrDefault() == null)
{
throw new ArgumentException("moveInfo argument must contain at least one item");
}
MoveInfoCollection = moveInfo;
//assign the legacy props
EventObject = moveInfo.First().Entity;
ParentId = moveInfo.First().NewParentId;
}
/// <summary>
/// Constructor accepting a collection of MoveEventInfo objects
/// </summary>
/// <param name="eventMessages"></param>
/// <param name="moveInfo">
/// A colleciton of MoveEventInfo objects that exposes all entities that have been moved during a single move operation
/// </param>
public MoveEventArgs(EventMessages eventMessages, params MoveEventInfo<TEntity>[] moveInfo)
: base(default(TEntity), eventMessages)
{
if (moveInfo.FirstOrDefault() == null)
{
throw new ArgumentException("moveInfo argument must contain at least one item");
}
MoveInfoCollection = moveInfo;
//assign the legacy props
EventObject = moveInfo.First().Entity;
ParentId = moveInfo.First().NewParentId;
}
/// <summary>
/// Constructor accepting a collection of MoveEventInfo objects
/// </summary>
@@ -4,6 +4,52 @@ namespace Umbraco.Core.Events
{
public class PublishEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
{
/// <summary>
/// Constructor accepting multiple entities that are used in the publish operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
/// <param name="isAllPublished"></param>
/// <param name="eventMessages"></param>
public PublishEventArgs(IEnumerable<TEntity> eventObject, bool canCancel, bool isAllPublished, EventMessages eventMessages)
: base(eventObject, canCancel, eventMessages)
{
IsAllRepublished = isAllPublished;
}
/// <summary>
/// Constructor accepting multiple entities that are used in the publish operation
/// </summary>
/// <param name="eventObject"></param>
/// <param name="eventMessages"></param>
public PublishEventArgs(IEnumerable<TEntity> eventObject, EventMessages eventMessages)
: base(eventObject, eventMessages)
{
}
/// <summary>
/// Constructor accepting a single entity instance
/// </summary>
/// <param name="eventObject"></param>
/// <param name="eventMessages"></param>
public PublishEventArgs(TEntity eventObject, EventMessages eventMessages)
: base(new List<TEntity> { eventObject }, eventMessages)
{
}
/// <summary>
/// Constructor accepting a single entity instance
/// </summary>
/// <param name="eventObject"></param>
/// <param name="canCancel"></param>
/// <param name="isAllPublished"></param>
/// <param name="eventMessages"></param>
public PublishEventArgs(TEntity eventObject, bool canCancel, bool isAllPublished, EventMessages eventMessages)
: base(new List<TEntity> { eventObject }, canCancel, eventMessages)
{
IsAllRepublished = isAllPublished;
}
/// <summary>
/// Constructor accepting multiple entities that are used in the publish operation
/// </summary>
@@ -22,8 +22,8 @@ namespace Umbraco.Core.Logging
private CancellationTokenSource _loggingCancelationTokenSource;
private CancellationToken _loggingCancelationToken;
private Task _loggingTask;
private Double _shutdownFlushTimeout = 5;
private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(5);
private Double _shutdownFlushTimeout = 1;
private TimeSpan _shutdownFlushTimespan = TimeSpan.FromSeconds(1);
private static readonly Type ThisType = typeof(ParallelForwardingAppender);
private volatile bool _shutDownRequested;
private int? _bufferSize = DefaultBufferSize;
+16 -4
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -140,11 +141,22 @@ namespace Umbraco.Core.Manifest
{
var result = new List<PackageManifest>();
foreach (var m in manifestFileContents)
{
if (m.IsNullOrWhiteSpace()) continue;
{
var manifestContent = m;
if (manifestContent.IsNullOrWhiteSpace()) continue;
// Strip byte object marker, JSON.NET does not like it
var preamble = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
// Strangely StartsWith(preamble) would always return true
if (manifestContent.Substring(0, 1) == preamble)
manifestContent = manifestContent.Remove(0, preamble.Length);
if (manifestContent.IsNullOrWhiteSpace()) continue;
//remove any comments first
var replaced = CommentsSurround.Replace(m, match => " ");
var replaced = CommentsSurround.Replace(manifestContent, match => " ");
replaced = CommentsLine.Replace(replaced, match => "");
JObject deserialized;
@@ -154,7 +166,7 @@ namespace Umbraco.Core.Manifest
}
catch (Exception ex)
{
LogHelper.Error<ManifestParser>("An error occurred parsing manifest with contents: " + m, ex);
LogHelper.Error<ManifestParser>("An error occurred parsing manifest with contents: " + manifestContent, ex);
continue;
}
+1 -7
View File
@@ -95,13 +95,7 @@ namespace Umbraco.Core.Models
[DataMember]
public virtual ITemplate Template
{
get
{
if (_template == null)
return _contentType.DefaultTemplate;
return _template;
}
get { return _template; }
set
{
SetPropertyValueAndDetectChanges(o =>
+5
View File
@@ -8,5 +8,10 @@ namespace Umbraco.Core.Models
string DomainName { get; set; }
int? RootContentId { get; set; }
bool IsWildcard { get; }
/// <summary>
/// Readonly value of the language ISO code for the domain
/// </summary>
string LanguageIsoCode { get; }
}
}
@@ -23,5 +23,11 @@ namespace Umbraco.Core.Models.Rdbms
[Column("domainName")]
public string DomainName { get; set; }
/// <summary>
/// Used for a result on the query to get the associated language for a domain if there is one
/// </summary>
[ResultColumn("languageISOCode")]
public string IsoCode { get; set; }
}
}
+1 -1
View File
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models
public class Stylesheet : File
{
public Stylesheet(string path)
: base(path)
: base(path.EnsureEndsWith(".css"))
{
InitializeProperties();
}
+10 -4
View File
@@ -14,6 +14,12 @@ namespace Umbraco.Core.Models
_domainName = domainName;
}
public UmbracoDomain(string domainName, string languageIsoCode)
: this(domainName)
{
LanguageIsoCode = languageIsoCode;
}
private int? _contentId;
private int? _languageId;
private string _domainName;
@@ -70,9 +76,9 @@ namespace Umbraco.Core.Models
get { return string.IsNullOrWhiteSpace(DomainName) || DomainName.StartsWith("*"); }
}
public string IsoCode
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// Readonly value of the language ISO code for the domain
/// </summary>
public string LanguageIsoCode { get; internal set; }
}
}
@@ -7,6 +7,7 @@ using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Persistence
{
@@ -75,8 +76,10 @@ namespace Umbraco.Core.Persistence
connection.Open();
connection.Close();
}
catch (DbException)
catch (DbException exc)
{
// Don't swallow this error, the exception is super handy for knowing "why" its not available
LogHelper.WarnWithException<IDbConnection>("Configured database is reporting as not being available!", exc);
return false;
}
@@ -43,12 +43,8 @@ namespace Umbraco.Core.Persistence.Factories
Path = dto.NodeDto.Path
};
if (childDefinitions.Any(x => x.ParentId == dto.NodeId))
{
template.IsMasterTemplate = true;
}
template.IsMasterTemplate = childDefinitions.Any(x => x.ParentId == dto.NodeId);
//TODO: Change this to ParentId: http://issues.umbraco.org/issue/U4-5846
if(dto.NodeDto.ParentId > 0)
template.MasterTemplateId = new Lazy<int>(() => dto.NodeDto.ParentId);
@@ -30,9 +30,26 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe
.NonClustered()
.WithOptions()
.Unique();
//now we need to fill in the data so that it is consistent, we can't have it generating random GUIDs for
// the already existing data, see: http://issues.umbraco.org/issue/U4-6942
foreach (var data in Context.Database.Query<dynamic>(@"
SELECT cmsPropertyType.id ptId, cmsPropertyType.Alias ptAlias, cmsContentType.alias ctAlias
FROM cmsPropertyType
INNER JOIN cmsContentType
ON cmsPropertyType.contentTypeId = cmsContentType.nodeId"))
{
//create a guid from the concatenation of the property type alias + the doc type alias
string concatAlias = data.ptAlias + data.ctAlias;
var ptGuid = concatAlias.ToGuid();
//set the Unique Id to the one we've generated
Update.Table("cmsPropertyType").Set(new {uniqueID = ptGuid}).Where(new {id = data.ptId });
}
}
}
public override void Down()
@@ -340,6 +340,12 @@ namespace Umbraco.Core.Persistence.Repositories
{
((Content)entity).AddingEntity();
//ensure the default template is assigned
if (entity.Template == null)
{
entity.Template = entity.ContentType.DefaultTemplate;
}
//Ensure unique name on the same level
entity.Name = EnsureUniqueNodeName(entity.ParentId, entity.Name);
@@ -648,8 +654,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IContent>(sqlClause, query);
var sql = translator.Translate()
.Where<DocumentDto>(x => x.Published)
.OrderBy<NodeDto>(x => x.Level)
.OrderBy<NodeDto>(x => x.SortOrder);
.OrderBy<NodeDto>(x => x.Level, SqlSyntax)
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
//NOTE: This doesn't allow properties to be part of the query
var dtos = Database.Fetch<DocumentDto, ContentVersionDto, ContentDto, NodeDto, DocumentPublishedReadOnlyDto>(sql);
@@ -855,7 +861,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="contentType"></param>
/// <param name="template"></param>
/// <param name="propCollection"></param>
@@ -873,6 +879,11 @@ namespace Umbraco.Core.Persistence.Repositories
{
content.Template = template ?? _templateRepository.Get(dto.TemplateId.Value);
}
else
{
//ensure there isn't one set.
content.Template = null;
}
content.Properties = propCollection;
@@ -885,7 +896,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <summary>
/// Private method to create a content object from a DocumentDto, which is used by Get and GetByVersion.
/// </summary>
/// <param name="d"></param>
/// <param name="dto"></param>
/// <param name="versionId"></param>
/// <param name="docSql"></param>
/// <returns></returns>
@@ -713,7 +713,7 @@ AND umbracoNode.id <> @id",
var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,
ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId
@@ -727,7 +727,12 @@ AND umbracoNode.id <> @id",
ON cmsContentTypeAllowedContentType.AllowedId = cmsContentType.nodeId
) AllowedTypes
ON AllowedTypes.Id = cmsContentType.nodeId
LEFT JOIN cmsContentType2ContentType as ParentTypes
LEFT JOIN (
SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
FROM cmsContentType2ContentType
INNER JOIN umbracoNode
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
@@ -842,7 +847,7 @@ AND umbracoNode.id <> @id",
cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc,
cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb,
AllowedTypes.allowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias,
ParentTypes.parentContentTypeId as chtParentId,
ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey,
umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser,
umbracoNode.parentID as nParentId, umbracoNode." + sqlSyntax.GetQuotedColumnName("path") + @" as nPath, umbracoNode.sortOrder as nSortOrder, umbracoNode." + sqlSyntax.GetQuotedColumnName("text") + @" as nName, umbracoNode.trashed as nTrashed,
umbracoNode.uniqueID as nUniqueId,
@@ -865,7 +870,12 @@ AND umbracoNode.id <> @id",
ON cmsTemplate.nodeId = umbracoNode.id
) as Template
ON Template.nodeId = cmsDocumentType.templateNodeId
LEFT JOIN cmsContentType2ContentType as ParentTypes
LEFT JOIN (
SELECT cmsContentType2ContentType.parentContentTypeId, umbracoNode.uniqueID AS parentContentTypeKey, cmsContentType2ContentType.childContentTypeId
FROM cmsContentType2ContentType
INNER JOIN umbracoNode
ON cmsContentType2ContentType.parentContentTypeId = umbracoNode." + sqlSyntax.GetQuotedColumnName("id") + @"
) ParentTypes
ON ParentTypes.childContentTypeId = cmsContentType.nodeId
WHERE (umbracoNode.nodeObjectType = @nodeObjectType)";
@@ -1043,7 +1053,14 @@ AND umbracoNode.id <> @id",
? x.ctId == currentCtId
: x.nUniqueId == currentCtId;
})
.Select(x => (TId?)x.chtParentId)
.Select(x =>
{
//TODO: This is a bit hacky right now but don't have time to do a nice refactor to support both GUID and Int queries, so this is
// how it is for now.
return (typeof(TId) == typeof(int))
? (TId?)x.chtParentId
: (TId?)x.chtParentKey;
})
.Where(x => x.HasValue)
.Distinct()
.Select(x => x.Value).ToList());
@@ -14,6 +14,8 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
//TODO: We need to get a readonly ISO code for the domain assigned
internal class DomainRepository : PetaPocoRepositoryBase<int, IDomain>, IDomainRepository
{
private readonly RepositoryCacheOptions _cacheOptions;
@@ -62,7 +64,18 @@ namespace Umbraco.Core.Persistence.Repositories
protected override Sql GetBaseQuery(bool isCount)
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*").From<DomainDto>(SqlSyntax);
if (isCount)
{
sql.Select("COUNT(*)").From<DomainDto>(SqlSyntax);
}
else
{
sql.Select("umbracoDomains.*, umbracoLanguage.languageISOCode")
.From<DomainDto>(SqlSyntax)
.LeftJoin<LanguageDto>(SqlSyntax)
.On<DomainDto, LanguageDto>(SqlSyntax, dto => dto.DefaultLanguage, dto => dto.Id);
}
return sql;
}
@@ -110,6 +123,12 @@ namespace Umbraco.Core.Persistence.Repositories
var id = Convert.ToInt32(Database.Insert(dto));
entity.Id = id;
//if the language changed, we need to resolve the ISO code!
if (entity.LanguageId.HasValue)
{
((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar<string>("SELECT languageISOCode FROM umbracoLanguage WHERE id=@langId", new { langId = entity.LanguageId });
}
entity.ResetDirtyProperties();
}
@@ -139,6 +158,12 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(dto);
//if the language changed, we need to resolve the ISO code!
if (entity.WasPropertyDirty("LanguageId"))
{
((UmbracoDomain)entity).LanguageIsoCode = Database.ExecuteScalar<string>("SELECT languageISOCode FROM umbracoLanguage WHERE id=@langId", new {langId = entity.LanguageId});
}
entity.ResetDirtyProperties();
}
@@ -176,7 +201,12 @@ namespace Umbraco.Core.Persistence.Repositories
public IDomain BuildEntity(DomainDto dto)
{
var domain = new UmbracoDomain(dto.DomainName) { Id = dto.Id, LanguageId = dto.DefaultLanguage, RootContentId = dto.RootStructureId };
var domain = new UmbracoDomain(dto.DomainName, dto.IsoCode)
{
Id = dto.Id,
LanguageId = dto.DefaultLanguage,
RootContentId = dto.RootStructureId
};
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
domain.ResetDirtyProperties(false);
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
@@ -10,12 +11,17 @@ namespace Umbraco.Core.Persistence.Repositories
IEnumerable<ITemplate> GetAll(params string[] aliases);
IEnumerable<ITemplate> GetChildren(int masterTemplateId);
IEnumerable<ITemplate> GetChildren(string alias);
IEnumerable<ITemplate> GetDescendants(int masterTemplateId);
IEnumerable<ITemplate> GetDescendants(string alias);
/// <summary>
/// Returns a template as a template node which can be traversed (parent, children)
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
[Obsolete("Use GetDescendants instead")]
TemplateNode GetTemplateNode(string alias);
/// <summary>
@@ -24,6 +30,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="anyNode"></param>
/// <param name="alias"></param>
/// <returns></returns>
[Obsolete("Use GetDescendants instead")]
TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias);
/// <summary>
@@ -25,6 +25,8 @@ namespace Umbraco.Core.Persistence.Repositories
public override Stylesheet Get(string id)
{
id = id.EnsureEndsWith(".css");
if (FileSystem.FileExists(id) == false)
{
return null;
@@ -63,7 +65,10 @@ namespace Umbraco.Core.Persistence.Repositories
public override IEnumerable<Stylesheet> GetAll(params string[] ids)
{
//ensure they are de-duplicated, easy win if people don't do this as this can cause many excess queries
ids = ids.Distinct().ToArray();
ids = ids
.Select(x => x.EnsureEndsWith(".css"))
.Distinct()
.ToArray();
if (ids.Any())
{
@@ -477,7 +477,7 @@ namespace Umbraco.Core.Persistence.Repositories
tagSetSql,
" inner join cmsTags as NewTags on (TagSet.Tag = NewTags.Tag and TagSet.",
SqlSyntax.GetQuotedColumnName("group"),
" = TagSet.",
" = NewTags.",
SqlSyntax.GetQuotedColumnName("group"),
") ",
") as NewTagsSet ",
@@ -67,7 +67,15 @@ namespace Umbraco.Core.Persistence.Repositories
protected override ITemplate PerformGet(int id)
{
return GetAll(new[] { id }).FirstOrDefault();
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.NodeId == id);
var result = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
if (result == null) return null;
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIds = GetAxisDefinitions(result).ToArray();
return MapFromDto(result, childIds);
}
protected override IEnumerable<ITemplate> PerformGetAll(params int[] ids)
@@ -89,20 +97,15 @@ namespace Umbraco.Core.Persistence.Repositories
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIdsSql = new Sql()
.Select("nodeId,alias,parentID")
.From<TemplateDto>()
.InnerJoin<NodeDto>()
.On<TemplateDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(t => t.ParentId > 0);
var childIds = Database.Fetch<dynamic>(childIdsSql)
.Select(x => new UmbracoEntity
var childIds = (ids.Any()
? GetAxisDefinitions(dtos.ToArray())
: dtos.Select(x => new UmbracoEntity
{
Id = x.nodeId,
ParentId = x.parentID,
Name = x.alias
});
Id = x.NodeId,
ParentId = x.NodeDto.ParentId,
Name = x.Alias
})).ToArray();
return dtos.Select(d => MapFromDto(d, childIds));
}
@@ -118,19 +121,7 @@ namespace Umbraco.Core.Persistence.Repositories
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIdsSql = new Sql()
.Select("nodeId,alias,parentID")
.From<TemplateDto>()
.InnerJoin<NodeDto>()
.On<TemplateDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(t => t.ParentId > 0);
var childIds = Database.Fetch<dynamic>(childIdsSql)
.Select(x => new UmbracoEntity
{
Id = x.nodeId,
ParentId = x.parentID,
Name = x.alias
});
var childIds = GetAxisDefinitions(dtos.ToArray()).ToArray();
return dtos.Select(d => MapFromDto(d, childIds));
}
@@ -143,9 +134,9 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = new Sql();
sql.Select(isCount ? "COUNT(*)" : "*")
.From<TemplateDto>()
.InnerJoin<NodeDto>()
.On<TemplateDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.From<TemplateDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<TemplateDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId);
return sql;
}
@@ -273,7 +264,11 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(dto.NodeDto);
Database.Update(dto);
//re-update if this is a master template, since it could have changed!
var axisDefs = GetAxisDefinitions(dto);
template.IsMasterTemplate = axisDefs.Any(x => x.ParentId == dto.NodeId);
//now do the file work
if (DetermineTemplateRenderingEngine(entity) == RenderingEngine.Mvc)
@@ -304,34 +299,27 @@ namespace Umbraco.Core.Persistence.Repositories
protected override void PersistDeletedItem(ITemplate entity)
{
var deletes = GetDeleteClauses().ToArray();
//TODO: This isn't the most ideal way to delete a template tree, because below it will actually end up
// recursing back to this method for each descendant and re-looking up the template list causing an extrac
// SQL call - not ideal but there shouldn't ever be a heaping list of descendant templates.
//The easiest way to overcome this is to expose the underlying cache upwards so that the repository has access
// to it, then in the PersistDeletedTemplate we wouldn't recurse the underlying function, we'd just call
// PersistDeletedItem with a Template object and clear it's cache.
var descendants = GetDescendants(entity.Id).ToList();
var sql = GetBaseQuery(false).Where<NodeDto>(dto => dto.ParentId > 0 || dto.NodeId == entity.Id);
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql);
var self = dtos.Single(x => x.NodeId == entity.Id);
var allChildren = dtos.Except(new[] { self });
var hierarchy = GenerateTemplateHierarchy(self, allChildren);
//remove ourselves
hierarchy.Remove(self);
//change the order so it goes bottom up!
hierarchy.Reverse();
//change the order so it goes bottom up! (deepest level first)
descendants.Reverse();
//delete the hierarchy
foreach (var descendant in hierarchy)
foreach (var descendant in descendants)
{
PersistDeletedTemplate(descendant);
foreach (var delete in deletes)
{
Database.Execute(delete, new { Id = GetEntityId(descendant) });
}
}
//now we can delete this one
base.PersistDeletedItem(entity);
foreach (var delete in deletes)
{
Database.Execute(delete, new { Id = GetEntityId(entity) });
}
if (DetermineTemplateRenderingEngine(entity) == RenderingEngine.Mvc)
{
@@ -342,28 +330,57 @@ namespace Umbraco.Core.Persistence.Repositories
{
var masterpageName = string.Concat(entity.Alias, ".master");
_masterpagesFileSystem.DeleteFile(masterpageName);
}
}
}
#endregion
private ITemplate MapFromDto(TemplateDto dto, IEnumerable<IUmbracoEntity> childDefinitions)
private IEnumerable<IUmbracoEntity> GetAxisDefinitions(params TemplateDto[] templates)
{
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIdsSql = new Sql()
.Select("nodeId,alias,parentID")
.From<TemplateDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<TemplateDto, NodeDto>(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
//lookup axis's
.Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("id") + " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)",
new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)});
var childIds = Database.Fetch<dynamic>(childIdsSql)
.Select(x => new UmbracoEntity
{
Id = x.nodeId,
ParentId = x.parentID,
Name = x.alias
});
return childIds;
}
/// <summary>
/// Maps from a dto to an ITemplate
/// </summary>
/// <param name="dto"></param>
/// <param name="axisDefinitions">
/// This is a collection of template definitions ... either all templates, or the collection of child templates and it's parent template
/// </param>
/// <returns></returns>
private ITemplate MapFromDto(TemplateDto dto, IUmbracoEntity[] axisDefinitions)
{
string csViewName = string.Concat(dto.Alias, ".cshtml");
string vbViewName = string.Concat(dto.Alias, ".vbhtml");
string masterpageName = string.Concat(dto.Alias, ".master");
var factory = new TemplateFactory();
var template = factory.BuildEntity(dto, childDefinitions);
var template = factory.BuildEntity(dto, axisDefinitions);
if (dto.NodeDto.ParentId > 0)
{
//TODO: Fix this n+1 query!
var masterTemplate = Get(dto.NodeDto.ParentId);
var masterTemplate = axisDefinitions.FirstOrDefault(x => x.Id == dto.NodeDto.ParentId);
if (masterTemplate != null)
{
template.MasterTemplateAlias = masterTemplate.Alias;
template.MasterTemplateAlias = masterTemplate.Name;
template.MasterTemplateId = new Lazy<int>(() => dto.NodeDto.ParentId);
}
}
@@ -390,36 +407,7 @@ namespace Umbraco.Core.Persistence.Repositories
return template;
}
private void PersistDeletedTemplate(TemplateDto dto)
{
//we need to get the real template for this item unfortunately to remove it
var template = Get(dto.NodeId);
if (template != null)
{
//NOTE: We must cast here so that it goes to the outter method to
// ensure the cache is updated.
PersistDeletedItem((IEntity)template);
}
}
/// <summary>
/// Returns a list of templates in order of descendants from the parent
/// </summary>
/// <param name="template"></param>
/// <param name="allChildTemplates"></param>
/// <returns></returns>
private static List<TemplateDto> GenerateTemplateHierarchy(TemplateDto template, IEnumerable<TemplateDto> allChildTemplates)
{
var hierarchy = new List<TemplateDto> { template };
foreach (var t in allChildTemplates.Where(x => x.NodeDto.ParentId == template.NodeId))
{
hierarchy.AddRange(GenerateTemplateHierarchy(t, allChildTemplates));
}
return hierarchy;
}
private void PopulateViewTemplate(ITemplate template, string fileName)
{
string content;
@@ -460,49 +448,119 @@ namespace Umbraco.Core.Persistence.Repositories
if (dto == null)
return null;
return Get(dto.NodeId);
return MapFromDto(dto, GetAxisDefinitions(dto).ToArray());
}
public IEnumerable<ITemplate> GetAll(params string[] aliases)
{
var sql = GetBaseQuery(false);
if (aliases.Any())
{
foreach (var id in aliases)
{
yield return Get(id);
}
}
else
{
var nodeDtos = Database.Fetch<NodeDto>("WHERE nodeObjectType = @NodeObjectType", new { NodeObjectType = NodeObjectTypeId });
foreach (var nodeDto in nodeDtos)
{
yield return Get(nodeDto.NodeId);
}
sql.Where("cmsTemplate.alias IN (@aliases)", new {aliases = aliases});
}
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
var axisDefos = GetAxisDefinitions(dtos).ToArray();
return dtos.Select(x => MapFromDto(x, axisDefos));
}
public IEnumerable<ITemplate> GetChildren(int masterTemplateId)
{
//TODO: Fix this N+1!
List<TemplateDto> found;
if (masterTemplateId == -1)
var sql = GetBaseQuery(false);
if (masterTemplateId <= 0)
{
var sql = GetBaseQuery(false).Where<NodeDto>(x => x.ParentId <= 0);
found = Database.Fetch<TemplateDto, NodeDto>(sql);
sql.Where<NodeDto>(x => x.ParentId <= 0);
}
else
{
var sql = GetBaseQuery(false).Where<NodeDto>(x => x.ParentId == masterTemplateId);
found = Database.Fetch<TemplateDto, NodeDto>(sql);
sql.Where<NodeDto>(x => x.ParentId == masterTemplateId);
}
foreach (var templateDto in found)
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
var axisDefos = GetAxisDefinitions(dtos).ToArray();
return dtos.Select(x => MapFromDto(x, axisDefos));
}
public IEnumerable<ITemplate> GetChildren(string alias)
{
var sql = GetBaseQuery(false);
if (alias.IsNullOrWhiteSpace())
{
yield return Get(templateDto.NodeId);
sql.Where<NodeDto>(x => x.ParentId <= 0);
}
else
{
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
// in a single query, now we have to lookup the path to acheive the same thing
var parent = Database.ExecuteScalar<int?>(new Sql().Select("nodeId").From<TemplateDto>(SqlSyntax).Where<TemplateDto>(dto => dto.Alias == alias));
if (parent.HasValue == false) return Enumerable.Empty<ITemplate>();
sql.Where<NodeDto>(x => x.ParentId == parent.Value);
}
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
var axisDefos = GetAxisDefinitions(dtos).ToArray();
return dtos.Select(x => MapFromDto(x, axisDefos));
}
public IEnumerable<ITemplate> GetDescendants(int masterTemplateId)
{
var sql = GetBaseQuery(false);
if (masterTemplateId > 0)
{
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
// in a single query, now we have to lookup the path to acheive the same thing
var path = Database.ExecuteScalar<string>(
new Sql().Select(SqlSyntax.GetQuotedColumnName("path"))
.From<TemplateDto>(SqlSyntax)
.InnerJoin<NodeDto>(SqlSyntax)
.On<TemplateDto, NodeDto>(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(dto => dto.NodeId == masterTemplateId));
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new { query = path + ",%" });
}
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
var axisDefos = GetAxisDefinitions(dtos).ToArray();
return dtos.Select(x => MapFromDto(x, axisDefos));
}
public IEnumerable<ITemplate> GetDescendants(string alias)
{
var sql = GetBaseQuery(false);
if (alias.IsNullOrWhiteSpace() == false)
{
//unfortunately SQLCE doesn't support scalar subqueries in the where clause, otherwise we could have done this
// in a single query, now we have to lookup the path to acheive the same thing
var path = Database.ExecuteScalar<string>(
"SELECT umbracoNode.path FROM cmsTemplate INNER JOIN umbracoNode ON cmsTemplate.nodeId = umbracoNode.id WHERE cmsTemplate.alias = @alias", new { alias = alias });
if (path.IsNullOrWhiteSpace()) return Enumerable.Empty<ITemplate>();
sql.Where(@"(umbracoNode." + SqlSyntax.GetQuotedColumnName("path") + @" LIKE @query)", new {query = path + ",%" });
}
sql.OrderBy("umbracoNode." + SqlSyntax.GetQuotedColumnName("level"));
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
if (dtos.Length == 0) return Enumerable.Empty<ITemplate>();
var axisDefos = GetAxisDefinitions(dtos).ToArray();
return dtos.Select(x => MapFromDto(x, axisDefos));
}
/// <summary>
@@ -510,13 +568,9 @@ namespace Umbraco.Core.Persistence.Repositories
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
[Obsolete("Use GetDescendants instead")]
public TemplateNode GetTemplateNode(string alias)
{
//in order to do this we need to get all of the templates and then organize,
// TODO: unfortunately our db structure does not use the path correctly for templates so we cannot just look
// up a template tree easily.
//TODO: We do use the 'path' now, so this might be able to be fixed up
{
//first get all template objects
var allTemplates = GetAll().ToArray();
@@ -525,23 +579,17 @@ namespace Umbraco.Core.Persistence.Repositories
{
return null;
}
//then we need to get all template Dto's because those contain the master property
var sql = GetBaseQuery(false);
var allDtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
var selfDto = allDtos.Single(x => x.NodeId == selfTemplate.Id);
//need to get the top-most node of the current tree
var top = selfDto;
while (top.NodeDto.ParentId > 0)
var top = selfTemplate;
while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false)
{
top = allDtos.Single(x => x.NodeId == top.NodeDto.ParentId);
top = allTemplates.Single(x => x.Alias == top.MasterTemplateAlias);
}
var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.NodeId));
var childIds = allDtos.Where(x => x.NodeDto.ParentId == top.NodeId).Select(x => x.NodeId);
var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.Id));
var childTemplates = allTemplates.Where(x => x.MasterTemplateAlias == top.Alias);
//This now creates the hierarchy recursively
topNode.Children = CreateChildren(topNode, childIds, allTemplates, allDtos);
topNode.Children = CreateChildren(topNode, childTemplates, allTemplates);
//now we'll return the TemplateNode requested
return FindTemplateInTree(topNode, alias);
@@ -568,6 +616,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <param name="anyNode"></param>
/// <param name="alias"></param>
/// <returns></returns>
[Obsolete("Use GetDescendants instead")]
public TemplateNode FindTemplateInTree(TemplateNode anyNode, string alias)
{
//first get the root
@@ -658,12 +707,12 @@ namespace Umbraco.Core.Persistence.Repositories
return validFile && validExtension;
}
private static IEnumerable<TemplateNode> CreateChildren(TemplateNode parent, IEnumerable<int> childIds, ITemplate[] allTemplates, TemplateDto[] allDtos)
private static IEnumerable<TemplateNode> CreateChildren(TemplateNode parent, IEnumerable<ITemplate> childTemplates, ITemplate[] allTemplates)
{
var children = new List<TemplateNode>();
foreach (var i in childIds)
foreach (var childTemplate in childTemplates)
{
var template = allTemplates.Single(x => x.Id == i);
var template = allTemplates.Single(x => x.Id == childTemplate.Id);
var child = new TemplateNode(template)
{
Parent = parent
@@ -673,10 +722,11 @@ namespace Umbraco.Core.Persistence.Repositories
children.Add(child);
//get this node's children
var kids = allDtos.Where(x => x.NodeDto.ParentId == i).Select(x => x.NodeId).ToArray();
var local = childTemplate;
var kids = allTemplates.Where(x => x.MasterTemplateAlias == local.Alias);
//recurse
child.Children = CreateChildren(child, kids, allTemplates, allDtos);
child.Children = CreateChildren(child, kids, allTemplates);
}
return children;
}
@@ -41,14 +41,14 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = new Sql();
sql.Select("*")
.From<ContentVersionDto>()
.InnerJoin<ContentDto>()
.On<ContentVersionDto, ContentDto>(left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>()
.On<ContentDto, NodeDto>(left => left.NodeId, right => right.NodeId)
.From<ContentVersionDto>(SqlSyntax)
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.Where<NodeDto>(x => x.NodeObjectType == NodeObjectTypeId)
.Where<NodeDto>(x => x.NodeId == id)
.OrderByDescending<ContentVersionDto>(x => x.VersionDate);
.OrderByDescending<ContentVersionDto>(x => x.VersionDate, SqlSyntax);
var dtos = Database.Fetch<ContentVersionDto, ContentDto, NodeDto>(sql);
foreach (var dto in dtos)
@@ -513,6 +513,8 @@ WHERE EXISTS(
// of ContentItemBasic instances) to the database field names.
switch (orderBy.ToUpperInvariant())
{
case "UPDATEDATE":
return "cmsContentVersion.VersionDate";
case "NAME":
return "umbracoNode.text";
case "OWNER":
+1 -1
View File
@@ -38,9 +38,9 @@ using System.Security.Permissions;
[assembly: InternalsVisibleTo("UmbracoExamine")]
[assembly: InternalsVisibleTo("Concorde.Sync")]
[assembly: InternalsVisibleTo("Umbraco.Belle")]
[assembly: InternalsVisibleTo("Umbraco.VisualStudio")]
[assembly: InternalsVisibleTo("Umbraco.Courier.Core")]
[assembly: InternalsVisibleTo("Umbraco.Courier.Persistence")]
[assembly: InternalsVisibleTo("umbraco.providers")]
//allow this to be mocked in our unit tests
+12 -16
View File
@@ -1,36 +1,32 @@
using System.Collections.Generic;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
/// <summary>
/// The result of publishing a content item
/// </summary>
public class PublishStatus
public class PublishStatus : OperationStatus<IContent, PublishStatusType>
{
public PublishStatus()
{
//initialize
InvalidProperties = new List<Property>();
}
public PublishStatus(IContent content, PublishStatusType statusType)
: this()
{
ContentItem = content;
StatusType = statusType;
public PublishStatus(IContent content, PublishStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
}
/// <summary>
/// Creates a successful publish status
/// </summary>
public PublishStatus(IContent content)
: this(content, PublishStatusType.Success)
public PublishStatus(IContent content, EventMessages eventMessages)
: this(content, PublishStatusType.Success, eventMessages)
{
}
public IContent ContentItem { get; private set; }
public PublishStatusType StatusType { get; internal set; }
public IContent ContentItem
{
get { return Entity; }
}
/// <summary>
/// Gets sets the invalid properties if the status failed due to validation.
@@ -5,6 +5,7 @@ using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
@@ -14,6 +15,16 @@ namespace Umbraco.Core.Publishing
/// </summary>
public class PublishingStrategy : BasePublishingStrategy
{
private readonly IEventMessagesFactory _eventMessagesFactory;
private readonly ILogger _logger;
public PublishingStrategy(IEventMessagesFactory eventMessagesFactory, ILogger logger)
{
if (eventMessagesFactory == null) throw new ArgumentNullException("eventMessagesFactory");
if (logger == null) throw new ArgumentNullException("logger");
_eventMessagesFactory = eventMessagesFactory;
_logger = logger;
}
/// <summary>
/// Publishes a single piece of Content
@@ -22,48 +33,50 @@ namespace Umbraco.Core.Publishing
/// <param name="userId">Id of the User issueing the publish operation</param>
internal Attempt<PublishStatus> PublishInternal(IContent content, int userId)
{
if (Publishing.IsRaisedEventCancelled(new PublishEventArgs<IContent>(content), this))
var evtMsgs = _eventMessagesFactory.Get();
if (Publishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(content, evtMsgs), this))
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published, the event was cancelled.", content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
}
//Check if the Content is Expired to verify that it can in fact be published
if (content.Status == ContentStatus.Expired)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has expired and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedHasExpired));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedHasExpired, evtMsgs));
}
//Check if the Content is Awaiting Release to verify that it can in fact be published
if (content.Status == ContentStatus.AwaitingRelease)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is awaiting release and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedAwaitingRelease));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedAwaitingRelease, evtMsgs));
}
//Check if the Content is Trashed to verify that it can in fact be published
if (content.Status == ContentStatus.Trashed)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is trashed and could not be published.",
content.Name, content.Id));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedIsTrashed));
return Attempt<PublishStatus>.Fail(new PublishStatus(content, PublishStatusType.FailedIsTrashed, evtMsgs));
}
content.ChangePublishedState(PublishedState.Published);
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been published.",
content.Name, content.Id));
return Attempt.Succeed(new PublishStatus(content));
return Attempt.Succeed(new PublishStatus(content, evtMsgs));
}
/// <summary>
@@ -122,11 +135,13 @@ namespace Umbraco.Core.Publishing
// much difference because we iterate over them all anyways?? Morten?
// Because we're grouping I think this will execute all the queries anyways so need to fetch it all first.
var fetchedContent = content.ToArray();
var evtMsgs = _eventMessagesFactory.Get();
//We're going to populate the statuses with all content that is already published because below we are only going to iterate over
// content that is not published. We'll set the status to "AlreadyPublished"
statuses.AddRange(fetchedContent.Where(x => x.Published)
.Select(x => Attempt.Succeed(new PublishStatus(x, PublishStatusType.SuccessAlreadyPublished))));
.Select(x => Attempt.Succeed(new PublishStatus(x, PublishStatusType.SuccessAlreadyPublished, evtMsgs))));
int? firstLevel = null;
@@ -138,7 +153,7 @@ namespace Umbraco.Core.Publishing
//be published regardless of the rules mentioned in the remarks.
if (!firstLevel.HasValue)
{
firstLevel = level.Key;
firstLevel = level.Key;
}
/* Only update content thats not already been published - we want to loop through
@@ -149,7 +164,7 @@ namespace Umbraco.Core.Publishing
//Check if this item should be excluded because it's parent's publishing has failed/cancelled
if (parentsIdsCancelled.Contains(item.ParentId))
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because it's parent's publishing action failed or was cancelled.", item.Name, item.Id));
//if this cannot be published, ensure that it's children can definitely not either!
parentsIdsCancelled.Add(item.Id);
@@ -165,26 +180,27 @@ namespace Umbraco.Core.Publishing
}
//Fire Publishing event
if (Publishing.IsRaisedEventCancelled(new PublishEventArgs<IContent>(item), this))
if (Publishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(item, evtMsgs), this))
{
//the publishing has been cancelled.
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published, the event was cancelled.", item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedCancelledByEvent)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedCancelledByEvent, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the content is valid if the flag is set to check
if (!item.IsValid())
if (item.IsValid() == false)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because some of it's content is not passing validation rules.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
@@ -195,24 +211,24 @@ namespace Umbraco.Core.Publishing
//Check if the Content is Expired to verify that it can in fact be published
if (item.Status == ContentStatus.Expired)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has expired and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedHasExpired)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedHasExpired, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
continue;
}
//Check if the Content is Awaiting Release to verify that it can in fact be published
if (item.Status == ContentStatus.AwaitingRelease)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is awaiting release and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedAwaitingRelease)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedAwaitingRelease, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
@@ -223,10 +239,10 @@ namespace Umbraco.Core.Publishing
//Check if the Content is Trashed to verify that it can in fact be published
if (item.Status == ContentStatus.Trashed)
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' is trashed and could not be published.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedIsTrashed)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedIsTrashed, evtMsgs)));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
@@ -236,13 +252,13 @@ namespace Umbraco.Core.Publishing
item.ChangePublishedState(PublishedState.Published);
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been published.",
item.Name, item.Id));
statuses.Add(Attempt.Succeed(new PublishStatus(item)));
statuses.Add(Attempt.Succeed(new PublishStatus(item, evtMsgs)));
}
}
return statuses;
@@ -263,7 +279,7 @@ namespace Umbraco.Core.Publishing
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
// any document that fails to publish...
var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
if (hasPublishedVersion && !includeUnpublishedDocuments)
{
//it has a published version but our flag tells us to not include un-published documents and therefore we should
@@ -273,7 +289,7 @@ namespace Umbraco.Core.Publishing
else if (!hasPublishedVersion)
{
//it doesn't have a published version so we certainly cannot publish it's children.
parentsIdsCancelled.Add(content.Id);
parentsIdsCancelled.Add(content.Id);
}
}
@@ -326,12 +342,15 @@ namespace Umbraco.Core.Publishing
// if published != newest, then the published flags need to be reseted by whoever is calling that method
// at the moment it's done by the content service
var evtMsgs = _eventMessagesFactory.Get();
//Fire UnPublishing event
if (UnPublishing.IsRaisedEventCancelled(new PublishEventArgs<IContent>(content), this))
if (UnPublishing.IsRaisedEventCancelled(
new PublishEventArgs<IContent>(content, evtMsgs), this))
{
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be unpublished, the event was cancelled.", content.Name, content.Id));
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent));
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
}
//If Content has a release date set to before now, it should be removed so it doesn't interrupt an unpublish
@@ -340,7 +359,7 @@ namespace Umbraco.Core.Publishing
{
content.ReleaseDate = null;
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' had its release date removed, because it was unpublished.",
content.Name, content.Id));
}
@@ -349,11 +368,11 @@ namespace Umbraco.Core.Publishing
if (content.Published)
content.ChangePublishedState(PublishedState.Unpublished);
LogHelper.Info<PublishingStrategy>(
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' has been unpublished.",
content.Name, content.Id));
return Attempt.Succeed(new PublishStatus(content));
return Attempt.Succeed(new PublishStatus(content, evtMsgs));
}
/// <summary>
@@ -385,7 +404,9 @@ namespace Umbraco.Core.Publishing
/// <param name="content"><see cref="IContent"/> thats being published</param>
public override void PublishingFinalized(IContent content)
{
Published.RaiseEvent(new PublishEventArgs<IContent>(content, false, false), this);
var evtMsgs = _eventMessagesFactory.Get();
Published.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
@@ -395,7 +416,9 @@ namespace Umbraco.Core.Publishing
/// <param name="isAllRepublished">Boolean indicating whether its all content that is republished</param>
public override void PublishingFinalized(IEnumerable<IContent> content, bool isAllRepublished)
{
Published.RaiseEvent(new PublishEventArgs<IContent>(content, false, isAllRepublished), this);
var evtMsgs = _eventMessagesFactory.Get();
Published.RaiseEvent(
new PublishEventArgs<IContent>(content, false, isAllRepublished, evtMsgs), this);
}
@@ -405,7 +428,9 @@ namespace Umbraco.Core.Publishing
/// <param name="content"><see cref="IContent"/> thats being unpublished</param>
public override void UnPublishingFinalized(IContent content)
{
UnPublished.RaiseEvent(new PublishEventArgs<IContent>(content, false, false), this);
var evtMsgs = _eventMessagesFactory.Get();
UnPublished.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
@@ -414,7 +439,9 @@ namespace Umbraco.Core.Publishing
/// <param name="content">An enumerable list of <see cref="IContent"/> thats being unpublished</param>
public override void UnPublishingFinalized(IEnumerable<IContent> content)
{
UnPublished.RaiseEvent(new PublishEventArgs<IContent>(content, false, false), this);
var evtMsgs = _eventMessagesFactory.Get();
UnPublished.RaiseEvent(
new PublishEventArgs<IContent>(content, false, false, evtMsgs), this);
}
/// <summary>
@@ -0,0 +1,30 @@
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
namespace Umbraco.Core.Publishing
{
/// <summary>
/// The result of unpublishing a content item
/// </summary>
public class UnPublishStatus : OperationStatus<IContent, UnPublishedStatusType>
{
public UnPublishStatus(IContent content, UnPublishedStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
}
/// <summary>
/// Creates a successful unpublish status
/// </summary>
public UnPublishStatus(IContent content, EventMessages eventMessages)
: this(content, UnPublishedStatusType.Success, eventMessages)
{
}
public IContent ContentItem
{
get { return Entity; }
}
}
}
@@ -0,0 +1,26 @@
namespace Umbraco.Core.Publishing
{
/// <summary>
/// A status type of the result of unpublishing a content item
/// </summary>
/// <remarks>
/// Anything less than 10 = Success!
/// </remarks>
public enum UnPublishedStatusType
{
/// <summary>
/// The unpublishing was successful.
/// </summary>
Success = 0,
/// <summary>
/// The item was already unpublished
/// </summary>
SuccessAlreadyUnPublished = 1,
/// <summary>
/// The publish action has been cancelled by an event handler
/// </summary>
FailedCancelledByEvent = 14,
}
}
+250 -151
View File
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Represents the Content Service, which is an easy access to operations involving <see cref="IContent"/>
/// </summary>
public class ContentService : RepositoryService, IContentService
public class ContentService : RepositoryService, IContentService, IContentServiceOperations
{
private readonly IPublishingStrategy _publishingStrategy;
private readonly EntityXmlSerializer _entitySerializer = new EntityXmlSerializer();
@@ -38,18 +38,18 @@ namespace Umbraco.Core.Services
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public ContentService(
IDatabaseUnitOfWorkProvider provider,
RepositoryFactory repositoryFactory,
IDatabaseUnitOfWorkProvider provider,
RepositoryFactory repositoryFactory,
ILogger logger,
IEventMessagesFactory eventMessagesFactory,
IPublishingStrategy publishingStrategy,
IDataTypeService dataTypeService,
IPublishingStrategy publishingStrategy,
IDataTypeService dataTypeService,
IUserService userService)
: base(provider, repositoryFactory, logger, eventMessagesFactory)
{
if (publishingStrategy == null) throw new ArgumentNullException("publishingStrategy");
if (dataTypeService == null) throw new ArgumentNullException("dataTypeService");
if (userService == null) throw new ArgumentNullException("userService");
if (userService == null) throw new ArgumentNullException("userService");
_publishingStrategy = publishingStrategy;
_dataTypeService = dataTypeService;
_userService = userService;
@@ -747,7 +747,7 @@ namespace Umbraco.Core.Services
}
}
/// <summary>
/// Checks whether an <see cref="IContent"/> item has any children
@@ -857,6 +857,110 @@ namespace Umbraco.Core.Services
return result.Success;
}
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="includeUnpublished"></param>
/// <returns>The list of statuses for all published items</returns>
IEnumerable<Attempt<PublishStatus>> IContentServiceOperations.PublishWithChildren(IContent content, int userId, bool includeUnpublished)
{
return PublishWithChildrenDo(content, userId, includeUnpublished);
}
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
Attempt<PublishStatus> IContentServiceOperations.SaveAndPublish(IContent content, int userId, bool raiseEvents)
{
return SaveAndPublishDo(content, userId, raiseEvents);
}
/// <summary>
/// Deletes an <see cref="IContent"/> object by moving it to the Recycle Bin
/// </summary>
/// <remarks>Move an item to the Recycle Bin will result in the item being unpublished</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt<OperationStatus> IContentServiceOperations.MoveToRecycleBin(IContent content, int userId)
{
var evtMsgs = EventMessagesFactory.Get();
using (new WriteLock(Locker))
{
var originalPath = content.Path;
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IContent>(evtMsgs, new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
var moveInfo = new List<MoveEventInfo<IContent>>
{
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)
};
//Make sure that published content is unpublished before being moved to the Recycle Bin
if (HasPublishedVersion(content.Id))
{
//TODO: this shouldn't be a 'sub operation', and if it needs to be it cannot raise events and cannot be cancelled!
UnPublish(content, userId);
}
//Unpublish descendents of the content item that is being moved to trash
var descendants = GetDescendants(content).OrderBy(x => x.Level).ToList();
foreach (var descendant in descendants)
{
//TODO: this shouldn't be a 'sub operation', and if it needs to be it cannot raise events and cannot be cancelled!
UnPublish(descendant, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
content.WriterId = userId;
content.ChangeTrashedState(true);
repository.AddOrUpdate(content);
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach (var descendant in descendants)
{
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
descendant.WriterId = userId;
descendant.ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
}
uow.Commit();
}
Trashed.RaiseEvent(new MoveEventArgs<IContent>(false, evtMsgs, moveInfo.ToArray()), this);
Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
}
/// <summary>
/// UnPublishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
Attempt<UnPublishStatus> IContentServiceOperations.UnPublish(IContent content, int userId)
{
return UnPublishDo(content, false, userId);
}
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
@@ -865,7 +969,7 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
public Attempt<PublishStatus> PublishWithStatus(IContent content, int userId = 0)
{
return SaveAndPublishDo(content, userId);
return ((IContentServiceOperations)this).Publish(content, userId);
}
/// <summary>
@@ -881,7 +985,7 @@ namespace Umbraco.Core.Services
//This used to just return false only when the parent content failed, otherwise would always return true so we'll
// do the same thing for the moment
if (!result.Any(x => x.Result.ContentItem.Id == content.Id))
if (result.All(x => x.Result.ContentItem.Id != content.Id))
return false;
return result.Single(x => x.Result.ContentItem.Id == content.Id).Success;
@@ -896,7 +1000,7 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
public IEnumerable<Attempt<PublishStatus>> PublishWithChildrenWithStatus(IContent content, int userId = 0, bool includeUnpublished = false)
{
return PublishWithChildrenDo(content, userId, includeUnpublished);
return ((IContentServiceOperations)this).PublishWithChildren(content, userId, includeUnpublished);
}
/// <summary>
@@ -907,7 +1011,7 @@ namespace Umbraco.Core.Services
/// <returns>True if unpublishing succeeded, otherwise False</returns>
public bool UnPublish(IContent content, int userId = 0)
{
return UnPublishDo(content, false, userId);
return ((IContentServiceOperations) this).UnPublish(content, userId).Success;
}
/// <summary>
@@ -933,7 +1037,7 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
public Attempt<PublishStatus> SaveAndPublishWithStatus(IContent content, int userId = 0, bool raiseEvents = true)
{
return SaveAndPublishDo(content, userId, raiseEvents);
return ((IContentServiceOperations)this).SaveAndPublish(content, userId, raiseEvents);
}
/// <summary>
@@ -942,9 +1046,9 @@ namespace Umbraco.Core.Services
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public Attempt<OperationStatus> SaveWithStatus(IContent content, int userId = 0, bool raiseEvents = true)
public void Save(IContent content, int userId = 0, bool raiseEvents = true)
{
return Save(content, true, userId, raiseEvents);
((IContentServiceOperations)this).Save(content, userId, raiseEvents);
}
/// <summary>
@@ -953,18 +1057,19 @@ namespace Umbraco.Core.Services
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public Attempt<OperationStatus> SaveWithStatus(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true)
Attempt<OperationStatus> IContentServiceOperations.Save(IEnumerable<IContent> contents, int userId, bool raiseEvents)
{
var asArray = contents.ToArray();
var evtMsgs = EventMessagesFactory.Get();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
EventMessagesFactory.Get(),
messages => new SaveEventArgs<IContent>(asArray, messages),
new SaveEventArgs<IContent>(asArray, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled);
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
}
using (new WriteLock(Locker))
@@ -1004,23 +1109,88 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
Saved.RaiseEvent(EventMessagesFactory.Get(), messages => new SaveEventArgs<IContent>(asArray, false, messages), this);
Saved.RaiseEvent(new SaveEventArgs<IContent>(asArray, false, evtMsgs), this);
Audit(AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root);
return Attempt.Succeed(OperationStatus.Success);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
}
/// <summary>
/// Permanently deletes an <see cref="IContent"/> object.
/// </summary>
/// <remarks>
/// This method will also delete associated media files, child content and possibly associated domains.
/// </remarks>
/// <remarks>Please note that this method will completely remove the Content from the database</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt<OperationStatus> IContentServiceOperations.Delete(IContent content, int userId)
{
var evtMsgs = EventMessagesFactory.Get();
using (new WriteLock(Locker))
{
if (Deleting.IsRaisedEventCancelled(
new DeleteEventArgs<IContent>(content, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
//Make sure that published content is unpublished before being deleted
if (HasPublishedVersion(content.Id))
{
UnPublish(content, userId);
}
//Delete children before deleting the 'possible parent'
var children = GetChildren(content.Id);
foreach (var child in children)
{
Delete(child, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
repository.Delete(content);
uow.Commit();
var args = new DeleteEventArgs<IContent>(content, false, evtMsgs);
Deleted.RaiseEvent(args, this);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
Audit(AuditType.Delete, "Delete Content performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
}
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>The published status attempt</returns>
Attempt<PublishStatus> IContentServiceOperations.Publish(IContent content, int userId)
{
return SaveAndPublishDo(content, userId);
}
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public void Save(IContent content, int userId = 0, bool raiseEvents = true)
Attempt<OperationStatus> IContentServiceOperations.Save(IContent content, int userId, bool raiseEvents)
{
SaveWithStatus(content, userId, raiseEvents);
return Save(content, true, userId, raiseEvents);
}
/// <summary>
@@ -1035,7 +1205,7 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public void Save(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true)
{
SaveWithStatus(contents, userId, raiseEvents);
((IContentServiceOperations)this).Save(contents, userId, raiseEvents);
}
/// <summary>
@@ -1093,39 +1263,7 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User deleting the Content</param>
public void Delete(IContent content, int userId = 0)
{
using (new WriteLock(Locker))
{
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IContent>(content), this))
return;
//Make sure that published content is unpublished before being deleted
if (HasPublishedVersion(content.Id))
{
UnPublish(content, userId);
}
//Delete children before deleting the 'possible parent'
var children = GetChildren(content.Id);
foreach (var child in children)
{
Delete(child, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
repository.Delete(content);
uow.Commit();
var args = new DeleteEventArgs<IContent>(content, false);
Deleted.RaiseEvent(args, this);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
Audit(AuditType.Delete, "Delete Content performed by user", userId, content.Id);
}
((IContentServiceOperations)this).Delete(content, userId);
}
/// <summary>
@@ -1194,59 +1332,7 @@ namespace Umbraco.Core.Services
/// <param name="userId">Optional Id of the User deleting the Content</param>
public void MoveToRecycleBin(IContent content, int userId = 0)
{
using (new WriteLock(Locker))
{
var originalPath = content.Path;
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IContent>(
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)), this))
{
return;
}
var moveInfo = new List<MoveEventInfo<IContent>>
{
new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)
};
//Make sure that published content is unpublished before being moved to the Recycle Bin
if (HasPublishedVersion(content.Id))
{
UnPublish(content, userId);
}
//Unpublish descendents of the content item that is being moved to trash
var descendants = GetDescendants(content).OrderBy(x => x.Level).ToList();
foreach (var descendant in descendants)
{
UnPublish(descendant, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
content.WriterId = userId;
content.ChangeTrashedState(true);
repository.AddOrUpdate(content);
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach (var descendant in descendants)
{
moveInfo.Add(new MoveEventInfo<IContent>(descendant, descendant.Path, descendant.ParentId));
descendant.WriterId = userId;
descendant.ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
}
uow.Commit();
}
Trashed.RaiseEvent(new MoveEventArgs<IContent>(false, moveInfo.ToArray()), this);
Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
}
((IContentServiceOperations) this).MoveToRecycleBin(content, userId);
}
/// <summary>
@@ -1414,7 +1500,7 @@ namespace Umbraco.Core.Services
SentToPublish.RaiseEvent(new SendToPublishEventArgs<IContent>(content, false), this);
Audit(AuditType.SendToPublish, "Send to Publish performed by user", content.WriterId, content.Id);
return true;
}
@@ -1504,6 +1590,7 @@ namespace Umbraco.Core.Services
if (content.Published)
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
var published = _publishingStrategy.Publish(content, userId);
shouldBePublished.Add(content);
}
@@ -1529,7 +1616,11 @@ namespace Umbraco.Core.Services
Saved.RaiseEvent(new SaveEventArgs<IContent>(asArray, false), this);
if (shouldBePublished.Any())
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
_publishingStrategy.PublishingFinalized(shouldBePublished, false);
}
Audit(AuditType.Sort, "Sorting content performed by user", userId, 0);
@@ -1683,6 +1774,8 @@ namespace Umbraco.Core.Services
{
if (content == null) throw new ArgumentNullException("content");
var evtMsgs = EventMessagesFactory.Get();
using (new WriteLock(Locker))
{
var result = new List<Attempt<PublishStatus>>();
@@ -1694,7 +1787,7 @@ namespace Umbraco.Core.Services
string.Format(
"Content '{0}' with Id '{1}' could not be published because its parent or one of its ancestors is not published.",
content.Name, content.Id));
result.Add(Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedPathNotPublished)));
result.Add(Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedPathNotPublished, evtMsgs)));
return result;
}
@@ -1706,10 +1799,10 @@ namespace Umbraco.Core.Services
content.Name, content.Id));
result.Add(
Attempt.Fail(
new PublishStatus(content, PublishStatusType.FailedContentInvalid)
{
InvalidProperties = ((ContentBase)content).LastInvalidProperties
}));
new PublishStatus(content, PublishStatusType.FailedContentInvalid, evtMsgs)
{
InvalidProperties = ((ContentBase)content).LastInvalidProperties
}));
return result;
}
@@ -1763,38 +1856,42 @@ namespace Umbraco.Core.Services
/// <param name="omitCacheRefresh">Optional boolean to avoid having the cache refreshed when calling this Unpublish method. By default this method will update the cache.</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
private bool UnPublishDo(IContent content, bool omitCacheRefresh = false, int userId = 0)
private Attempt<UnPublishStatus> UnPublishDo(IContent content, bool omitCacheRefresh = false, int userId = 0)
{
var newest = GetById(content.Id); // ensure we have the newest version
if (content.Version != newest.Version) // but use the original object if it's already the newest version
content = newest;
var evtMsgs = EventMessagesFactory.Get();
var published = content.Published ? content : GetPublishedVersion(content.Id); // get the published version
if (published == null)
return false; // already unpublished
var unpublished = _publishingStrategy.UnPublish(content, userId);
if (unpublished)
{
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
content.WriterId = userId;
repository.AddOrUpdate(content);
// is published is not newest, reset the published flag on published version
if (published.Version != content.Version)
repository.ClearPublished(published);
repository.DeleteContentXml(content);
uow.Commit();
}
//Delete xml from db? and call following method to fire event through PublishingStrategy to update cache
if (omitCacheRefresh == false)
_publishingStrategy.UnPublishingFinalized(content);
Audit(AuditType.UnPublish, "UnPublish performed by user", userId, content.Id);
return Attempt.Succeed(new UnPublishStatus(content, UnPublishedStatusType.SuccessAlreadyUnPublished, evtMsgs)); // already unpublished
}
var unpublished = _publishingStrategy.UnPublish(content, userId);
if (unpublished == false) return Attempt.Fail(new UnPublishStatus(content, UnPublishedStatusType.FailedCancelledByEvent, evtMsgs));
return unpublished;
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateContentRepository(uow))
{
content.WriterId = userId;
repository.AddOrUpdate(content);
// is published is not newest, reset the published flag on published version
if (published.Version != content.Version)
repository.ClearPublished(published);
repository.DeleteContentXml(content);
uow.Commit();
}
//Delete xml from db? and call following method to fire event through PublishingStrategy to update cache
if (omitCacheRefresh == false)
_publishingStrategy.UnPublishingFinalized(content);
Audit(AuditType.UnPublish, "UnPublish performed by user", userId, content.Id);
return Attempt.Succeed(new UnPublishStatus(content, UnPublishedStatusType.Success, evtMsgs));
}
/// <summary>
@@ -1806,13 +1903,14 @@ namespace Umbraco.Core.Services
/// <returns>True if publishing succeeded, otherwise False</returns>
private Attempt<PublishStatus> SaveAndPublishDo(IContent content, int userId = 0, bool raiseEvents = true)
{
var evtMsgs = EventMessagesFactory.Get();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
EventMessagesFactory.Get(),
messages => new SaveEventArgs<IContent>(content, messages), this))
new SaveEventArgs<IContent>(content, evtMsgs), this))
{
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent));
return Attempt.Fail(new PublishStatus(content, PublishStatusType.FailedCancelledByEvent, evtMsgs));
}
}
@@ -1820,7 +1918,7 @@ namespace Umbraco.Core.Services
{
//Has this content item previously been published? If so, we don't need to refresh the children
var previouslyPublished = content.HasIdentity && HasPublishedVersion(content.Id); //content might not have an id
var publishStatus = new PublishStatus(content, PublishStatusType.Success); //initially set to success
var publishStatus = new PublishStatus(content, PublishStatusType.Success, evtMsgs); //initially set to success
//Check if parent is published (although not if its a root node) - if parent isn't published this Content cannot be published
publishStatus.StatusType = CheckAndLogIsPublishable(content);
@@ -1870,7 +1968,7 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
Saved.RaiseEvent(EventMessagesFactory.Get(), messages => new SaveEventArgs<IContent>(content, false, messages), this);
Saved.RaiseEvent(new SaveEventArgs<IContent>(content, false, evtMsgs), this);
//Save xml to db and call following method to fire event through PublishingStrategy to update cache
if (published)
@@ -1901,15 +1999,16 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
private Attempt<OperationStatus> Save(IContent content, bool changeState, int userId = 0, bool raiseEvents = true)
{
var evtMsgs = EventMessagesFactory.Get();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
EventMessagesFactory.Get(),
messages => new SaveEventArgs<IContent>(content, messages),
this))
if (Saving.IsRaisedEventCancelled(
new SaveEventArgs<IContent>(content, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled);
}
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
}
using (new WriteLock(Locker))
@@ -1936,11 +2035,11 @@ namespace Umbraco.Core.Services
}
if (raiseEvents)
Saved.RaiseEvent(EventMessagesFactory.Get(), messages => new SaveEventArgs<IContent>(content, false, messages), this);
Saved.RaiseEvent(new SaveEventArgs<IContent>(content, false, evtMsgs), this);
Audit(AuditType.Save, "Save Content performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
}
+47
View File
@@ -319,6 +319,53 @@ namespace Umbraco.Core.Services
}
}
public IEnumerable<ITemplate> GetTemplateDescendants(string alias)
{
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
{
return repository.GetDescendants(alias);
}
}
/// <summary>
/// Gets the template descendants
/// </summary>
/// <param name="masterTemplateId"></param>
/// <returns></returns>
public IEnumerable<ITemplate> GetTemplateDescendants(int masterTemplateId)
{
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
{
return repository.GetDescendants(masterTemplateId);
}
}
/// <summary>
/// Gets the template children
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
public IEnumerable<ITemplate> GetTemplateChildren(string alias)
{
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
{
return repository.GetChildren(alias);
}
}
/// <summary>
/// Gets the template children
/// </summary>
/// <param name="masterTemplateId"></param>
/// <returns></returns>
public IEnumerable<ITemplate> GetTemplateChildren(int masterTemplateId)
{
using (var repository = _repositoryFactory.CreateTemplateRepository(_dataUowProvider.GetUnitOfWork()))
{
return repository.GetChildren(masterTemplateId);
}
}
/// <summary>
/// Returns a template as a template node which can be traversed (parent, children)
/// </summary>
+83 -23
View File
@@ -8,6 +8,86 @@ using Umbraco.Core.Publishing;
namespace Umbraco.Core.Services
{
/// <summary>
/// A temporary interface until we are in v8, this is used to return a different result for the same method and this interface gets implemented
/// explicitly. These methods will replace the normal ones in IContentService in v8 and this will be removed.
/// </summary>
public interface IContentServiceOperations
{
//TODO: Remove this class in v8
//TODO: There's probably more that needs to be added like the EmptyRecycleBin, etc...
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> Save(IContent content, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a collection of <see cref="IContent"/> objects.
/// </summary>
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> Save(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Permanently deletes an <see cref="IContent"/> object.
/// </summary>
/// <remarks>
/// This method will also delete associated media files, child content and possibly associated domains.
/// </remarks>
/// <remarks>Please note that this method will completely remove the Content from the database</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt<OperationStatus> Delete(IContent content, int userId = 0);
/// <summary>
/// Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>The published status attempt</returns>
Attempt<PublishStatus> Publish(IContent content, int userId = 0);
/// <summary>
/// Publishes a <see cref="IContent"/> object and all its children
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish along with its children</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="includeUnpublished"></param>
/// <returns>The list of statuses for all published items</returns>
IEnumerable<Attempt<PublishStatus>> PublishWithChildren(IContent content, int userId = 0, bool includeUnpublished = false);
/// <summary>
/// Saves and Publishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save and publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
Attempt<PublishStatus> SaveAndPublish(IContent content, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Deletes an <see cref="IContent"/> object by moving it to the Recycle Bin
/// </summary>
/// <remarks>Move an item to the Recycle Bin will result in the item being unpublished</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
Attempt<OperationStatus> MoveToRecycleBin(IContent content, int userId = 0);
/// <summary>
/// UnPublishes a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to publish</param>
/// <param name="userId">Optional Id of the User issueing the publishing</param>
/// <returns>True if unpublishing succeeded, otherwise False</returns>
Attempt<UnPublishStatus> UnPublish(IContent content, int userId = 0);
}
/// <summary>
/// Defines the ContentService, which is an easy access to operations involving <see cref="IContent"/>
/// </summary>
@@ -188,31 +268,13 @@ namespace Umbraco.Core.Services
/// </summary>
/// <returns>An Enumerable list of <see cref="IContent"/> objects</returns>
IEnumerable<IContent> GetContentInRecycleBin();
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> SaveWithStatus(IContent content, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a collection of <see cref="IContent"/> objects.
/// </summary>
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> SaveWithStatus(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a single <see cref="IContent"/> object
/// </summary>
/// <param name="content">The <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use SaveWithStatus instead, that method will provide more detailed information on the outcome")]
void Save(IContent content, int userId = 0, bool raiseEvents = true);
/// <summary>
@@ -221,8 +283,6 @@ namespace Umbraco.Core.Services
/// <param name="contents">Collection of <see cref="IContent"/> to save</param>
/// <param name="userId">Optional Id of the User saving the Content</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use SaveWithStatus instead, that method will provide more detailed information on the outcome")]
void Save(IEnumerable<IContent> contents, int userId = 0, bool raiseEvents = true);
/// <summary>
@@ -407,7 +467,7 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise save events.</param>
/// <returns>True if publishing succeeded, otherwise False</returns>
Attempt<PublishStatus> SaveAndPublishWithStatus(IContent content, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Permanently deletes an <see cref="IContent"/> object.
/// </summary>
@@ -416,7 +476,7 @@ namespace Umbraco.Core.Services
/// </remarks>
/// <remarks>Please note that this method will completely remove the Content from the database</remarks>
/// <param name="content">The <see cref="IContent"/> to delete</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
/// <param name="userId">Optional Id of the User deleting the Content</param>
void Delete(IContent content, int userId = 0);
/// <summary>
+29 -1
View File
@@ -130,10 +130,38 @@ namespace Umbraco.Core.Services
ITemplate GetTemplate(int id);
/// <summary>
/// Returns a template as a template node which can be traversed (parent, children)
/// Gets the template descendants
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
IEnumerable<ITemplate> GetTemplateDescendants(string alias);
/// <summary>
/// Gets the template descendants
/// </summary>
/// <param name="masterTemplateId"></param>
/// <returns></returns>
IEnumerable<ITemplate> GetTemplateDescendants(int masterTemplateId);
/// <summary>
/// Gets the template children
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
IEnumerable<ITemplate> GetTemplateChildren(string alias);
/// <summary>
/// Gets the template children
/// </summary>
/// <param name="masterTemplateId"></param>
/// <returns></returns>
IEnumerable<ITemplate> GetTemplateChildren(int masterTemplateId);
/// <summary>
/// Returns a template as a template node which can be traversed (parent, children)
/// </summary>
/// <param name="alias"></param>
/// <returns></returns>
TemplateNode GetTemplateNode(string alias);
/// <summary>
@@ -47,5 +47,13 @@ namespace Umbraco.Core.Services
/// to resolve the full culture if possible.
/// </remarks>
CultureInfo ConvertToSupportedCultureWithRegionCode(CultureInfo currentCulture);
/// <summary>
/// HAAAAAAAAAAACK! Used for backwards compat to convert a user's real culture code to a region code - normally this would be two letters
/// TODO: REmove in v8
/// </summary>
/// <param name="currentCulture"></param>
/// <returns></returns>
string ConvertToRegionCodeFromSupportedCulture(CultureInfo currentCulture);
}
}
+46 -21
View File
@@ -6,6 +6,51 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Services
{
/// <summary>
/// A temporary interface until we are in v8, this is used to return a different result for the same method and this interface gets implemented
/// explicitly. These methods will replace the normal ones in IContentService in v8 and this will be removed.
/// </summary>
public interface IMediaServiceOperations
{
//TODO: Remove this class in v8
//TODO: There's probably more that needs to be added like the EmptyRecycleBin, etc...
/// <summary>
/// Deletes an <see cref="IMedia"/> object by moving it to the Recycle Bin
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to delete</param>
/// <param name="userId">Id of the User deleting the Media</param>
Attempt<OperationStatus> MoveToRecycleBin(IMedia media, int userId = 0);
/// <summary>
/// Permanently deletes an <see cref="IMedia"/> object
/// </summary>
/// <remarks>
/// Please note that this method will completely remove the Media from the database,
/// but current not from the file system.
/// </remarks>
/// <param name="media">The <see cref="IMedia"/> to delete</param>
/// <param name="userId">Id of the User deleting the Media</param>
Attempt<OperationStatus> Delete(IMedia media, int userId = 0);
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> Save(IMedia media, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a collection of <see cref="IMedia"/> objects
/// </summary>
/// <param name="medias">Collection of <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> Save(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true);
}
/// <summary>
/// Defines the Media Service, which is an easy access to operations involving <see cref="IMedia"/>
/// </summary>
@@ -174,31 +219,13 @@ namespace Umbraco.Core.Services
/// <param name="media">The <see cref="IMedia"/> to delete</param>
/// <param name="userId">Id of the User deleting the Media</param>
void Delete(IMedia media, int userId = 0);
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> SaveWithStatus(IMedia media, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a collection of <see cref="IMedia"/> objects
/// </summary>
/// <param name="medias">Collection of <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> SaveWithStatus(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use SaveWithStatus instead, that method will provide more detailed information on the outcome")]
void Save(IMedia media, int userId = 0, bool raiseEvents = true);
/// <summary>
@@ -207,8 +234,6 @@ namespace Umbraco.Core.Services
/// <param name="medias">Collection of <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use SaveWithStatus instead, that method will provide more detailed information on the outcome")]
void Save(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true);
/// <summary>
@@ -191,6 +191,23 @@ namespace Umbraco.Core.Services
return attempt ? attempt.Result : currentCulture;
}
/// <summary>
/// HAAAAAAAAAAACK! Used for backwards compat to convert a user's real culture code to a region code - normally this would be two letters
/// </summary>
/// <param name="currentCulture"></param>
/// <returns></returns>
public string ConvertToRegionCodeFromSupportedCulture(CultureInfo currentCulture)
{
if (currentCulture == null) throw new ArgumentNullException("currentCulture");
if (_fileSources == null) return currentCulture.Name;
var attempt = _fileSources.Value.TryConvert4LetterCultureTo2Letter(currentCulture);
return attempt
? attempt.Result
: currentCulture.Name;
}
private string GetFromDictionarySource(CultureInfo culture, string area, string key, IDictionary<string, string> tokens)
{
if (_dictionarySource.ContainsKey(culture) == false)
@@ -158,7 +158,7 @@ namespace Umbraco.Core.Services
//TODO: See other notes in this class, this is purely a hack because we store 2 letter culture file names that contain 4 letter cultures :(
public Attempt<CultureInfo> TryConvert2LetterCultureTo4Letter(string twoLetterCulture)
{
if (twoLetterCulture.Length != 2) Attempt<CultureInfo>.Fail();
if (twoLetterCulture.Length != 2) return Attempt<CultureInfo>.Fail();
//This needs to be resolved before continuing so that the _twoLetterCultureConverter cache is initialized
var resolved = _xmlSources.Value;
@@ -168,6 +168,19 @@ namespace Umbraco.Core.Services
: Attempt<CultureInfo>.Fail();
}
//TODO: See other notes in this class, this is purely a hack because we store 2 letter culture file names that contain 4 letter cultures :(
public Attempt<string> TryConvert4LetterCultureTo2Letter(CultureInfo culture)
{
if (culture == null) throw new ArgumentNullException("culture");
//This needs to be resolved before continuing so that the _twoLetterCultureConverter cache is initialized
var resolved = _xmlSources.Value;
return _twoLetterCultureConverter.Values.Contains(culture)
? Attempt.Succeed(culture.Name.Substring(0, 2))
: Attempt<string>.Fail();
}
private void MergeSupplementaryFiles(CultureInfo culture, XDocument xMasterDoc)
{
if (xMasterDoc.Root == null) return;
+182 -147
View File
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Represents the Media Service, which is an easy access to operations involving <see cref="IMedia"/>
/// </summary>
public class MediaService : RepositoryService, IMediaService
public class MediaService : RepositoryService, IMediaService, IMediaServiceOperations
{
//Support recursive locks because some of the methods that require locking call other methods that require locking.
@@ -726,54 +726,144 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the User deleting the Media</param>
public void MoveToRecycleBin(IMedia media, int userId = 0)
{
if (media == null) throw new ArgumentNullException("media");
((IMediaServiceOperations) this).MoveToRecycleBin(media, userId);
}
var originalPath = media.Path;
/// <summary>
/// Permanently deletes an <see cref="IMedia"/> object
/// </summary>
/// <remarks>
/// Please note that this method will completely remove the Media from the database,
/// but current not from the file system.
/// </remarks>
/// <param name="media">The <see cref="IMedia"/> to delete</param>
/// <param name="userId">Id of the User deleting the Media</param>
Attempt<OperationStatus> IMediaServiceOperations.Delete(IMedia media, int userId)
{
//TODO: IT would be much nicer to mass delete all in one trans in the repo level!
var evtMsgs = EventMessagesFactory.Get();
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IMedia>(
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
if (Deleting.IsRaisedEventCancelled(
new DeleteEventArgs<IMedia>(media, evtMsgs), this))
{
return;
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
var moveInfo = new List<MoveEventInfo<IMedia>>
//Delete children before deleting the 'possible parent'
var children = GetChildren(media.Id);
foreach (var child in children)
{
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
};
//Find Descendants, which will be moved to the recycle bin along with the parent/grandparent.
var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList();
Delete(child, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
//TODO: This should be part of the repo!
repository.Delete(media);
uow.Commit();
//Remove 'published' xml from the cmsContentXml table for the unpublished media
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = media.Id });
var args = new DeleteEventArgs<IMedia>(media, false, evtMsgs);
Deleted.RaiseEvent(args, this);
media.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
repository.AddOrUpdate(media);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach (var descendant in descendants)
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> IMediaServiceOperations.Save(IMedia media, int userId, bool raiseEvents)
{
var evtMsgs = EventMessagesFactory.Get();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
new SaveEventArgs<IMedia>(media, evtMsgs),
this))
{
//Remove 'published' xml from the cmsContentXml table for the unpublished media
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = descendant.Id });
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
descendant.ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
}
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
{
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
uow.Commit();
}
Trashed.RaiseEvent(new MoveEventArgs<IMedia>(false, moveInfo.ToArray()), this);
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMedia>(media, false, evtMsgs), this);
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
Audit(AuditType.Save, "Save Media performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
/// <summary>
/// Saves a collection of <see cref="IMedia"/> objects
/// </summary>
/// <param name="medias">Collection of <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
Attempt<OperationStatus> IMediaServiceOperations.Save(IEnumerable<IMedia> medias, int userId, bool raiseEvents)
{
var asArray = medias.ToArray();
var evtMsgs = EventMessagesFactory.Get();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
new SaveEventArgs<IMedia>(asArray, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
foreach (var media in asArray)
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
{
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
}
//commit the whole lot in one go
uow.Commit();
}
if (raiseEvents)
Saved.RaiseEvent(new SaveEventArgs<IMedia>(asArray, false, evtMsgs), this);
Audit(AuditType.Save, "Save Media items performed by user", userId, -1);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
/// <summary>
@@ -859,6 +949,67 @@ namespace Umbraco.Core.Services
}
}
/// <summary>
/// Deletes an <see cref="IMedia"/> object by moving it to the Recycle Bin
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to delete</param>
/// <param name="userId">Id of the User deleting the Media</param>
Attempt<OperationStatus> IMediaServiceOperations.MoveToRecycleBin(IMedia media, int userId)
{
if (media == null) throw new ArgumentNullException("media");
var originalPath = media.Path;
var evtMsgs = EventMessagesFactory.Get();
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
}
var moveInfo = new List<MoveEventInfo<IMedia>>
{
new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)
};
//Find Descendants, which will be moved to the recycle bin along with the parent/grandparent.
var descendants = GetDescendants(media).OrderBy(x => x.Level).ToList();
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
//TODO: This should be part of the repo!
//Remove 'published' xml from the cmsContentXml table for the unpublished media
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = media.Id });
media.ChangeTrashedState(true, Constants.System.RecycleBinMedia);
repository.AddOrUpdate(media);
//Loop through descendants to update their trash state, but ensuring structure by keeping the ParentId
foreach (var descendant in descendants)
{
//Remove 'published' xml from the cmsContentXml table for the unpublished media
uow.Database.Delete<ContentXmlDto>("WHERE nodeId = @Id", new { Id = descendant.Id });
descendant.ChangeTrashedState(true, descendant.ParentId);
repository.AddOrUpdate(descendant);
moveInfo.Add(new MoveEventInfo<IMedia>(descendant, descendant.Path, descendant.ParentId));
}
uow.Commit();
}
Trashed.RaiseEvent(
new MoveEventArgs<IMedia>(false, evtMsgs, moveInfo.ToArray()), this);
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
}
/// <summary>
/// Permanently deletes an <see cref="IMedia"/> object as well as all of its Children.
/// </summary>
@@ -870,32 +1021,7 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the User deleting the Media</param>
public void Delete(IMedia media, int userId = 0)
{
//TODO: IT would be much nicer to mass delete all in one trans in the repo level!
if (Deleting.IsRaisedEventCancelled(new DeleteEventArgs<IMedia>(media), this))
return;
//Delete children before deleting the 'possible parent'
var children = GetChildren(media.Id);
foreach (var child in children)
{
Delete(child, userId);
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
repository.Delete(media);
uow.Commit();
var args = new DeleteEventArgs<IMedia>(media, false);
Deleted.RaiseEvent(args, this);
//remove any flagged media files
repository.DeleteMediaFiles(args.MediaFilesToDelete);
}
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
}
@@ -954,98 +1080,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Delete, "Delete Media by version performed by user", userId, -1);
}
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public Attempt<OperationStatus> SaveWithStatus(IMedia media, int userId = 0, bool raiseEvents = true)
{
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
EventMessagesFactory.Get(),
messages => new SaveEventArgs<IMedia>(media, messages),
this))
{
return Attempt.Fail(OperationStatus.Cancelled);
}
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
{
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
uow.Commit();
}
if (raiseEvents)
Saved.RaiseEvent(EventMessagesFactory.Get(), messages => new SaveEventArgs<IMedia>(media, false, messages), this);
Audit(AuditType.Save, "Save Media performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success);
}
/// <summary>
/// Saves a collection of <see cref="IMedia"/> objects
/// </summary>
/// <param name="medias">Collection of <see cref="IMedia"/> to save</param>
/// <param name="userId">Id of the User saving the Media</param>
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public Attempt<OperationStatus> SaveWithStatus(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true)
{
var asArray = medias.ToArray();
if (raiseEvents)
{
if (Saving.IsRaisedEventCancelled(
EventMessagesFactory.Get(),
messages => new SaveEventArgs<IMedia>(asArray, messages),
this))
{
return Attempt.Fail(OperationStatus.Cancelled);
}
}
var uow = UowProvider.GetUnitOfWork();
using (var repository = RepositoryFactory.CreateMediaRepository(uow))
{
foreach (var media in asArray)
{
media.CreatorId = userId;
repository.AddOrUpdate(media);
repository.AddOrUpdateContentXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
// generate preview for blame history?
if (UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled)
{
repository.AddOrUpdatePreviewXml(media, m => _entitySerializer.Serialize(this, _dataTypeService, _userService, m));
}
}
//commit the whole lot in one go
uow.Commit();
}
if (raiseEvents)
Saved.RaiseEvent(EventMessagesFactory.Get(), messages => new SaveEventArgs<IMedia>(asArray, false, messages), this);
Audit(AuditType.Save, "Save Media items performed by user", userId, -1);
return Attempt.Succeed(OperationStatus.Success);
}
/// <summary>
/// Saves a single <see cref="IMedia"/> object
/// </summary>
@@ -1054,7 +1089,7 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public void Save(IMedia media, int userId = 0, bool raiseEvents = true)
{
SaveWithStatus(media, userId, raiseEvents);
((IMediaServiceOperations)this).Save (media, userId, raiseEvents);
}
/// <summary>
@@ -1065,7 +1100,7 @@ namespace Umbraco.Core.Services
/// <param name="raiseEvents">Optional boolean indicating whether or not to raise events.</param>
public void Save(IEnumerable<IMedia> medias, int userId = 0, bool raiseEvents = true)
{
SaveWithStatus(medias, userId, raiseEvents);
((IMediaServiceOperations)this).Save(medias, userId, raiseEvents);
}
/// <summary>
@@ -61,7 +61,7 @@ namespace Umbraco.Core.Services
}
var content = (IContent) entity;
//we'll lazily get these if we need to send notifications
IContent[] allVersions = null;
IEnumerable<IContent> allVersions = null;
int totalUsers;
var allUsers = _userService.GetAll(0, int.MaxValue, out totalUsers);
@@ -75,12 +75,17 @@ namespace Umbraco.Core.Services
//lazy load versions if notifications are required
if (allVersions == null)
{
allVersions = _contentService.GetVersions(entity.Id).ToArray();
allVersions = _contentService.GetVersions(entity.Id);
}
try
{
SendNotification(operatingUser, u, content, allVersions, actionName, http, createSubject, createBody);
SendNotification(
operatingUser, u, content,
allVersions,
actionName, http, createSubject, createBody);
_logger.Debug<NotificationService>(string.Format("Notification type: {0} sent to {1} ({2})", action, u.Name, u.Email));
}
catch (Exception ex)
@@ -192,7 +197,7 @@ namespace Umbraco.Core.Services
/// <param name="http"></param>
/// <param name="createSubject">Callback to create the mail subject</param>
/// <param name="createBody">Callback to create the mail body</param>
private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IContent[] allVersions, string actionName, HttpContextBase http,
private void SendNotification(IUser performingUser, IUser mailingUser, IContent content, IEnumerable<IContent> allVersions, string actionName, HttpContextBase http,
Func<IUser, string[], string> createSubject,
Func<IUser, string[], string> createBody)
{
@@ -204,8 +209,11 @@ namespace Umbraco.Core.Services
if (createSubject == null) throw new ArgumentNullException("createSubject");
if (createBody == null) throw new ArgumentNullException("createBody");
int versionCount = (allVersions.Length > 1) ? (allVersions.Length - 2) : (allVersions.Length - 1);
var oldDoc = _contentService.GetByVersion(allVersions[versionCount].Version);
//Ensure they are sorted: http://issues.umbraco.org/issue/U4-5180
var allVersionsAsArray = allVersions.OrderBy(x => x.UpdateDate).ToArray();
int versionCount = (allVersionsAsArray.Length > 1) ? (allVersionsAsArray.Length - 2) : (allVersionsAsArray.Length - 1);
var oldDoc = _contentService.GetByVersion(allVersionsAsArray[versionCount].Version);
// build summary
var summary = new StringBuilder();
+49 -14
View File
@@ -1,26 +1,61 @@
using System;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
/// <summary>
/// The status returned by many of the service methods
/// </summary>
public class OperationStatus
public class OperationStatus<TEntity, TStatus> : OperationStatus<TStatus>
where TStatus : struct
{
//TODO: This is a pretty simple class atm, but is 'future' proofed in case we need to add more detail here
internal static OperationStatus Cancelled
public OperationStatus(TEntity entity, TStatus statusType, EventMessages eventMessages) : base(statusType, eventMessages)
{
get { return new OperationStatus(OperationStatusType.FailedCancelledByEvent);}
Entity = entity;
}
internal static OperationStatus Success
{
get { return new OperationStatus(OperationStatusType.Success); }
}
public TEntity Entity { get; private set; }
public OperationStatus(OperationStatusType statusType)
{
StatusType = statusType;
}
public OperationStatusType StatusType { get; internal set; }
}
public class OperationStatus<TStatus>
where TStatus : struct
{
public OperationStatus(TStatus statusType, EventMessages eventMessages)
{
if (eventMessages == null) throw new ArgumentNullException("eventMessages");
StatusType = statusType;
EventMessages = eventMessages;
}
public TStatus StatusType { get; internal set; }
public EventMessages EventMessages { get; private set; }
}
/// <summary>
/// The default operation status
/// </summary>
public class OperationStatus : OperationStatus<OperationStatusType>
{
public OperationStatus(OperationStatusType statusType, EventMessages eventMessages) : base(statusType, eventMessages)
{
}
#region Static Helper methods
internal static OperationStatus Cancelled(EventMessages eventMessages)
{
return new OperationStatus(OperationStatusType.FailedCancelledByEvent, eventMessages);
}
internal static OperationStatus Success(EventMessages eventMessages)
{
return new OperationStatus(OperationStatusType.Success, eventMessages);
}
#endregion
}
}
@@ -17,5 +17,7 @@ namespace Umbraco.Core.Services
/// The saving has been cancelled by a 3rd party add-in
/// </summary>
FailedCancelledByEvent = 14
//TODO: In the future, we might need to add more operations statuses, potentially like 'FailedByPermissions', etc...
}
}
@@ -12,6 +12,25 @@ using Umbraco.Core.Events;
namespace Umbraco.Core.Services
{
/// <summary>
/// These are used currently to return the temporary 'operation' interfaces for services
/// which are used to return a status from operational methods so we can determine if things are
/// cancelled, etc...
///
/// These will be obsoleted in v8 since all real services methods will be changed to have the correct result.
/// </summary>
public static class ServiceWithResultExtensions
{
public static IContentServiceOperations WithResult(this IContentService contentService)
{
return (IContentServiceOperations)contentService;
}
public static IMediaServiceOperations WithResult(this IMediaService mediaService)
{
return (IMediaServiceOperations)mediaService;
}
}
/// <summary>
/// The Umbraco ServiceContext, which provides access to the following services:
/// <see cref="IContentService"/>, <see cref="IContentTypeService"/>, <see cref="IDataTypeService"/>,
@@ -1,89 +0,0 @@
using System;
using System.Diagnostics;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Publishing;
using Umbraco.Core.Services;
namespace Umbraco.Core.Standalone
{
internal class ServiceContextManager : IDisposable
{
private readonly string _connectionString;
private readonly string _providerName;
private readonly ILogger _logger;
private readonly ISqlSyntaxProvider _syntaxProvider;
private ServiceContext _serviceContext;
private readonly StandaloneCoreApplication _application;
public ServiceContextManager(string connectionString, string providerName, string baseDirectory, ILogger logger, ISqlSyntaxProvider syntaxProvider)
{
_connectionString = connectionString;
_providerName = providerName;
_logger = logger;
_syntaxProvider = syntaxProvider;
Trace.WriteLine("ServiceContextManager-Current AppDomain: " + AppDomain.CurrentDomain.FriendlyName);
Trace.WriteLine("ServiceContextManager-Current AppDomain: " + AppDomain.CurrentDomain.BaseDirectory);
//var webAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(x => x.FullName.StartsWith("umbraco"));
//if (webAssembly != null && examineEventHandlerToRemove == null)
//{
// var examineEventType = webAssembly.GetType("Umbraco.Web.Search.ExamineEvents");
// examineEventHandlerToRemove = examineEventType;
//}
_application = StandaloneCoreApplication.GetApplication(baseDirectory);
var examineEventHandlerToRemove = Type.GetType("Umbraco.Web.Search.ExamineEvents, umbraco");
if (examineEventHandlerToRemove != null)
_application.WithoutApplicationEventHandler(examineEventHandlerToRemove);
_application.Start();
}
public ServiceContext Services
{
get
{
if (_serviceContext == null)
{
var cacheHelper = new CacheHelper(
new ObjectCacheRuntimeCacheProvider(),
new StaticCacheProvider(),
//we have no request based cache when running standalone
new NullCacheProvider());
var dbFactory = new DefaultDatabaseFactory(_connectionString, _providerName, _logger);
var dbContext = new DatabaseContext(dbFactory, _logger, _syntaxProvider, _providerName);
Database.Mapper = new PetaPocoMapper();
_serviceContext = new ServiceContext(
new RepositoryFactory(cacheHelper, _logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(dbFactory),
new FileUnitOfWorkProvider(),
new PublishingStrategy(),
cacheHelper,
new DebugDiagnosticsLogger(),
new TransientMessagesFactory());
//initialize the DatabaseContext
dbContext.Initialize(_providerName);
}
return _serviceContext;
}
}
public void Dispose()
{
((IDisposable)ApplicationContext.Current).Dispose();
_application.Dispose();
}
}
}
@@ -1,122 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Standalone
{
internal class StandaloneCoreApplication : UmbracoApplicationBase
{
/// <summary>
/// Initializes a new instance of the <see cref="StandaloneCoreApplication"/> class.
/// </summary>
protected StandaloneCoreApplication(string baseDirectory)
{
_baseDirectory = baseDirectory;
}
/// <summary>
/// Provides the application boot manager.
/// </summary>
/// <returns>An application boot manager.</returns>
protected override IBootManager GetBootManager()
{
return new StandaloneCoreBootManager(this, _handlersToAdd, _handlersToRemove, _baseDirectory);
}
#region Application
private readonly string _baseDirectory;
private static StandaloneCoreApplication _application;
private static bool _started;
private static readonly object AppLock = new object();
/// <summary>
/// Gets the instance of the standalone Umbraco application.
/// </summary>
public static StandaloneCoreApplication GetApplication(string baseDirectory)
{
lock (AppLock)
{
return _application ?? (_application = new StandaloneCoreApplication(baseDirectory));
}
}
/// <summary>
/// Starts the application.
/// </summary>
public void Start()
{
lock (AppLock)
{
if (_started)
throw new InvalidOperationException("Application has already started.");
Application_Start(this, EventArgs.Empty);
_started = true;
}
}
#endregion
#region IApplicationEventHandler management
private readonly List<Type> _handlersToAdd = new List<Type>();
private readonly List<Type> _handlersToRemove = new List<Type>();
/// <summary>
/// Associates an <see cref="IApplicationEventHandler"/> type with the application.
/// </summary>
/// <typeparam name="T">The type to associate.</typeparam>
/// <returns>The application.</returns>
/// <remarks>Types implementing <see cref="IApplicationEventHandler"/> from within
/// an executable are not automatically discovered by Umbraco and have to be
/// explicitely associated with the application using this method.</remarks>
public StandaloneCoreApplication WithApplicationEventHandler<T>()
where T : IApplicationEventHandler
{
_handlersToAdd.Add(typeof(T));
return this;
}
/// <summary>
/// Dissociates an <see cref="IApplicationEventHandler"/> type from the application.
/// </summary>
/// <typeparam name="T">The type to dissociate.</typeparam>
/// <returns>The application.</returns>
public StandaloneCoreApplication WithoutApplicationEventHandler<T>()
where T : IApplicationEventHandler
{
_handlersToRemove.Add(typeof(T));
return this;
}
/// <summary>
/// Associates an <see cref="IApplicationEventHandler"/> type with the application.
/// </summary>
/// <param name="type">The type to associate.</param>
/// <returns>The application.</returns>
/// <remarks>Types implementing <see cref="IApplicationEventHandler"/> from within
/// an executable are not automatically discovered by Umbraco and have to be
/// explicitely associated with the application using this method.</remarks>
public StandaloneCoreApplication WithApplicationEventHandler(Type type)
{
if (type.Implements<IApplicationEventHandler>() == false)
throw new ArgumentException("Type does not implement IApplicationEventHandler.", "type");
_handlersToAdd.Add(type);
return this;
}
/// <summary>
/// Dissociates an <see cref="IApplicationEventHandler"/> type from the application.
/// </summary>
/// <param name="type">The type to dissociate.</param>
/// <returns>The application.</returns>
public StandaloneCoreApplication WithoutApplicationEventHandler(Type type)
{
if (type.Implements<IApplicationEventHandler>() == false)
throw new ArgumentException("Type does not implement IApplicationEventHandler.", "type");
_handlersToRemove.Add(type);
return this;
}
#endregion
}
}
@@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Persistence.Mappers;
using umbraco.interfaces;
namespace Umbraco.Core.Standalone
{
internal class StandaloneCoreBootManager : CoreBootManager
{
private readonly IEnumerable<Type> _handlersToAdd;
private readonly IEnumerable<Type> _handlersToRemove;
private readonly string _baseDirectory;
public StandaloneCoreBootManager(UmbracoApplicationBase umbracoApplication, IEnumerable<Type> handlersToAdd, IEnumerable<Type> handlersToRemove, string baseDirectory)
: base(umbracoApplication)
{
_handlersToAdd = handlersToAdd;
_handlersToRemove = handlersToRemove;
_baseDirectory = baseDirectory;
base.InitializeApplicationRootPath(_baseDirectory);
// this is only here to ensure references to the assemblies needed for
// the DataTypesResolver otherwise they won't be loaded into the AppDomain.
var interfacesAssemblyName = typeof(IDataType).Assembly.FullName;
}
protected override void InitializeApplicationEventsResolver()
{
base.InitializeApplicationEventsResolver();
foreach (var type in _handlersToAdd)
ApplicationEventsResolver.Current.AddType(type);
foreach (var type in _handlersToRemove)
ApplicationEventsResolver.Current.RemoveType(type);
}
protected override void InitializeResolvers()
{
base.InitializeResolvers();
//Mappers are not resolved, which could be because of a known TypeMapper issue
/*MappingResolver.Reset();
MappingResolver.Current = new MappingResolver(
() =>
new List<Type>
{
typeof (ContentMapper),
typeof (ContentTypeMapper),
typeof (MediaMapper),
typeof (MediaTypeMapper),
typeof (DataTypeDefinitionMapper),
typeof (UmbracoEntityMapper)
});*/
}
}
}
+13
View File
@@ -1409,5 +1409,18 @@ namespace Umbraco.Core
{
return string.IsNullOrEmpty(text) ? text : InvalidXmlChars.Replace(text, "");
}
/// <summary>
/// Converts a string to a Guid - WARNING, depending on the string, this may not be unique
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
internal static Guid ToGuid(this string text)
{
var md5 = MD5.Create();
byte[] myStringBytes = Encoding.ASCII.GetBytes(text);
byte[] hash = md5.ComputeHash(myStringBytes);
return new Guid(hash);
}
}
}
@@ -100,7 +100,7 @@ namespace Umbraco.Core.Sync
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
/// Callers MUST ensure thread-safety.
/// </remarks>
internal protected void Boot()
protected void Boot()
{
// weight:10, must release *before* the facade service, because once released
// the service will *not* be able to properly handle our notifications anymore
+9 -10
View File
@@ -37,13 +37,13 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.9.0, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -302,6 +302,7 @@
<Compile Include="DatabaseContext.cs" />
<Compile Include="DataTableExtensions.cs" />
<Compile Include="DateTimeExtensions.cs" />
<Compile Include="DelegateExtensions.cs" />
<Compile Include="DictionaryExtensions.cs" />
<Compile Include="Dictionary\CultureDictionaryFactoryResolver.cs" />
<Compile Include="Dictionary\ICultureDictionaryFactory.cs" />
@@ -445,6 +446,8 @@
<Compile Include="Persistence\Repositories\TaskRepository.cs" />
<Compile Include="Persistence\Repositories\TaskTypeRepository.cs" />
<Compile Include="PropertyEditors\ValueConverters\GridValueConverter.cs" />
<Compile Include="Publishing\UnPublishedStatusType.cs" />
<Compile Include="Publishing\UnPublishStatus.cs" />
<Compile Include="Security\BackOfficeClaimsIdentityFactory.cs" />
<Compile Include="Security\BackOfficeSignInManager.cs" />
<Compile Include="Security\BackOfficeUserManager.cs" />
@@ -1246,9 +1249,6 @@
<Compile Include="Services\UserService.cs" />
<Compile Include="Services\UserServiceExtensions.cs" />
<Compile Include="IO\ViewHelper.cs" />
<Compile Include="Standalone\ServiceContextManager.cs" />
<Compile Include="Standalone\StandaloneCoreApplication.cs" />
<Compile Include="Standalone\StandaloneCoreBootManager.cs" />
<Compile Include="Strategies\RelateOnCopyHandler.cs" />
<Compile Include="Strategies\RelateOnTrashHandler.cs" />
<Compile Include="Strings\ContentBaseExtensions.cs" />
@@ -1387,7 +1387,6 @@
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net4" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
+2 -2
View File
@@ -100,7 +100,7 @@
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
@@ -108,7 +108,7 @@
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Web.Mvc;
using Microsoft.Web.Mvc;
using NUnit.Framework;
using Umbraco.Core.Profiling;
using Umbraco.Web;
@@ -16,46 +15,38 @@ namespace Umbraco.Tests.BootManagers
{
IList<IViewEngine> engines = new List<IViewEngine>
{
new FixedWebFormViewEngine(),
new FixedRazorViewEngine(),
new RenderViewEngine(),
new PluginViewEngine()
};
WebBootManager.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(4));
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(engines[0], Is.InstanceOf<ProfilingViewEngine>());
Assert.That(engines[1], Is.InstanceOf<ProfilingViewEngine>());
Assert.That(engines[2], Is.InstanceOf<ProfilingViewEngine>());
Assert.That(engines[3], Is.InstanceOf<ProfilingViewEngine>());
}
[Test]
public void WrapViewEngines_HasEngines_KeepsSortOrder()
{
IList<IViewEngine> engines = new List<IViewEngine>
IList<IViewEngine> engines = new List<IViewEngine>
{
new FixedWebFormViewEngine(),
new FixedRazorViewEngine(),
new RenderViewEngine(),
new PluginViewEngine()
};
WebBootManager.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(4));
Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf<FixedWebFormViewEngine>());
Assert.That(((ProfilingViewEngine)engines[1]).Inner, Is.InstanceOf<FixedRazorViewEngine>());
Assert.That(((ProfilingViewEngine)engines[2]).Inner, Is.InstanceOf<RenderViewEngine>());
Assert.That(((ProfilingViewEngine)engines[3]).Inner, Is.InstanceOf<PluginViewEngine>());
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf<RenderViewEngine>());
Assert.That(((ProfilingViewEngine)engines[1]).Inner, Is.InstanceOf<PluginViewEngine>());
}
[Test]
public void WrapViewEngines_HasProfiledEngine_AddsSameInstance()
{
var profiledEngine = new ProfilingViewEngine(new FixedRazorViewEngine());
var profiledEngine = new ProfilingViewEngine(new PluginViewEngine());
IList<IViewEngine> engines = new List<IViewEngine>
{
profiledEngine
@@ -0,0 +1,46 @@
using System;
using Lucene.Net.Index;
using NUnit.Framework;
using Umbraco.Core;
namespace Umbraco.Tests
{
[TestFixture]
public class DelegateExtensionsTests
{
[Test]
public void Only_Executes_Specific_Count()
{
var maxTries = 5;
var totalTries = 0;
DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) =>
{
totalTries = currentTry;
return Attempt<IndexWriter>.Fail();
}, 5, TimeSpan.FromMilliseconds(10));
Assert.AreEqual(maxTries, totalTries);
}
[Test]
public void Quits_On_Success_Count()
{
var maxTries = 5;
var totalTries = 0;
DelegateExtensions.RetryUntilSuccessOrMaxAttempts((currentTry) =>
{
totalTries = currentTry;
if (totalTries == 2)
{
return Attempt<string>.Succeed();
}
return Attempt<string>.Fail();
}, 5, TimeSpan.FromMilliseconds(10));
Assert.AreEqual(2, totalTries);
}
}
}
@@ -119,6 +119,7 @@ namespace Umbraco.Tests.Logging
}
[Test]
[Ignore]
public void CanLogAtleast1000MessagesASecond()
{
// Arrange
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -460,13 +461,15 @@ javascript: ['~/test.js',/*** some note about stuff asd09823-4**09234*/ '~/test2
var content2 = "{javascript: []}";
var content3 = "{javascript: ['~/test.js', '~/test2.js']}";
var content4 = "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
var content5 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble()) + "{propertyEditors: [], javascript: ['~/test.js', '~/test2.js']}";
var result = ManifestParser.CreateManifests(null, content1, content2, content3, content4);
var result = ManifestParser.CreateManifests(null, content1, content2, content3, content4, content5);
Assert.AreEqual(4, result.Count());
Assert.AreEqual(5, result.Count());
Assert.AreEqual(0, result.ElementAt(1).JavaScriptInitialize.Count);
Assert.AreEqual(2, result.ElementAt(2).JavaScriptInitialize.Count);
Assert.AreEqual(2, result.ElementAt(3).JavaScriptInitialize.Count);
Assert.AreEqual(2, result.ElementAt(4).JavaScriptInitialize.Count);
}
[Test]
@@ -56,11 +56,12 @@ namespace Umbraco.Tests.Persistence
var repositoryFactory = new RepositoryFactory(cacheHelper, _logger, SqlSyntaxProvider, SettingsForTests.GenerateMockSettings());
var evtMsgs = new TransientMessagesFactory();
ApplicationContext.Current = new ApplicationContext(
//assign the db context
dbContext,
//assign the service context
new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(_logger), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper, _logger, new TransientMessagesFactory()),
new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(_logger), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, _logger), cacheHelper, _logger, evtMsgs),
cacheHelper,
new ProfilingLogger(_logger, Mock.Of<IProfiler>()))
{
@@ -44,7 +44,13 @@ namespace Umbraco.Tests.Persistence.Repositories
private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository)
{
var templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
TemplateRepository tr;
return CreateRepository(unitOfWork, out contentTypeRepository, out tr);
}
private ContentRepository CreateRepository(IDatabaseUnitOfWork unitOfWork, out ContentTypeRepository contentTypeRepository, out TemplateRepository templateRepository)
{
templateRepository = new TemplateRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, Mock.Of<IFileSystem>(), Mock.Of<IFileSystem>(), Mock.Of<ITemplatesSection>());
var tagRepository = new TagRepository(unitOfWork, CacheHelper, Logger, SqlSyntax);
contentTypeRepository = new ContentTypeRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, templateRepository);
var repository = new ContentRepository(unitOfWork, CacheHelper, Logger, SqlSyntax, contentTypeRepository, templateRepository, tagRepository, Mock.Of<IContentSection>());
@@ -217,6 +223,37 @@ namespace Umbraco.Tests.Persistence.Repositories
// Assert
Assert.That(contentType.HasIdentity, Is.True);
Assert.That(textpage.HasIdentity, Is.True);
}
}
[Test]
public void Can_Perform_Add_With_Default_Template()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
TemplateRepository templateRepository;
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository, out templateRepository))
{
var template = new Template("hello", "hello");
templateRepository.AddOrUpdate(template);
unitOfWork.Commit();
ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage");
contentType.SetDefaultTemplate(template);
Content textpage = MockedContent.CreateSimpleContent(contentType);
// Act
contentTypeRepository.AddOrUpdate(contentType);
repository.AddOrUpdate(textpage);
unitOfWork.Commit();
// Assert
Assert.That(textpage.Template, Is.Not.Null);
Assert.That(textpage.Template, Is.EqualTo(contentType.DefaultTemplate));
}
}
@@ -327,6 +364,28 @@ namespace Umbraco.Tests.Persistence.Repositories
}
[Test]
public void Can_Update_With_Null_Template()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentTypeRepository contentTypeRepository;
using (var repository = CreateRepository(unitOfWork, out contentTypeRepository))
{
// Act
var content = repository.Get(NodeDto.NodeIdSeed + 2);
content.Template = null;
repository.AddOrUpdate(content);
unitOfWork.Commit();
var updatedContent = repository.Get(NodeDto.NodeIdSeed + 2);
// Assert
Assert.That(updatedContent.Template, Is.Null);
}
}
[Test]
public void Can_Perform_Delete_On_ContentRepository()
{
@@ -235,13 +235,16 @@ namespace Umbraco.Tests.Persistence.Repositories
using (var repository = CreateRepository(unitOfWork))
{
var contentType = repository.Get(NodeDto.NodeIdSeed + 1);
var childContentType = MockedContentTypes.CreateSimpleContentType("blah", "Blah", contentType, randomizeAliases:true);
repository.AddOrUpdate(childContentType);
unitOfWork.Commit();
// Act
contentType = repository.Get(contentType.Key);
var result = repository.Get(childContentType.Key);
// Assert
Assert.That(contentType, Is.Not.Null);
Assert.That(contentType.Id, Is.EqualTo(NodeDto.NodeIdSeed + 1));
Assert.That(result, Is.Not.Null);
Assert.That(result.Id, Is.EqualTo(childContentType.Id));
}
}
@@ -85,9 +85,41 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual("test.com", domain.DomainName);
Assert.AreEqual(content.Id, domain.RootContentId);
Assert.AreEqual(lang.Id, domain.LanguageId);
Assert.AreEqual(lang.IsoCode, domain.LanguageIsoCode);
}
}
[Test]
public void Can_Create_And_Get_By_Id_Empty_lang()
{
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
ContentType ct;
var contentId = CreateTestData("en-AU", out ct);
ContentRepository contentRepo;
LanguageRepository langRepo;
ContentTypeRepository contentTypeRepo;
using (var repo = CreateRepository(unitOfWork, out contentTypeRepo, out contentRepo, out langRepo))
{
var content = contentRepo.Get(contentId);
var domain = (IDomain)new UmbracoDomain("test.com") { RootContentId = content.Id };
repo.AddOrUpdate(domain);
unitOfWork.Commit();
//re-get
domain = repo.Get(domain.Id);
Assert.NotNull(domain);
Assert.IsTrue(domain.HasIdentity);
Assert.Greater(domain.Id, 0);
Assert.AreEqual("test.com", domain.DomainName);
Assert.AreEqual(content.Id, domain.RootContentId);
Assert.IsFalse(domain.LanguageId.HasValue);
}
}
[Test]
@@ -203,11 +235,13 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual("blah.com", domain.DomainName);
Assert.AreEqual(content2.Id, domain.RootContentId);
Assert.AreEqual(lang2.Id, domain.LanguageId);
Assert.AreEqual(lang2.IsoCode, domain.LanguageIsoCode);
}
}
[Test]
public void Exists()
{
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -63,7 +64,7 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual(0, found.First().AssigneeUserId);
Assert.AreEqual(false, found.First().Closed);
Assert.AreEqual("hello world", found.First().Comment);
Assert.GreaterOrEqual(found.First().CreateDate, created);
Assert.GreaterOrEqual(found.First().CreateDate.TruncateTo(DateTimeExtensions.DateTruncate.Second), created.TruncateTo(DateTimeExtensions.DateTruncate.Second));
Assert.AreEqual(-1, found.First().EntityId);
Assert.AreEqual(0, found.First().OwnerUserId);
Assert.AreEqual(true, found.First().HasIdentity);
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
@@ -489,77 +490,7 @@ namespace Umbraco.Tests.Persistence.Repositories
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
var parent = new Template("parent", "parent")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var child1 = new Template("child1", "child1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler1 = new Template("toddler1", "toddler1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler2 = new Template("toddler2", "toddler2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var baby1 = new Template("baby1", "baby1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var child2 = new Template("child2", "child2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler3 = new Template("toddler3", "toddler3")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler4 = new Template("toddler4", "toddler4")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var baby2 = new Template("baby2", "baby2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
child1.MasterTemplateAlias = parent.Alias;
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
child2.MasterTemplateAlias = parent.Alias;
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
toddler1.MasterTemplateAlias = child1.Alias;
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler2.MasterTemplateAlias = child1.Alias;
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler3.MasterTemplateAlias = child2.Alias;
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
toddler4.MasterTemplateAlias = child2.Alias;
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
baby1.MasterTemplateAlias = toddler2.Alias;
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
baby2.MasterTemplateAlias = toddler4.Alias;
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child1);
repository.AddOrUpdate(child2);
repository.AddOrUpdate(toddler1);
repository.AddOrUpdate(toddler2);
repository.AddOrUpdate(toddler3);
repository.AddOrUpdate(toddler4);
repository.AddOrUpdate(baby1);
repository.AddOrUpdate(baby2);
unitOfWork.Commit();
CreateHierarchy(repository, unitOfWork);
// Act
var rootNode = repository.GetTemplateNode("parent");
@@ -575,8 +506,102 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2"));
}
}
[Test]
public void Can_Get_All()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
var created = CreateHierarchy(repository, unitOfWork).ToArray();
// Act
var all = repository.GetAll();
var allByAlias = repository.GetAll("parent", "child2", "baby2", "notFound");
var allById = repository.GetAll(created[0].Id, created[2].Id, created[4].Id, created[5].Id, 999999);
// Assert
Assert.AreEqual(9, all.Count());
Assert.AreEqual(9, all.DistinctBy(x => x.Id).Count());
Assert.AreEqual(3, allByAlias.Count());
Assert.AreEqual(3, allByAlias.DistinctBy(x => x.Id).Count());
Assert.AreEqual(4, allById.Count());
Assert.AreEqual(4, allById.DistinctBy(x => x.Id).Count());
}
}
[Test]
public void Can_Get_Children()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
var created = CreateHierarchy(repository, unitOfWork).ToArray();
// Act
var childrenById = repository.GetChildren(created[1].Id);
var childrenByAlias = repository.GetChildren(created[1].Alias);
// Assert
Assert.AreEqual(2, childrenById.Count());
Assert.AreEqual(2, childrenById.DistinctBy(x => x.Id).Count());
Assert.AreEqual(2, childrenByAlias.Count());
Assert.AreEqual(2, childrenByAlias.DistinctBy(x => x.Id).Count());
}
}
[Test]
public void Can_Get_Children_At_Root()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
CreateHierarchy(repository, unitOfWork).ToArray();
// Act
var children = repository.GetChildren(-1);
// Assert
Assert.AreEqual(1, children.Count());
Assert.AreEqual(1, children.DistinctBy(x => x.Id).Count());
}
}
[Test]
public void Can_Get_Descendants()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider(Logger);
var unitOfWork = provider.GetUnitOfWork();
using (var repository = CreateRepository(unitOfWork))
{
var created = CreateHierarchy(repository, unitOfWork).ToArray();
// Act
var descendantsById = repository.GetDescendants(created[1].Id);
var descendantsByAlias = repository.GetDescendants(created[1].Alias);
// Assert
Assert.AreEqual(3, descendantsById.Count());
Assert.AreEqual(3, descendantsById.DistinctBy(x => x.Id).Count());
Assert.AreEqual(3, descendantsByAlias.Count());
Assert.AreEqual(3, descendantsByAlias.DistinctBy(x => x.Id).Count());
}
}
[Test]
@@ -717,5 +742,82 @@ namespace Umbraco.Tests.Persistence.Repositories
return stream;
}
private IEnumerable<ITemplate> CreateHierarchy(ITemplateRepository repository, IDatabaseUnitOfWork unitOfWork)
{
var parent = new Template("parent", "parent")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var child1 = new Template("child1", "child1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler1 = new Template("toddler1", "toddler1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler2 = new Template("toddler2", "toddler2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var baby1 = new Template("baby1", "baby1")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var child2 = new Template("child2", "child2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler3 = new Template("toddler3", "toddler3")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var toddler4 = new Template("toddler4", "toddler4")
{
Content = @"<%@ Master Language=""C#"" %>"
};
var baby2 = new Template("baby2", "baby2")
{
Content = @"<%@ Master Language=""C#"" %>"
};
child1.MasterTemplateAlias = parent.Alias;
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
child2.MasterTemplateAlias = parent.Alias;
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
toddler1.MasterTemplateAlias = child1.Alias;
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler2.MasterTemplateAlias = child1.Alias;
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler3.MasterTemplateAlias = child2.Alias;
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
toddler4.MasterTemplateAlias = child2.Alias;
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
baby1.MasterTemplateAlias = toddler2.Alias;
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
baby2.MasterTemplateAlias = toddler4.Alias;
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child1);
repository.AddOrUpdate(child2);
repository.AddOrUpdate(toddler1);
repository.AddOrUpdate(toddler2);
repository.AddOrUpdate(toddler3);
repository.AddOrUpdate(toddler4);
repository.AddOrUpdate(baby1);
repository.AddOrUpdate(baby2);
unitOfWork.Commit();
return new[] {parent, child1, child2, toddler1, toddler2, toddler3, toddler4, baby1, baby2};
}
}
}
@@ -454,6 +454,20 @@ namespace Umbraco.Tests.PublishedContent
}
[Test]
public void FirstChild()
{
var doc = GetNode(1173); // has child nodes
Assert.IsNotNull(doc.FirstChild());
Assert.IsNotNull(doc.FirstChild(x => true));
Assert.IsNotNull(doc.FirstChild<IPublishedContent>());
doc = GetNode(1175); // does not have child nodes
Assert.IsNull(doc.FirstChild());
Assert.IsNull(doc.FirstChild(x => true));
Assert.IsNull(doc.FirstChild<IPublishedContent>());
}
[Test]
public void HasProperty()
{
@@ -64,7 +64,7 @@ namespace Umbraco.Tests.Publishing
ServiceContext.ContentTypeService.GetContentType("umbTextpage"), "Sub Sub Sub", mandatorContent.Id);
ServiceContext.ContentService.Save(subContent, 0);
var strategy = new PublishingStrategy();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
//publish root and nodes at it's children level
var listToPublish = ServiceContext.ContentService.GetDescendants(_homePage.Id).Concat(new[] { _homePage });
@@ -91,8 +91,8 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
PublishingStrategy.Publishing +=PublishingStrategyPublishing;
@@ -118,8 +118,8 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
//publish root and nodes at it's children level
var result1 = strategy.Publish(_homePage, 0);
Assert.IsTrue(result1);
@@ -145,7 +145,7 @@ namespace Umbraco.Tests.Publishing
{
CreateTestData();
var strategy = new PublishingStrategy();
var strategy = new PublishingStrategy(new TransientMessagesFactory(), Logger);
//publish root and nodes at it's children level
var result1 = strategy.Publish(_homePage, 0);
@@ -13,7 +13,7 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001}
new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"}
});
}
@@ -22,12 +22,12 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001},
new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011},
new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012},
new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032}
new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"},
new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"},
new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"}
});
}
@@ -30,19 +30,22 @@ namespace Umbraco.Tests.Routing
{
Id = 1,
LanguageId = LangDeId,
RootContentId = 1001
RootContentId = 1001,
LanguageIsoCode = "de-DE"
},
new UmbracoDomain("domain1.com/en")
{
Id = 1,
LanguageId = LangEngId,
RootContentId = 10011
RootContentId = 10011,
LanguageIsoCode = "en-US"
},
new UmbracoDomain("domain1.com/fr")
{
Id = 1,
LanguageId = LangFrId,
RootContentId = 10012
RootContentId = 10012,
LanguageIsoCode = "fr-FR"
}
});
}
@@ -55,55 +58,64 @@ namespace Umbraco.Tests.Routing
{
Id = 1,
LanguageId = LangDeId,
RootContentId = 1001
RootContentId = 1001,
LanguageIsoCode = "de-DE"
},
new UmbracoDomain("domain1.com/en")
{
Id = 1,
LanguageId = LangEngId,
RootContentId = 10011
RootContentId = 10011,
LanguageIsoCode = "en-US"
},
new UmbracoDomain("domain1.com/fr")
{
Id = 1,
LanguageId = LangFrId,
RootContentId = 10012
RootContentId = 10012,
LanguageIsoCode = "fr-FR"
},
new UmbracoDomain("*1001")
{
Id = 1,
LanguageId = LangDeId,
RootContentId = 1001
RootContentId = 1001,
LanguageIsoCode = "de-DE"
},
new UmbracoDomain("*10011")
{
Id = 1,
LanguageId = LangCzId,
RootContentId = 10011
RootContentId = 10011,
LanguageIsoCode = "cs-CZ"
},
new UmbracoDomain("*100112")
{
Id = 1,
LanguageId = LangNlId,
RootContentId = 100112
RootContentId = 100112,
LanguageIsoCode = "nl-NL"
},
new UmbracoDomain("*1001122")
{
Id = 1,
LanguageId = LangDkId,
RootContentId = 1001122
RootContentId = 1001122,
LanguageIsoCode = "da-DK"
},
new UmbracoDomain("*10012")
{
Id = 1,
LanguageId = LangNlId,
RootContentId = 10012
RootContentId = 10012,
LanguageIsoCode = "nl-NL"
},
new UmbracoDomain("*10031")
{
Id = 1,
LanguageId = LangNlId,
RootContentId =10031
RootContentId =10031,
LanguageIsoCode = "nl-NL"
}
});
}
@@ -343,19 +355,22 @@ namespace Umbraco.Tests.Routing
{
Id = 1,
LanguageId = LangEngId,
RootContentId = 1001
RootContentId = 1001,
LanguageIsoCode = "en-US"
},
new UmbracoDomain("domain1.fr/")
{
Id = 1,
LanguageId = LangFrId,
RootContentId = 1001
RootContentId = 1001,
LanguageIsoCode = "fr-FR"
},
new UmbracoDomain("*100112")
{
Id = 1,
LanguageId = LangDeId,
RootContentId = 100112
RootContentId = 100112,
LanguageIsoCode = "de-DE"
}
});
@@ -28,7 +28,7 @@ namespace Umbraco.Tests.Routing
SetupDomainServiceMock(new[]
{
new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001}
new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"}
});
}
@@ -36,7 +36,7 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001}
new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"}
});
}
@@ -44,7 +44,7 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011}
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"}
});
}
@@ -52,12 +52,12 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001},
new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011},
new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012},
new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032}
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"},
new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"}
});
}
@@ -65,14 +65,14 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011},
new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011},
new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011},
new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012},
new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012},
new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032}
new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"},
new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"},
new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"},
new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"}
});
}
@@ -42,19 +42,10 @@ namespace Umbraco.Tests.Routing
domainService.Setup(service => service.GetAll(It.IsAny<bool>()))
.Returns((bool incWildcards) => new[]
{
new UmbracoDomain("domain1.com/"){Id = 1, LanguageId = 333, RootContentId = 1001},
new UmbracoDomain("domain1.com/en"){Id = 1, LanguageId = 334, RootContentId = 10011},
new UmbracoDomain("domain1.com/fr"){Id = 1, LanguageId = 335, RootContentId = 10012}
});
var langService = Mock.Get(svcCtx.LocalizationService);
//setup mock domain service
langService.Setup(service => service.GetLanguageById(LangDeId)).Returns(new Language("de-DE") {Id = LangDeId });
langService.Setup(service => service.GetLanguageById(LangEngId)).Returns(new Language("en-US") { Id = LangEngId });
langService.Setup(service => service.GetLanguageById(LangFrId)).Returns(new Language("fr-FR") { Id = LangFrId });
langService.Setup(service => service.GetLanguageById(LangCzId)).Returns(new Language("cs-CZ") { Id = LangCzId });
langService.Setup(service => service.GetLanguageById(LangNlId)).Returns(new Language("nl-NL") { Id = LangNlId });
langService.Setup(service => service.GetLanguageById(LangDkId)).Returns(new Language("da-DK") { Id = LangDkId });
new UmbracoDomain("domain1.com/"){Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"},
new UmbracoDomain("domain1.com/en"){Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"},
new UmbracoDomain("domain1.com/fr"){Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}
});
return svcCtx;
}
@@ -80,8 +80,8 @@ namespace Umbraco.Tests.Routing
{
SetupDomainServiceMock(new[]
{
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001},
new UmbracoDomain("http://domain2.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 10011}
new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"},
new UmbracoDomain("http://domain2.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}
});
}
@@ -50,14 +50,15 @@ namespace Umbraco.Tests.Services
//we need a new Database object for each thread.
var repositoryFactory = new RepositoryFactory(cacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings());
_uowProvider = new PerThreadPetaPocoUnitOfWorkProvider(_dbFactory);
ApplicationContext.Services = new ServiceContext(
var evtMsgs = new TransientMessagesFactory();
ApplicationContext.Services = new ServiceContext(
repositoryFactory,
_uowProvider,
new FileUnitOfWorkProvider(),
new PublishingStrategy(),
new PublishingStrategy(evtMsgs, Logger),
cacheHelper,
Logger,
new TransientMessagesFactory());
evtMsgs);
CreateTestData();
}
@@ -24,6 +24,18 @@ namespace Umbraco.Tests.Strings
ShortStringHelperResolver.Reset();
}
[TestCase("hello", "world", false)]
[TestCase("hello", "hello", true)]
[TestCase("hellohellohellohellohellohellohello", "hellohellohellohellohellohellohelloo", false)]
[TestCase("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohelloo", false)]
[TestCase("hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", true)]
public void String_To_Guid(string first, string second, bool result)
{
Console.WriteLine("First: " + first.ToGuid());
Console.WriteLine("Second: " + second.ToGuid());
Assert.AreEqual(result, first.ToGuid() == second.ToGuid());
}
[TestCase("alert('hello');", false)]
[TestCase("~/Test.js", true)]
[TestCase("../Test.js", true)]
@@ -76,11 +76,12 @@ namespace Umbraco.Tests.TestHelpers
var repositoryFactory = new RepositoryFactory(cacheHelper, Logger, SqlSyntax, SettingsForTests.GenerateMockSettings());
var evtMsgs = new TransientMessagesFactory();
_appContext = new ApplicationContext(
//assign the db context
new DatabaseContext(dbFactory, Logger, SqlSyntax, "System.Data.SqlServerCe.4.0"),
//assign the service context
new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(), cacheHelper, Logger, new TransientMessagesFactory()),
new ServiceContext(repositoryFactory, new PetaPocoUnitOfWorkProvider(dbFactory), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), cacheHelper, Logger, evtMsgs),
cacheHelper,
ProfilingLogger)
{
@@ -153,12 +153,13 @@ namespace Umbraco.Tests.TestHelpers
var sqlSyntax = new SqlCeSyntaxProvider();
var repoFactory = new RepositoryFactory(CacheHelper.CreateDisabledCacheHelper(), Logger, sqlSyntax, SettingsForTests.GenerateMockSettings());
var evtMsgs = new TransientMessagesFactory();
ApplicationContext.Current = new ApplicationContext(
//assign the db context
new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, Logger),
Logger, sqlSyntax, "System.Data.SqlServerCe.4.0"),
//assign the service context
new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(Logger), new FileUnitOfWorkProvider(), new PublishingStrategy(), CacheHelper, Logger, new TransientMessagesFactory()),
new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(Logger), new FileUnitOfWorkProvider(), new PublishingStrategy(evtMsgs, Logger), CacheHelper, Logger, evtMsgs),
CacheHelper,
ProfilingLogger)
{
+21 -23
View File
@@ -51,16 +51,16 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoMapper, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll</HintPath>
<Reference Include="AutoMapper, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
<Reference Include="AutoMapper.Net4, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Examine, Version=0.1.66.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.66.0\lib\Examine.dll</HintPath>
<Reference Include="Examine, Version=0.1.68.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.68.0\lib\Examine.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
@@ -79,9 +79,6 @@
<Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Mvc.FixedDisplayModes">
<HintPath>..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.1\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll</HintPath>
</Reference>
<Reference Include="MiniProfiler, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b44f9351044011a3, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll</HintPath>
@@ -124,9 +121,9 @@
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
@@ -139,25 +136,26 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=4.0.0.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.40804.0\lib\net40\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Services" />
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.30506.0\lib\net40\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -178,6 +176,7 @@
<Compile Include="Cache\CacheRefresherTests.cs" />
<Compile Include="Cache\DeepCloneRuntimeCacheProviderTests.cs" />
<Compile Include="Controllers\BackOfficeControllerUnitTests.cs" />
<Compile Include="DelegateExtensionsTests.cs" />
<Compile Include="Logging\AsyncRollingFileAppenderTest.cs" />
<Compile Include="Logging\DebugAppender.cs" />
<Compile Include="Logging\ParallelForwarderTest.cs" />
@@ -732,7 +731,6 @@
xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\" /Y /F /E /D</PostBuildEvent>
</PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<Import Project="..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets" Condition="Exists('..\packages\AutoMapper.3.3.1\tools\AutoMapper.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
+5 -6
View File
@@ -1,19 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="Examine" version="0.1.66.0" targetFramework="net45" />
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.68.0" targetFramework="net45" />
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.40804.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc.FixedDisplayModes" version="1.0.1" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.SelfHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="MiniProfiler" version="2.1.0" targetFramework="net45" />
<package id="Moq" version="4.1.1309.0919" targetFramework="net45" />
-1
View File
@@ -25,7 +25,6 @@
"jquery-ui": "1.10.3",
"angular-dynamic-locale": "~0.1.27",
"tinymce": "~4.1.10",
"bootstrap-tabdrop": "~1.0.0",
"codemirror": "~5.3.0"
}
}
+3 -6
View File
@@ -418,7 +418,8 @@ module.exports = function (grunt) {
ignorePackages: ['blueimp-canvas-to-blob', 'blueimp-tmpl', 'bootstrap'],
packageSpecific: {
'typeahead.js': {
keepExpandedHierarchy: false
keepExpandedHierarchy: false,
files: ['dist/typeahead.bundle.min.js']
},
'underscore': {
files: ['underscore-min.js', 'underscore-min.map']
@@ -455,11 +456,7 @@ module.exports = function (grunt) {
},
'angular-dynamic-locale': {
files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map']
},
'bootstrap-tabdrop': {
keepExpandedHierarchy: false,
files: ['build/js/bootstrap-tabdrop.min.js']
},
},
'codemirror': {
files: [
'lib/codemirror.js',
@@ -0,0 +1,62 @@
bootstrap-tabdrop
=================
*****************************************************************
NOTE: THIS IS A CUSTOM FIXED VERSION!!!!!!!!!!!!!!!!!!!!!!
- THE ORIGINAL HAS A MEMORY LEAK, SO WE'VE HAD TO EMBED THIS
INTO THE CORE WITH THE FIX
--- UMBRACO CORE TEAM
*****************************************************************
A dropdown tab tool for @twitter bootstrap forked from Stefan Petre's (of eyecon.ro),
The dropdown tab appears when your tabs do not all fit in the same row.
Original site and examples: http://www.eyecon.ro/bootstrap-tabdrop/
Added functionality: Displays the text of an active tab selected from the dropdown list instead of the text option on the dropdown tab.
## Requirements
* [Bootstrap](http://twitter.github.com/bootstrap/) 2.0.4+
* [jQuery](http://jquery.com/) 1.7.1+
## Example
No additional HTML needed - the script adds it when the dropdown tab is needed.
Using bootstrap-tabdrop.js
Call the tab drop via javascript on .nav-tabs and .nav-pills:
```js
$('.nav-pills, .nav-tabs').tabdrop()
```
### Options
#### text
Type: string
Default: icon
```html
<i class="icon-align-justify"></i>
```
To change the default value, call
```javascript
.tabdrop({text: "your text here"});
```
when initalizing the tabdrop. The displayed value will change when a tab is selected from the dropdown list.
### Methods
```js
.tabdrop(options)
```
Initializes an tab drop.
```js
.tabdrop('layout')
```
Checks if the tabs fit in one single row.
@@ -0,0 +1,132 @@
/* =========================================================
* bootstrap-tabdrop.js
* http://www.eyecon.ro/bootstrap-tabdrop
* =========================================================
* Copyright 2012 Stefan Petre
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
/*****************************************************************
* NOTE: THIS IS A CUSTOM FIXED VERSION!!!!!!!!!!!!!!!!!!!!!!
* - THE ORIGINAL HAS A MEMORY LEAK, SO WE'VE HAD TO EMBED THIS
* INTO THE CORE WITH THE FIX
*
* --- UMBRACO CORE TEAM
*****************************************************************/
!function( $ ) {
var WinReszier = (function(){
var registered = [];
var inited = false;
var timer;
var resize = function(ev) {
clearTimeout(timer);
timer = setTimeout(notify, 100);
};
var notify = function() {
for(var i=0, cnt=registered.length; i<cnt; i++) {
registered[i].apply();
}
};
return {
register: function(fn) {
registered.push(fn);
if (inited === false) {
$(window).bind('resize', resize);
inited = true;
}
},
unregister: function(fn) {
var index = registered.indexOf(fn);
if (index > -1) {
registered.splice(index, 1);
}
}
};
}());
var TabDrop = function(element, options) {
this.element = $(element);
this.dropdown = $('<li class="dropdown hide pull-right tabdrop">' +
'<a class="dropdown-toggle" data-toggle="dropdown" href="#">' +
options.text + ' <b class="caret"></b></a>' +
'<ul class="dropdown-menu"></ul></li>').prependTo(this.element);
if (this.element.parent().is('.tabs-below')) {
this.dropdown.addClass('dropup');
}
this.resizeCallback = $.proxy(this.layout, this);
WinReszier.register(this.resizeCallback);
this.layout();
};
TabDrop.prototype = {
constructor: TabDrop,
layout: function() {
var collection = [];
this.dropdown.removeClass('hide');
this.element
.append(this.dropdown.find('li'))
.find('>li')
.not('.tabdrop')
.each(function(){
if(this.offsetTop > 0) {
collection.push(this);
}
});
if (collection.length > 0) {
collection = $(collection);
this.dropdown
.find('ul')
.empty()
.append(collection);
if (this.dropdown.find('.active').length == 1) {
this.dropdown.addClass('active');
} else {
this.dropdown.removeClass('active');
}
} else {
this.dropdown.addClass('hide');
}
},
destroy: function() {
this.dropdown.html();
WinReszier.unregister(this.resizeCallback);
}
};
$.fn.tabdrop = function ( option ) {
return this.each(function () {
var $this = $(this),
data = $this.data('tabdrop'),
options = typeof option === 'object' && option;
if (!data) {
$this.data('tabdrop', (data = new TabDrop(this, $.extend({},
$.fn.tabdrop.defaults,options))));
}
if (typeof option == 'string') {
data[option]();
}
});
};
$.fn.tabdrop.defaults = {
text: '<i class="icon-align-justify"></i>'
};
$.fn.tabdrop.Constructor = TabDrop;
}(window.jQuery);
@@ -1,32 +1,41 @@
angular.module("umbraco.directives")
.directive('umbAutoResize', function($timeout) {
return {
require: "^?umbTabs",
link: function(scope, element, attr, tabsCtrl) {
var domEl = element[0];
var update = function(force) {
if (force === true) {
element.height(0);
}
return function(scope, element, attr){
var domEl = element[0];
var update = function(force) {
if (domEl.scrollHeight !== domEl.clientHeight) {
element.height(domEl.scrollHeight);
}
};
var blur = function() {
update(true);
};
if(force === true){
element.height(0);
element.bind('keyup keydown keypress change', update);
element.bind('blur', blur);
$timeout(function() {
update(true);
}, 200);
//listen for tab changes
if (tabsCtrl != null) {
tabsCtrl.onTabShown(function(args) {
update();
});
}
if(domEl.scrollHeight !== domEl.clientHeight){
element.height(domEl.scrollHeight);
}
};
element.bind('keyup keydown keypress change', update);
element.bind('blur', function(){ update(true); });
$timeout(function() {
update(true);
}, 200);
//I hate bootstrap tabs
$('a[data-toggle="tab"]').on('shown', update);
scope.$on('$destroy', function() {
$('a[data-toggle="tab"]').unbind("shown", update);
});
};
});
scope.$on('$destroy', function () {
element.unbind('keyup keydown keypress change', update);
element.unbind('blur', blur);
});
}
};
});
@@ -1,5 +1,13 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbHeader
* @restrict E
* @function
* @description
* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead
**/
angular.module("umbraco.directives")
.directive('umbHeader', function($parse, $timeout){
.directive('umbHeader', function ($parse, $timeout) {
return {
restrict: 'E',
replace: true,
@@ -11,50 +19,13 @@ angular.module("umbraco.directives")
tabs: "="
},
link: function (scope, iElement, iAttrs) {
var maxTabs = 4;
function collectFromDom(activeTab){
var $panes = $('div.tab-content');
angular.forEach($panes.find('.tab-pane'), function (pane, index) {
var $this = angular.element(pane);
var id = $this.attr("rel");
var label = $this.attr("label");
var tab = {id: id, label: label, active: false};
if(!activeTab){
tab.active = true;
activeTab = tab;
}
if ($this.attr("rel") === String(activeTab.id)) {
$this.addClass('active');
}
else {
$this.removeClass('active');
}
if(label){
scope.visibleTabs.push(tab);
}
});
$('.nav-pills, .nav-tabs').tabdrop();
}
scope.showTabs = iAttrs.tabs ? true : false;
scope.visibleTabs = [];
scope.overflownTabs = [];
$timeout(function () {
collectFromDom(undefined);
}, 500);
//when the tabs change, we need to hack the planet a bit and force the first tab content to be active,
//unfortunately twitter bootstrap tabs is not playing perfectly with angular.
scope.$watch("tabs", function (newValue, oldValue) {
//since tabs are loaded async, we need to put a watch on them to determine
// when they are loaded, then we can close the watch
var tabWatch = scope.$watch("tabs", function (newValue, oldValue) {
angular.forEach(newValue, function(val, index){
var tab = {id: val.id, label: val.label};
@@ -64,16 +35,25 @@ angular.module("umbraco.directives")
//don't process if we cannot or have already done so
if (!newValue) {return;}
if (!newValue.length || newValue.length === 0){return;}
var activeTab = _.find(newValue, function (item) {
return item.active;
});
//we need to do a timeout here so that the current sync operation can complete
// and update the UI, then this will fire and the UI elements will be available.
$timeout(function () {
collectFromDom(activeTab);
}, 500);
//use bootstrap tabs API to show the first one
iElement.find(".nav-tabs a:first").tab('show');
//enable the tab drop
iElement.find('.nav-pills, .nav-tabs').tabdrop();
//ensure to destroy tabdrop (unbinds window resize listeners)
scope.$on('$destroy', function () {
iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy");
});
//stop watching now
tabWatch();
}, 200);
});
}
@@ -4,11 +4,11 @@
* @restrict E
**/
angular.module("umbraco.directives")
.directive('umbTab', function(){
return {
.directive('umbTab', function ($parse, $timeout) {
return {
restrict: 'E',
replace: true,
transclude: 'true',
templateUrl: 'views/directives/umb-tab.html'
};
replace: true,
transclude: 'true',
templateUrl: 'views/directives/umb-tab.html'
};
});
@@ -0,0 +1,45 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbTabs
* @restrict A
* @description Used to bind to bootstrap tab events so that sub directives can use this API to listen to tab changes
**/
angular.module("umbraco.directives")
.directive('umbTabs', function () {
return {
restrict: 'A',
controller: function ($scope, $element, $attrs) {
var callbacks = [];
this.onTabShown = function(cb) {
callbacks.push(cb);
};
function tabShown(event) {
var curr = $(event.target); // active tab
var prev = $(event.relatedTarget); // previous tab
for (var c in callbacks) {
callbacks[c].apply(this, [{current: curr, previous: prev}]);
}
}
//NOTE: it MUST be done this way - binding to an ancestor element that exists
// in the DOM to bind to the dynamic elements that will be created.
// It would be nicer to create this event handler as a directive for which child
// directives can attach to.
$element.on('shown', '.nav-tabs a', tabShown);
//ensure to unregister
$scope.$on('$destroy', function () {
$element.off('shown', '.nav-tabs a', tabShown);
for (var c in callbacks) {
delete callbacks[c];
}
callbacks = null;
});
}
};
});
@@ -64,6 +64,13 @@ function umbImageFolder($rootScope, assetsService, $timeout, $log, umbRequestHel
found.completed = true;
}
//Show notifications!!!!
if (data.result && data.result.notifications && angular.isArray(data.result.notifications)) {
for (var n = 0; n < data.result.notifications.length; n++) {
notificationsService.showNotification(data.result.notifications[n]);
}
}
//when none are left resync everything
var remaining = _.filter(scope.files, function (file) { return file.completed !== true; });
if (remaining.length === 0) {
@@ -7,7 +7,7 @@
* Handles how the navigation responds to window resizing and controls how the draggable resize panel works
**/
angular.module("umbraco.directives")
.directive('navResize', function (appState, eventsService) {
.directive('navResize', function (appState, eventsService, windowResizeListener) {
return {
restrict: 'A',
link: function (scope, element, attrs, ctrl) {
@@ -22,22 +22,28 @@ angular.module("umbraco.directives")
function enableResize() {
//only enable when the size is correct and it's not already enabled
if (!resizeEnabled && appState.getGlobalState("isTablet") === false) {
element.find(".navigation-inner-container").resizable(
element.resizable(
{
containment: $("#mainwrapper"),
autoHide: true,
handles: 'e',
handles: "e",
alsoResize: ".navigation-inner-container",
resize: function(e, ui) {
var wrapper = $("#mainwrapper");
var contentPanel = $("#leftcolumn").next();
var contentPanel = $("#contentwrapper");
var apps = $("#applications");
var navOffeset = ui.element.next();
var bottomBar = contentPanel.find(".umb-bottom-bar");
var navOffeset = $("#navOffset");
var leftPanelWidth = ui.element.width() + apps.width();
contentPanel.css({ left: leftPanelWidth });
bottomBar.css({ left: leftPanelWidth });
navOffeset.css({ "margin-left": ui.element.outerWidth() });
},
stop: function(e, ui) {
stop: function (e, ui) {
}
});
@@ -47,14 +53,14 @@ angular.module("umbraco.directives")
function resetResize() {
if (resizeEnabled) {
var navInnerContainer = element.find(".navigation-inner-container");
//kill the resize
navInnerContainer.resizable("destroy");
element.resizable("destroy");
element.css("width", "");
var navInnerContainer = element.find(".navigation-inner-container");
navInnerContainer.css("width", "");
$("#leftcolumn").next().css("left", "");
navInnerContainer.next().css("margin-left", "");
$("#contentwrapper").css("left", "");
$("#navOffset").css("margin-left", "");
resizeEnabled = false;
}
@@ -74,16 +80,17 @@ angular.module("umbraco.directives")
}
}));
$(window).bind("resize", function () {
var resizeCallback = function(size) {
//set the global app state
appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
appState.setGlobalState("isTablet", (size.width <= minScreenSize));
setTreeMode();
});
};
windowResizeListener.register(resizeCallback);
//ensure to unregister from all events and kill jquery plugins
scope.$on('$destroy', function () {
windowResizeListener.unregister(resizeCallback);
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
@@ -91,7 +98,6 @@ angular.module("umbraco.directives")
navInnerContainer.resizable("destroy");
});
//init
//set the global app state
appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
@@ -37,8 +37,7 @@ angular.module("umbraco.directives")
template: '<li ng-class="{\'current\': (node == currentNode)}" on-right-click="altSelect(node, $event)">' +
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" >' +
//NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
'<ins style="width:18px;"></ins>' +
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded, \'icon-navigation-down\': node.expanded}" ng-click="load(node)"></ins>' +
'<i class="icon umb-tree-icon sprTree"></i>' +
'<a ng-click="select(node, $event)"></a>' +
@@ -87,7 +86,7 @@ angular.module("umbraco.directives")
icon.addClass(node.cssClass);
icon.attr("title", node.routePath);
element.find("a:first").html(node.name);
element.find("a:first").text(node.name);
if (!node.menuUrl) {
element.find("a.umb-options").remove();
@@ -5,21 +5,31 @@ angular.module("umbraco.directives")
link: function (scope, element, attrs, ctrl) {
var active = false;
var fn = $parse(attrs.delayedMouseleave);
element.on("mouseleave", function(event) {
var callback = function() {
fn(scope, {$event:event});
function mouseLeave(event) {
var callback = function () {
fn(scope, { $event: event });
};
active = false;
$timeout(function(){
if(active === false){
$timeout(function () {
if (active === false) {
scope.$apply(callback);
}
}, 650);
});
}
element.on("mouseenter", function(event, args){
function mouseEnter(event, args){
active = true;
}
element.on("mouseleave", mouseLeave);
element.on("mouseenter", mouseEnter);
//unbind!!
scope.$on('$destroy', function () {
element.off("mouseleave", mouseLeave);
element.off("mouseenter", mouseEnter);
});
}
};
@@ -1,49 +1,80 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbPanel
* @restrict E
* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor
* exceeds the height of the window
**/
angular.module("umbraco.directives.html")
.directive('detectFold', function($timeout, $log){
return {
.directive('detectFold', function ($timeout, $log, windowResizeListener) {
return {
require: "^?umbTabs",
restrict: 'A',
link: function (scope, el, attrs) {
var state = false,
parent = $(".umb-panel-body"),
winHeight = $(window).height(),
calculate = _.throttle(function(){
if(el && el.is(":visible") && !el.hasClass("umb-bottom-bar")){
//var parent = el.parent();
var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
//var belowFold = (el.offset().top + el.height()) > winHeight;
if(hasOverflow){
el.addClass("umb-bottom-bar");
}
}
return state;
}, 1000);
link: function (scope, el, attrs, tabsCtrl) {
scope.$watch(calculate, function(newVal, oldVal) {
if(newVal !== oldVal){
if(newVal){
el.addClass("umb-bottom-bar");
}else{
el.removeClass("umb-bottom-bar");
}
}
});
var firstRun = false;
var parent = $(".umb-panel-body");
var winHeight = $(window).height();
var calculate = function () {
if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) {
$(window).bind("resize", function () {
winHeight = $(window).height();
el.removeClass("umb-bottom-bar");
state = false;
calculate();
});
//now that the element is visible, set the flag in a couple of seconds,
// this will ensure that loading time of a current tab get's completed and that
// we eventually stop watching to save on CPU time
$timeout(function() {
firstRun = true;
}, 4000);
$('a[data-toggle="tab"]').on('shown', function (e) {
calculate();
});
//var parent = el.parent();
var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
//var belowFold = (el.offset().top + el.height()) > winHeight;
if (hasOverflow) {
el.addClass("umb-bottom-bar");
//I wish we didn't have to put this logic here but unfortunately we
// do. This needs to calculate the left offest to place the bottom bar
// depending on if the left column splitter has been moved by the user
// (based on the nav-resize directive)
var wrapper = $("#mainwrapper");
var contentPanel = $("#leftcolumn").next();
var contentPanelLeftPx = contentPanel.css("left");
el.css({ left: contentPanelLeftPx });
}
}
return firstRun;
};
var resizeCallback = function(size) {
winHeight = size.height;
el.removeClass("umb-bottom-bar");
calculate();
};
windowResizeListener.register(resizeCallback);
//Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute
// the watcher since it will be recalculated when the tab changes!
if (el.closest(".umb-tab-pane").index() === 0) {
//run a watcher to ensure that the calculation occurs until it's firstRun but ensure
// the calculations are throttled to save a bit of CPU
var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) {
if (newVal !== oldVal) {
listener();
}
});
}
//listen for tab changes
if (tabsCtrl != null) {
tabsCtrl.onTabShown(function (args) {
calculate();
});
}
//ensure to unregister
scope.$on('$destroy', function() {
windowResizeListener.unregister(resizeCallback);
});
}
};
});
@@ -242,18 +242,19 @@ angular.module('umbraco.services')
_.each(assets, function (asset) {
LazyLoad.js(appendRnd(asset.path), function () {
asset.state = "loaded";
if (!scope) {
asset.state = "loaded";
asset.deferred.resolve(true);
} else {
asset.state = "loaded";
}
else {
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
});
} else {
}
else {
//return and resolve
var deferred = $q.defer();
promise = deferred.promise;
@@ -55,6 +55,12 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
err: err,
rebindCallback: self.reBindChangedProperties(args.content, err.data)
});
//show any notifications
if (angular.isArray(err.data.notifications)) {
for (var i = 0; i < err.data.notifications.length; i++) {
notificationsService.showNotification(err.data.notifications[i]);
}
}
args.scope.busy = false;
deferred.reject(err);
});
@@ -466,13 +472,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
* @description
* Changes the location to be editing the newly created content after create was successful.
* We need to decide if we need to redirect to edito mode or if we will remain in create mode.
* We will only need to maintain create mode if we have not fulfilled the basic requirements for creating an entity which is at least having a name.
* We will only need to maintain create mode if we have not fulfilled the basic requirements for creating an entity which is at least having a name and ID
*/
redirectToCreatedContent: function (id, modelState) {
//only continue if we are currently in create mode and if there is no 'Name' modelstate errors
// since we need at least a name to create content.
if ($routeParams.create && (!modelState || !modelState["Name"])) {
if ($routeParams.create && (id > 0 && (!modelState || !modelState["Name"]))) {
//need to change the location to not be in 'create' mode. Currently the route will be something like:
// /belle/#/content/edit/1234?doctype=newsArticle&create=true
@@ -248,7 +248,7 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
//This is a bit of a hack to check if the error is due to a file being uploaded that is too large,
// we have to just check for the existence of a string value but currently that is the best way to
// do this since it's very hacky/difficult to catch this on the server
if (data.indexOf("Maximum request length exceeded") >= 0) {
if (typeof data !== "undefined" && typeof data.indexOf === "function" && data.indexOf("Maximum request length exceeded") >= 0) {
notificationsService.error("Server error", "The uploaded file was too large, check with your site administrator to adjust the maximum size allowed");
}
else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) {
@@ -0,0 +1,67 @@
/**
* @ngdoc service
* @name umbraco.services.windowResizeListener
* @function
*
* @description
* A single window resize listener... we don't want to have more than one in theory to ensure that
* there aren't too many events raised. This will debounce the event with 100 ms intervals and force
* a $rootScope.$apply when changed and notify all listeners
*
*/
function windowResizeListener($rootScope) {
var WinReszier = (function () {
var registered = [];
var inited = false;
var resize = _.debounce(function(ev) {
notify();
}, 100);
var notify = function () {
var h = $(window).height();
var w = $(window).width();
//execute all registrations inside of a digest
$rootScope.$apply(function() {
for (var i = 0, cnt = registered.length; i < cnt; i++) {
registered[i].apply($(window), [{ width: w, height: h }]);
}
});
};
return {
register: function (fn) {
registered.push(fn);
if (inited === false) {
$(window).bind('resize', resize);
inited = true;
}
},
unregister: function (fn) {
var index = registered.indexOf(fn);
if (index > -1) {
registered.splice(index, 1);
}
}
};
}());
return {
/**
* Register a callback for resizing
* @param {Function} cb
*/
register: function (cb) {
WinReszier.register(cb);
},
/**
* Removes a registered callback
* @param {Function} cb
*/
unregister: function(cb) {
WinReszier.unregister(cb);
}
};
}
angular.module('umbraco.services').factory('windowResizeListener', windowResizeListener);

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