diff --git a/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs b/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs
new file mode 100644
index 0000000000..95bb1fd92c
--- /dev/null
+++ b/src/Umbraco.Web.Common/DependencyInjection/StaticServiceProvider.cs
@@ -0,0 +1,25 @@
+using System;
+using System.ComponentModel;
+
+namespace Umbraco.Cms.Web.Common.DependencyInjection
+{
+ ///
+ /// INTERNAL Service locator. Should only be used if no other ways exist.
+ ///
+ ///
+ /// It is created with only two goals in mind
+ /// 1) Continue to have the same extension methods on IPublishedContent and IPublishedElement as in V8. To make migration easier.
+ /// 2) To have a tool to avoid breaking changes in minor versions. All methods using this should in theory be obsolete.
+ ///
+ /// Keep in mind, everything this is used, the code becomes basically untestable.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal static class StaticServiceProvider
+ {
+ ///
+ /// The service locator.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal static IServiceProvider Instance { get; set; }
+ }
+}
diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs
index d843bfb4aa..f8ca73e283 100644
--- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs
+++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoStartupFilter.cs
@@ -19,6 +19,7 @@ namespace Umbraco.Cms.Web.Common.DependencyInjection
public Action Configure(Action next) =>
app =>
{
+ StaticServiceProvider.Instance = app.ApplicationServices;
_options.Value.PreUmbracoPipeline(app);
app.UseUmbraco();
diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs
new file mode 100644
index 0000000000..92456813f8
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedContentExtensions.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using Examine;
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Core;
+using Umbraco.Cms.Core.Models.PublishedContent;
+using Umbraco.Cms.Core.Routing;
+using Umbraco.Cms.Core.Services;
+using Umbraco.Cms.Core.Web;
+using Umbraco.Cms.Web.Common.DependencyInjection;
+using Umbraco.Extensions;
+
+namespace Umbraco.FriendlyExtensions
+{
+ public static class FriendlyPublishedContentExtensions
+ {
+ private static IVariationContextAccessor VariationContextAccessor { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static IPublishedModelFactory PublishedModelFactory { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static IPublishedUrlProvider PublishedUrlProvider { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static IUserService UserService { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static IUmbracoContextAccessor UmbracoContextAccessor { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static ISiteDomainHelper SiteDomainHelper { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ private static IExamineManager ExamineManager { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+
+ ///
+ /// Creates a strongly typed published content model for an internal published content.
+ ///
+ /// The internal published content.
+ /// The strongly typed published content model.
+ public static IPublishedContent CreateModel(
+ this IPublishedContent content)
+ => content.CreateModel(PublishedModelFactory);
+
+ ///
+ /// Gets the name of the content item.
+ ///
+ /// The content item.
+ /// The specific culture to get the name for. If null is used the current culture is used (Default is null).
+ public static string Name(
+ this IPublishedContent content,
+ string culture = null)
+ => content.Name(VariationContextAccessor, culture);
+
+ ///
+ /// Gets the URL segment of the content item.
+ ///
+ /// The content item.
+ /// The specific culture to get the URL segment for. If null is used the current culture is used (Default is null).
+ public static string UrlSegment(
+ this IPublishedContent content,
+ string culture = null)
+ => content.UrlSegment(VariationContextAccessor, culture);
+
+ ///
+ /// Gets the url for a media.
+ ///
+ /// The content item.
+ /// The culture (use current culture by default).
+ /// The url mode (use site configuration by default).
+ /// The alias of the property (use 'umbracoFile' by default).
+ /// The url for the media.
+ ///
+ /// The value of this property is contextual. It depends on the 'current' request uri,
+ /// if any. In addition, when the content type is multi-lingual, this is the url for the
+ /// specified culture. Otherwise, it is the invariant url.
+ ///
+ public static string MediaUrl(
+ this IPublishedContent content,
+ string culture = null,
+ UrlMode mode = UrlMode.Default,
+ string propertyAlias = Constants.Conventions.Media.File)
+ => content.MediaUrl(PublishedUrlProvider, culture, mode, propertyAlias);
+
+ ///
+ /// Gets the name of the content item creator.
+ ///
+ /// The content item.
+ public static string CreatorName(this IPublishedContent content) =>
+ content.CreatorName(UserService);
+
+ ///
+ /// Gets the name of the content item writer.
+ ///
+ /// The content item.
+ public static string WriterName(this IPublishedContent content) =>
+ content.WriterName(UserService);
+
+ ///
+ /// Gets the culture assigned to a document by domains, in the context of a current Uri.
+ ///
+ /// The document.
+ /// An optional current Uri.
+ /// The culture assigned to the document by domains.
+ ///
+ /// In 1:1 multilingual setup, a document contains several cultures (there is not
+ /// one document per culture), and domains, withing the context of a current Uri, assign
+ /// a culture to that document.
+ ///
+ public static string GetCultureFromDomains(
+ this IPublishedContent content,
+ Uri current = null)
+ => content.GetCultureFromDomains(UmbracoContextAccessor, SiteDomainHelper, current);
+
+
+ public static IEnumerable SearchDescendants(
+ this IPublishedContent content,
+ string term,
+ string indexName = null)
+ => content.SearchDescendants(ExamineManager, UmbracoContextAccessor, term, indexName);
+
+
+ public static IEnumerable SearchChildren(
+ this IPublishedContent content,
+ string term,
+ string indexName = null)
+ => content.SearchChildren(ExamineManager, UmbracoContextAccessor, term, indexName);
+
+
+ }
+}
diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyPublishedElementExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedElementExtensions.cs
new file mode 100644
index 0000000000..d99220e33b
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/FriendlyPublishedElementExtensions.cs
@@ -0,0 +1,75 @@
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Cms.Core.Models.PublishedContent;
+using Umbraco.Cms.Web.Common.DependencyInjection;
+using Umbraco.Extensions;
+
+namespace Umbraco.FriendlyExtensions
+{
+ public static class FriendlyPublishedElementExtensions
+ {
+ private static IPublishedValueFallback PublishedValueFallback { get; } =
+ StaticServiceProvider.Instance.GetRequiredService();
+
+ ///
+ /// Gets the value of a content's property identified by its alias.
+ ///
+ /// The content.
+ /// The property alias.
+ /// The variation language.
+ /// The variation segment.
+ /// Optional fallback strategy.
+ /// The default value.
+ /// The value of the content's property identified by the alias, if it exists, otherwise a default value.
+ ///
+ /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
+ /// If no property with the specified alias exists, or if the property has no value, returns .
+ /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
+ /// The alias is case-insensitive.
+ ///
+ public static object Value(
+ this IPublishedElement content,
+ string alias,
+ string culture = null,
+ string segment = null,
+ Fallback fallback = default,
+ object defaultValue = default)
+ => content.Value(PublishedValueFallback, alias, culture, segment, fallback, defaultValue);
+
+ ///
+ /// Gets the value of a content's property identified by its alias, converted to a specified type.
+ ///
+ /// The target property type.
+ /// The content.
+ /// The property alias.
+ /// The variation language.
+ /// The variation segment.
+ /// Optional fallback strategy.
+ /// The default value.
+ /// The value of the content's property identified by the alias, converted to the specified type.
+ ///
+ /// The value comes from IPublishedProperty field Value ie it is suitable for use when rendering content.
+ /// If no property with the specified alias exists, or if the property has no value, or if it could not be converted, returns default(T).
+ /// If eg a numeric property wants to default to 0 when value source is empty, this has to be done in the converter.
+ /// The alias is case-insensitive.
+ ///
+ public static T Value(
+ this IPublishedElement content,
+ string alias,
+ string culture = null,
+ string segment = null,
+ Fallback fallback = default,
+ T defaultValue = default)
+ => content.Value(PublishedValueFallback, alias, culture, segment, fallback, defaultValue);
+
+ ///
+ /// Gets a value indicating whether the content is visible.
+ ///
+ /// The content.
+ /// A value indicating whether the content is visible.
+ /// A content is not visible if it has an umbracoNaviHide property with a value of "1". Otherwise,
+ /// the content is visible.
+ public static bool IsVisible(this IPublishedElement content) => content.IsVisible(PublishedValueFallback);
+
+
+ }
+}
diff --git a/src/Umbraco.Web.Website/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs
similarity index 99%
rename from src/Umbraco.Web.Website/Extensions/PublishedContentExtensions.cs
rename to src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs
index c33d5e6ef3..c02e609372 100644
--- a/src/Umbraco.Web.Website/Extensions/PublishedContentExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/PublishedContentExtensions.cs
@@ -3,12 +3,12 @@ using System.Collections.Generic;
using System.Web;
using Examine;
using Microsoft.AspNetCore.Html;
+using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.Examine;
-using Constants = Umbraco.Cms.Core.Constants;
namespace Umbraco.Extensions
{