Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solves #37 - Support for Umbraco 8 #38

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ artifacts/
src/packages/
node_modules/
releases/files/
src/.vs/Our.Umbraco.OpeningHours/v16/
src/.vs/Our.Umbraco.OpeningHours/config/
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
angular.module("umbraco").controller("OpeningHours.Controllers.OpeningHoursController", function ($scope) {

// Double-check that we're getting the model back in the expected format
// (ie.Nested Content might init the property editor with a string)
if (typeof ($scope.model.value) === 'string') {
if ($scope.model.value === '') {
$scope.model.value = {};
} else {
$scope.model.value = JSON.parse($scope.model.value);
}
}

function parseBoolean(str) {
str = str + '';
return str == '1' || str == 'true';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
templateUrl: '/App_Plugins/OpeningHours/Views/Directives/Weekdays.html',
link: function (scope) {

function parseBoolean(str) {
str = str + '';


function parseBoolean(str) {
str = str + '';
return str == '1' || str == 'true';
}

Expand All @@ -22,14 +24,20 @@
scope.weekdays = [];
scope.times = ['00:00', '00:05', '00:10', '00:15', '00:20', '00:25', '00:30', '00:35', '00:40', '00:45', '00:50', '00:55', '01:00', '01:05', '01:10', '01:15', '01:20', '01:25', '01:30', '01:35', '01:40', '01:45', '01:50', '01:55', '02:00', '02:05', '02:10', '02:15', '02:20', '02:25', '02:30', '02:35', '02:40', '02:45', '02:50', '02:55', '03:00', '03:05', '03:10', '03:15', '03:20', '03:25', '03:30', '03:35', '03:40', '03:45', '03:50', '03:55', '04:00', '04:05', '04:10', '04:15', '04:20', '04:25', '04:30', '04:35', '04:40', '04:45', '04:50', '04:55', '05:00', '05:05', '05:10', '05:15', '05:20', '05:25', '05:30', '05:35', '05:40', '05:45', '05:50', '05:55', '06:00', '06:05', '06:10', '06:15', '06:20', '06:25', '06:30', '06:35', '06:40', '06:45', '06:50', '06:55', '07:00', '07:05', '07:10', '07:15', '07:20', '07:25', '07:30', '07:35', '07:40', '07:45', '07:50', '07:55', '08:00', '08:05', '08:10', '08:15', '08:20', '08:25', '08:30', '08:35', '08:40', '08:45', '08:50', '08:55', '09:00', '09:05', '09:10', '09:15', '09:20', '09:25', '09:30', '09:35', '09:40', '09:45', '09:50', '09:55', '10:00', '10:05', '10:10', '10:15', '10:20', '10:25', '10:30', '10:35', '10:40', '10:45', '10:50', '10:55', '11:00', '11:05', '11:10', '11:15', '11:20', '11:25', '11:30', '11:35', '11:40', '11:45', '11:50', '11:55', '12:00', '12:05', '12:10', '12:15', '12:20', '12:25', '12:30', '12:35', '12:40', '12:45', '12:50', '12:55', '13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55', '14:00', '14:05', '14:10', '14:15', '14:20', '14:25', '14:30', '14:35', '14:40', '14:45', '14:50', '14:55', '15:00', '15:05', '15:10', '15:15', '15:20', '15:25', '15:30', '15:35', '15:40', '15:45', '15:50', '15:55', '16:00', '16:05', '16:10', '16:15', '16:20', '16:25', '16:30', '16:35', '16:40', '16:45', '16:50', '16:55', '17:00', '17:05', '17:10', '17:15', '17:20', '17:25', '17:30', '17:35', '17:40', '17:45', '17:50', '17:55', '18:00', '18:05', '18:10', '18:15', '18:20', '18:25', '18:30', '18:35', '18:40', '18:45', '18:50', '18:55', '19:00', '19:05', '19:10', '19:15', '19:20', '19:25', '19:30', '19:35', '19:40', '19:45', '19:50', '19:55', '20:00', '20:05', '20:10', '20:15', '20:20', '20:25', '20:30', '20:35', '20:40', '20:45', '20:50', '20:55', '21:00', '21:05', '21:10', '21:15', '21:20', '21:25', '21:30', '21:35', '21:40', '21:45', '21:50', '21:55', '22:00', '22:05', '22:10', '22:15', '22:20', '22:25', '22:30', '22:35', '22:40', '22:45', '22:50', '22:55', '23:00', '23:05', '23:10', '23:15', '23:20', '23:25', '23:30', '23:35', '23:40', '23:45', '23:50', '23:55'];

// We're watching the "value" comming from the parent scope, since this object is updated on save/publish we need to reinit the view when this happens.
// scope.value is also changed when the view loads so this also works as the "init"-call when loading the directive.
scope.$watch('value', function() {
initModel();
initShadowModel();
});

// Initializes an empty day
function initDay() {
function initDay() {
return { label: null, items: [] };
}

// Function for initializing the model (AKA "value" from the directive attribute)
function initModel() {

// Convert legacy values (beta1 didn't support multiple times)
if (scope.value && typeof (scope.value) == 'object') {
angular.forEach(scope.value, function (value, key) {
Expand All @@ -56,7 +64,6 @@

// Initializes a shadow array to be used in the UI
function initShadowModel() {

// Some hardcoded days (currently "Monday" is always the first day of the week)
var weekdays = [
{ id: 1, name: 'Monday', alias: 'monday' },
Expand All @@ -66,8 +73,11 @@
{ id: 5, name: "Friday", alias: 'friday' },
{ id: 6, name: "Saturday", alias: 'saturday' },
{ id: 0, name: "Sunday", alias: 'sunday' }
];
];

// if need to clear the weekdays-array on the scope so that we don't just append new
// items if initShadowModel is called after a save/publish.
scope.weekdays = [];
// Populate the shadow array of weekdays
angular.forEach(weekdays, function (day) {
scope.value[day.id].label = day.name;
Expand All @@ -78,21 +88,17 @@
}

// Adds a new item to the specified day
scope.addItem = function (day) {
scope.addItem = function (day) {
day.items.push({
opens: '09:00',
closes: '17:00'
});
};

// Removes an item for day at the specified index
scope.removeItem = function (day, index) {
scope.removeItem = function (day, index) {
day.items.splice(index, 1);
};

initModel();
initShadowModel();

}
};
});
19 changes: 19 additions & 0 deletions src/Our.Umbraco.OpeningHours/Composition/OpeningHoursComposer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Our.Umbraco.OpeningHours.Converters;
using Umbraco.Core;
using Umbraco.Core.Composing;

namespace Our.Umbraco.OpeningHours.Composition
{
public class OpeningHoursComposer : IUserComposer
{
public void Compose(global::Umbraco.Core.Composing.Composition composition)
{
// Appending property value converter
composition.PropertyValueConverters().Append<OpeningHoursValueConverter>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,48 @@

namespace Our.Umbraco.OpeningHours.Converters
{
[PropertyValueType(typeof(OpeningHoursModel))]
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
public class OpeningHoursValueConverter : PropertyValueConverterBase
public class OpeningHoursValueConverter : IPropertyValueConverter
{
public override bool IsConverter(PublishedPropertyType propertyType)
private readonly ILogger _logger;

public OpeningHoursValueConverter(ILogger logger)
{
_logger = logger;
}

public bool IsConverter(IPublishedPropertyType propertyType) => propertyType.EditorAlias.Equals("OpeningHours");
public Type GetPropertyValueType(IPublishedPropertyType propertyType) => typeof(OpeningHoursModel);
public PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) => PropertyCacheLevel.Element;

public bool? IsValue(object value, PropertyValueLevel level)
{
return propertyType.PropertyEditorAlias.InvariantEquals("OpeningHours");
return true;
}

public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
public object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview)
{
return source;
}

public object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
try
{
return Model.OpeningHoursModel.Deserialize(source as string);
return OpeningHoursModel.Deserialize(inter as string);
}
catch (Exception e)
{
LogHelper.Error<OpeningHoursValueConverter>("Error converting value", e);
_logger.Error<OpeningHoursValueConverter>("Error converting value", e);
}

// Create default model
return new Model.OpeningHoursModel();
return new OpeningHoursModel();
}

public object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
{
return inter.ToString();
}

}
}
32 changes: 32 additions & 0 deletions src/Our.Umbraco.OpeningHours/Extensions/JArrayExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json.Linq;

namespace Our.Umbraco.OpeningHours.Extensions
{
public static class JArrayExtensions
{

/// <summary>
/// Gets an array of <typeparamref name="T"/> from the token matching the specified <paramref name="path"/>,
/// using the specified delegate <paramref name="callback"/> for parsing each item in the array.
/// </summary>
/// <param name="obj">The instance of <see cref="JObject"/>.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <param name="callback">A callback function used for parsing or converting the token value.</param>
public static T[] GetArray<T>(this JObject obj, string path, Func<JObject, T> callback)
{

if (!(obj?.SelectToken(path) is JArray token)) return null;

return (
from child in token
where child is JObject
select callback((JObject)child)
).ToArray();

}
}
}
130 changes: 130 additions & 0 deletions src/Our.Umbraco.OpeningHours/Extensions/JTokenExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Newtonsoft.Json.Linq;

namespace Our.Umbraco.OpeningHours.Extensions
{

public static class JObjectExtensions
{
/// <summary>
/// Gets an object from a token matching the specified <paramref name="path"/>.
/// </summary>
/// <param name="obj">The parent object.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <param name="func">The delegate (callback method) used for parsing the object.</param>
/// <returns>An instance of <typeparamref name="T"/>, or the default value of <typeparamref name="T"/> if not
/// found.</returns>
public static T GetObject<T>(this JObject obj, string path, Func<JObject, T> func)
{
return obj == null ? default(T) : func(obj.SelectToken(path) as JObject);
}

/// <summary>
/// Gets the string value of the token matching the specified <paramref name="path"/>, or <c>null</c> if
/// <paramref name="path"/> doesn't match a token.
/// </summary>
/// <param name="obj">The parent object.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <returns>An instance of <see cref="string"/>, or <c>null</c>.</returns>
public static string GetString(this JObject obj, string path)
{
if (obj == null) return null;
JToken token = GetSimpleTypeTokenFromPath(obj, path);
return token?.Value<string>();
}

/// <summary>
/// Gets the value of the token matching the specified <paramref name="path"/>, or <c>null</c> if
/// <paramref name="path"/> doesn't match a token.
/// </summary>
/// <param name="obj">The parent object.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <param name="callback">The callback used for converting the string value.</param>
/// <returns>An instance of <typeparamref name="T"/>, or <c>null</c>.</returns>
public static T GetString<T>(this JObject obj, string path, Func<string, T> callback)
{
if (obj == null) return default(T);
JToken token = GetSimpleTypeTokenFromPath(obj, path);
return token == null ? default(T) : callback(token.Value<string>());
}

/// <summary>
/// Gets the <see cref="bool"/> value of the token matching the specified <paramref name="path"/>, or
/// <c>0</c> if <paramref name="path"/> doesn't match a token.
/// </summary>
/// <param name="obj">The parent object.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <returns>An instance of <see cref="bool"/>.</returns>
public static bool GetBoolean(this JObject obj, string path)
{
return GetBoolean(obj, path, x => x);
}

/// <summary>
/// Gets the <see cref="bool"/> value of the token matching the specified <paramref name="path"/> and parses
/// it into an instance of <typeparamref name="T"/>, or the default value of <typeparamref name="T"/> if
/// <paramref name="path"/> doesn't match a token.
/// </summary>
/// <param name="obj">The parent object.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <param name="callback">A callback function used for parsing or converting the token value.</param>
/// <returns>An instance of <see cref="bool"/>, or <c>false</c> if <paramref name="path"/>
/// doesn't match a token.</returns>
public static T GetBoolean<T>(this JObject obj, string path, Func<bool, T> callback)
{

// Get the token from the path
JToken token = GetSimpleTypeTokenFromPath(obj, path);

// Check whether the token is null
if (token == null || token.Type == JTokenType.Null) return default(T);

// Convert the value to a boolean
bool value = token.ToString().Equals("true",StringComparison.InvariantCultureIgnoreCase);

// Invoke the callback and return the value
return callback(value);

}

/// <summary>
/// Gets the <see cref="JToken"/> at the specified <paramref name="path"/>. If the type of the token is either
/// <see cref="JTokenType.Object"/> or <see cref="JTokenType.Array"/>, the method will return
/// <c>null</c> instead.
/// </summary>
/// <param name="obj">The instance of <see cref="JObject"/>.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <returns>An instance of <see cref="JToken"/>, or <c>null</c>.</returns>
private static JToken GetSimpleTypeTokenFromPath(JObject obj, string path)
{
JToken token = obj?.SelectToken(path);
return token == null || token.Type == JTokenType.Object || token.Type == JTokenType.Array ? null : token;
}

/// <summary>
/// Gets the items of the <see cref="JArray"/> from the token matching the specfied <paramref name="path"/>.
/// </summary>
/// <param name="obj">The instance of <see cref="JObject"/>.</param>
/// <param name="path">A <see cref="string"/> that contains a JPath expression.</param>
/// <param name="callback">A callback function used for parsing or converting the token value.</param>
/// <returns>An array of <typeparamref name="T"/>. If the a matching token isn't found, an empty array will
/// still be returned.</returns>
public static T[] GetArrayItems<T>(this JObject obj, string path, Func<JObject, T> callback)
{

if (!(obj?.SelectToken(path) is JArray token)) return new T[0];

return (
from JObject child in token
select callback(child)
).ToArray();

}


}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Our.Umbraco.OpeningHours.Model.Items;
using Our.Umbraco.OpeningHours.Model.Offset;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Web;

namespace Our.Umbraco.OpeningHours.Extensions {
Expand All @@ -30,7 +31,7 @@ public static OpeningHoursModel GetOpeningHours(this IPublishedContent content)
/// <param name="propertyAlias">The alias of the property.</param>
/// <returns>Returns an instance of <see cref="OpeningHoursModel"/>.</returns>
public static OpeningHoursModel GetOpeningHours(this IPublishedContent content, string propertyAlias) {
OpeningHoursModel openingHours = content == null ? null : content.GetPropertyValue<OpeningHoursModel>(propertyAlias);
OpeningHoursModel openingHours = content == null ? null : content.Value<OpeningHoursModel>(propertyAlias);
return openingHours ?? new OpeningHoursModel();
}

Expand Down
Loading