├── config ├── readme.txt ├── meta.json ├── package.nuspec └── package.xml ├── src ├── G42.UmbracoGrease │ ├── App_Plugins │ │ └── G42UmbracoGrease │ │ │ ├── HideTab │ │ │ ├── css │ │ │ │ └── styles.css │ │ │ ├── views │ │ │ │ └── hideTab.html │ │ │ └── js │ │ │ │ └── directives.js │ │ │ ├── SearchTracker │ │ │ ├── css │ │ │ │ └── styles.css │ │ │ └── js │ │ │ │ ├── services.js │ │ │ │ └── controller.js │ │ │ ├── Assets │ │ │ └── Images │ │ │ │ ├── slack.png │ │ │ │ └── loader.gif │ │ │ ├── General │ │ │ ├── css │ │ │ │ └── styles.css │ │ │ └── js │ │ │ │ ├── services.js │ │ │ │ └── controller.js │ │ │ ├── _404Tracker │ │ │ ├── css │ │ │ │ └── styles.css │ │ │ └── js │ │ │ │ ├── services.js │ │ │ │ └── controller.js │ │ │ ├── ErrorReporting │ │ │ ├── css │ │ │ │ └── styles.css │ │ │ └── js │ │ │ │ ├── services.js │ │ │ │ └── controller.js │ │ │ ├── NodeHelper │ │ │ └── js │ │ │ │ ├── services.js │ │ │ │ └── controller.js │ │ │ ├── Shared │ │ │ └── css │ │ │ │ └── styles.css │ │ │ ├── Backoffice │ │ │ └── G42UmbracoGreaseTree │ │ │ │ ├── general-dashboard.html │ │ │ │ ├── node-helper-dashboard.html │ │ │ │ ├── search-tracker-dashboard.html │ │ │ │ ├── error-reporting-dashboard.html │ │ │ │ └── _404-tracker-dashboard.html │ │ │ └── package.manifest │ ├── G42404Helper │ │ ├── Models │ │ │ ├── G42Grease404ResultsModel.cs │ │ │ ├── G42Grease404DomainPath.cs │ │ │ ├── G42Grease404Tracker.cs │ │ │ ├── G42Grease404ConfigModel.cs │ │ │ └── G42Grease404TableModel.cs │ │ ├── Controllers │ │ │ └── G42404ReportsApiController.cs │ │ ├── Services │ │ │ └── G42404Service.cs │ │ └── Repositories │ │ │ └── G42404Repository.cs │ ├── G42RedirectHelper │ │ ├── Models │ │ │ └── Redirect.cs │ │ └── RedirectHelper.cs │ ├── Interfaces │ │ └── IG42ErrorHandler.cs │ ├── G42MigrationHelper │ │ ├── Migrations │ │ │ ├── MigrationBase.cs │ │ │ ├── TargetVersionZeroSixTwo.cs │ │ │ ├── TargetVersionOneZeroThree.cs │ │ │ └── TargetVersionOneZeroZero.cs │ │ └── MigrationHelper.cs │ ├── Applications │ │ └── GreaseApplication.cs │ ├── G42TransformationHelper │ │ ├── Models │ │ │ ├── ImageTransformation.cs │ │ │ └── ImageTag.cs │ │ └── TransformationHelper.cs │ ├── G42Slack │ │ ├── Models │ │ │ ├── SlackAttachmentFieldModel.cs │ │ │ ├── SlackSaySomethingModel.cs │ │ │ ├── SlackAttachmentModel.cs │ │ │ └── SlackSlashCommandModel.cs │ │ └── Helpers │ │ │ └── SlackHelper.cs │ ├── Extensions │ │ ├── PetaPocoExtensions.cs │ │ ├── IntExtensions.cs │ │ ├── PublishedContentExtensions.cs │ │ ├── ExamineExtensions.cs │ │ └── StringExtensions.cs │ ├── G42AppSettings │ │ ├── Models │ │ │ └── G42AppSetting.cs │ │ ├── Repositories │ │ │ └── G42AppSettingRepository.cs │ │ └── Services │ │ │ └── G42AppSettingsService.cs │ ├── G42SearchHelper │ │ ├── Models │ │ │ ├── G42GreaseSearchTrackerKeyword.cs │ │ │ ├── G42GreaseSearchTrackerSearch.cs │ │ │ └── G42GreaseSearchTableModel.cs │ │ ├── Controllers │ │ │ └── G42SearchReportsApiController.cs │ │ ├── Services │ │ │ └── G42SearchService.cs │ │ └── Repositories │ │ │ └── G42SearchRepository.cs │ ├── Shared │ │ ├── Controllers │ │ │ ├── G42GeneralApiController.cs │ │ │ └── GreaseTreeController.cs │ │ ├── Models │ │ │ └── G42GeneralConfigModel.cs │ │ └── Services │ │ │ └── G42GeneralService.cs │ ├── G42ErrorReporting │ │ ├── Controllers │ │ │ └── G42ErrorReportingApiController.cs │ │ ├── Services │ │ │ └── G42ErrorReportingService.cs │ │ ├── GreaseUmbracoApplication.cs │ │ ├── Models │ │ │ └── G42ErrorReportingConfigModel.cs │ │ └── DefaultErrorHandler.cs │ ├── G42NodeHelper │ │ ├── Events │ │ │ └── NodeHelperCacheUpdated.cs │ │ ├── Controllers │ │ │ └── NodeHelperApiController.cs │ │ ├── Models │ │ │ └── Site.cs │ │ └── NodeHelper.cs │ ├── Helpers │ │ ├── IpHelper.cs │ │ ├── SecurityHelper.cs │ │ ├── FormHelper.cs │ │ └── WebHelper.cs │ ├── Core │ │ ├── Services.cs │ │ ├── PetaPocoUnitOfWork.cs │ │ ├── Constants.cs │ │ └── Grease.cs │ ├── Web.Debug.config │ ├── ViewEngines │ │ └── G42ViewEngine.cs │ ├── Web.Release.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Filters │ │ └── CamelCaseFilter.cs │ ├── Events │ │ ├── RegisterViewEngine.cs │ │ └── PackageActions.cs │ ├── Views │ │ └── Partials │ │ │ └── ImageTransformations.cshtml │ ├── packages.config │ └── Web.config └── G42.UmbracoGrease.Tests │ ├── G42404Tests │ ├── UnitTests.cs │ └── FunctionalTests.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── MigrationTests │ ├── ZeroSixTwo.cs │ ├── OneZeroZero.cs │ └── OneZeroThree.cs │ ├── packages.config │ └── ExtensionTests │ └── UnitTests.cs ├── assets └── logo.png ├── lib └── PackageActionsContrib.dll ├── .gitignore ├── package.json ├── G42.UmbracoGrease.sln ├── README.md └── gruntfile.js /config/readme.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/HideTab/css/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgiszewski/G42.UmbracoGrease/HEAD/assets/logo.png -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/HideTab/views/hideTab.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/PackageActionsContrib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgiszewski/G42.UmbracoGrease/HEAD/lib/PackageActionsContrib.dll -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/G42404Tests/UnitTests.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.Tests.G42404Tests 2 | { 3 | class UnitTests 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/SearchTracker/css/styles.css: -------------------------------------------------------------------------------- 1 | .search-tracker-dashboard input[type=text] { 2 | text-align: right; 3 | width: 50px; 4 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Assets/Images/slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgiszewski/G42.UmbracoGrease/HEAD/src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Assets/Images/slack.png -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Assets/Images/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgiszewski/G42.UmbracoGrease/HEAD/src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Assets/Images/loader.gif -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/General/css/styles.css: -------------------------------------------------------------------------------- 1 | .general-dashboard input[type=checkbox] { 2 | margin-right: 5px; 3 | } 4 | 5 | .general-dashboard label { 6 | margin-top: 5px; 7 | } 8 | 9 | .general-dashboard small { 10 | display: block; 11 | } -------------------------------------------------------------------------------- /config/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "G42UmbracoGrease", 3 | "version": "1.1.0", 4 | "url": "https://github.com/kgiszewski/G42.UmbracoGrease", 5 | "author": "Kevin Giszewski", 6 | "authorUrl": "http://giszewski.com/", 7 | "license": "MIT", 8 | "licenseUrl": "http://opensource.org/licenses/MIT" 9 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Models/G42Grease404ResultsModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace G42.UmbracoGrease.G42404Helper.Models 4 | { 5 | public class G42Grease404ResultsModel 6 | { 7 | public int Count { get; set; } 8 | public string Domain { get; set; } 9 | public string Path { get; set; } 10 | public DateTime LastVisited { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42RedirectHelper/Models/Redirect.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.G42RedirectHelper.Models 2 | { 3 | /// 4 | /// Model that represents redirect data. 5 | /// 6 | public class Redirect 7 | { 8 | public int StatusCode { get; set; } 9 | public string UrlToRedirect { get; set; } 10 | public string RedirectToUrl { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Interfaces/IG42ErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace G42.UmbracoGrease.Interfaces 4 | { 5 | /// 6 | /// Interfact that is used to define methods that are required for implementing a custom Grease error handler. 7 | /// 8 | public interface IG42ErrorHandler 9 | { 10 | void Execute(object sender, EventArgs e, Exception ex); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42MigrationHelper/Migrations/MigrationBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace G42.UmbracoGrease.G42MigrationHelper.Migrations 4 | { 5 | /// 6 | /// Class that represents a base migration. 7 | /// 8 | public abstract class MigrationBase 9 | { 10 | public abstract Version TargetVersion { get; } 11 | 12 | public abstract void Excecute(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Applications/GreaseApplication.cs: -------------------------------------------------------------------------------- 1 | using umbraco.businesslogic; 2 | using umbraco.interfaces; 3 | 4 | namespace G42.UmbracoGrease.Applications 5 | { 6 | /// 7 | /// A class that defines a custom section in the Umbraco Backoffice. 8 | /// 9 | [Application("G42UmbracoGrease", "Grease", "icon-handprint", 15)] 10 | public class G42UmbracoGrease : IApplication 11 | { 12 | } 13 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/_404Tracker/css/styles.css: -------------------------------------------------------------------------------- 1 | .url-table { 2 | width: 100%; 3 | border: 1px solid #ccc; 4 | } 5 | 6 | td { 7 | word-break: break-all; 8 | } 9 | 10 | td.url-last-tried { 11 | width: 100px; 12 | } 13 | 14 | td.url-count { 15 | text-align: right; 16 | width: 75px; 17 | } 18 | 19 | ._404-tracker-dashboard input[type=text] { 20 | text-align: right; 21 | width: 50px; 22 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42TransformationHelper/Models/ImageTransformation.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.G42TransformationHelper.Models 2 | { 3 | /// 4 | /// Model that represents a transformed image. 5 | /// 6 | public class ImageTransformation 7 | { 8 | public string Type { get; set; } 9 | public string Text { get; set; } 10 | public string Html { get; set; } 11 | public object Meta { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/SearchTracker/js/services.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.services').factory('greaseSearchTrackerService', function ($http, $q, umbRequestHelper) { 2 | return { 3 | getKeywords: function (countFilter) { 4 | return umbRequestHelper.resourcePromise( 5 | $http.get("/umbraco/backoffice/G42UmbracoGrease/g42searchreportsapi/getkeywords?countFilter=" + countFilter), 'Failed to get keyword data' 6 | ); 7 | } 8 | } 9 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42TransformationHelper/Models/ImageTag.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.G42TransformationHelper.Models 2 | { 3 | /// 4 | /// Model that represents an image tag to be used on a partial. 5 | /// 6 | public class ImageTag 7 | { 8 | public string Rel { get; set; } 9 | public string Src { get; set; } 10 | public string Title { get; set; } 11 | public string Alt { get; set; } 12 | public string Classes { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Models/G42Grease404DomainPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Umbraco.Core.Persistence; 3 | 4 | namespace G42.UmbracoGrease.G42404Helper.Models 5 | { 6 | [PrimaryKey("id")] 7 | [TableName("G42Grease404TrackerDomainPaths")] 8 | public class G42Grease404DomainPath 9 | { 10 | public long Id { get; set; } 11 | public string Domain { get; set; } 12 | public string Path { get; set; } 13 | public DateTime AddedOn { get; set; } 14 | public DateTime LastVisited { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42Slack/Models/SlackAttachmentFieldModel.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace G42.UmbracoGrease.G42Slack.Models 4 | { 5 | /// 6 | /// Model that represents a Slack attachment field. 7 | /// 8 | public class SlackAttachmentFieldModel 9 | { 10 | [JsonProperty("title")] 11 | public string Title { get; set; } 12 | 13 | [JsonProperty("value")] 14 | public string Value { get; set; } 15 | 16 | [JsonProperty("short")] 17 | public bool Short { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Extensions/PetaPocoExtensions.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Core.Persistence; 2 | 3 | namespace G42.UmbracoGrease.Extensions 4 | { 5 | public static class PetaPocoExtensions 6 | { 7 | public static bool DoesTableExist(this Database db, string tableName) 8 | { 9 | var result = 10 | db.ExecuteScalar("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = @TableName", 11 | new { TableName = tableName }); 12 | 13 | return result > 0; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42AppSettings/Models/G42AppSetting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Umbraco.Core.Persistence; 4 | 5 | namespace G42.UmbracoGrease.G42AppSettings.Models 6 | { 7 | [TableName("G42GreaseAppSettings")] 8 | [PrimaryKey("id")] 9 | public class G42AppSetting 10 | { 11 | [JsonProperty("id")] 12 | public int Id { get; set; } 13 | [JsonProperty("key")] 14 | public string Key { get; set; } 15 | [JsonProperty("value")] 16 | public string Value { get; set; } 17 | [JsonProperty("updatedOn")] 18 | public DateTime UpdatedOn { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/ErrorReporting/css/styles.css: -------------------------------------------------------------------------------- 1 | .error-reporting-dashboard h2 { 2 | font-size: 20px; 3 | margin-bottom: 0; 4 | } 5 | 6 | .error-reporting-dashboard h2 .slack { 7 | width: 24px; 8 | margin-right: 4px; 9 | } 10 | 11 | .error-reporting-dashboard #slack-webhook { 12 | width: 99%; 13 | } 14 | 15 | .error-reporting-dashboard #reporting-interval { 16 | width: 30px; 17 | } 18 | 19 | .error-reporting-dashboard input[type=checkbox] { 20 | margin-right: 5px; 21 | } 22 | 23 | .error-reporting-dashboard label { 24 | margin-top: 5px; 25 | } 26 | 27 | .error-reporting-dashboard small { 28 | display: block; 29 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Models/G42GreaseSearchTrackerKeyword.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Umbraco.Core.Persistence; 3 | 4 | namespace G42.UmbracoGrease.G42SearchHelper.Models 5 | { 6 | /// 7 | /// Model that represents a search keyword in the DB. 8 | /// 9 | [TableName("G42GreaseSearchTrackerKeywords")] 10 | [PrimaryKey("id")] 11 | public class G42GreaseSearchTrackerKeyword 12 | { 13 | public int Id { get; set; } 14 | public string Domain { get; set; } 15 | public string Keyword { get; set; } 16 | public int Count { get; set; } 17 | public DateTime LastUsedOn { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Models/G42GreaseSearchTrackerSearch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Umbraco.Core.Persistence; 3 | 4 | namespace G42.UmbracoGrease.G42SearchHelper.Models 5 | { 6 | /// 7 | /// Model that represents a single search in the DB. 8 | /// 9 | [TableName("G42GreaseSearchTrackerSearches")] 10 | [PrimaryKey("id")] 11 | public class G42GreaseSearchTrackerSearch 12 | { 13 | public int Id { get; set; } 14 | public string Domain { get; set; } 15 | public string Path { get; set; } 16 | public string Keywords { get; set; } 17 | public DateTime SearchedOn { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/General/js/services.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.services').factory('greaseGeneralService', function ($http, $q, umbRequestHelper) { 2 | return { 3 | save: function (config) { 4 | return umbRequestHelper.resourcePromise( 5 | $http.post("/umbraco/backoffice/G42UmbracoGrease/g42generalapi/save", config), 'Failed to save data' 6 | ); 7 | }, 8 | getConfig: function () { 9 | return umbRequestHelper.resourcePromise( 10 | $http.get("/umbraco/backoffice/G42UmbracoGrease/g42generalapi/getConfig"), 'Failed to get config' 11 | ); 12 | } 13 | } 14 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/SearchTracker/js/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco').controller('G42UmbracoGreaseSearchTrackerDashboardController', function ($scope, $routeParams, greaseSearchTrackerService) { 2 | 3 | $scope.model = {}; 4 | $scope.model.Name = decodeURIComponent($routeParams.id); 5 | 6 | $scope.model.data = []; 7 | $scope.model.countFilter = 5; 8 | 9 | $scope.getResults = function() { 10 | $scope.isLoading = true; 11 | 12 | greaseSearchTrackerService.getKeywords($scope.model.countFilter).then(function (results) { 13 | $scope.model.data = results.data; 14 | $scope.isLoading = false; 15 | }); 16 | } 17 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Models/G42Grease404Tracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Umbraco.Core.Persistence; 3 | 4 | namespace G42.UmbracoGrease.G42404Helper.Models 5 | { 6 | /// 7 | /// Model that represents the 404 data stored in the DB. 8 | /// 9 | [PrimaryKey("id")] 10 | [TableName("G42Grease404Tracker")] 11 | public class G42Grease404Tracker 12 | { 13 | public long Id { get; set; } 14 | public long DomainPathId { get; set; } 15 | public string Referrer { get; set; } 16 | public string UserAgent { get; set; } 17 | public DateTime AddedOn { get; set; } 18 | public string IpAddress { get; set; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/ErrorReporting/js/services.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.services').factory('greaseErrorReportingService', function ($http, $q, umbRequestHelper) { 2 | return { 3 | save: function (config) { 4 | return umbRequestHelper.resourcePromise( 5 | $http.post("/umbraco/backoffice/G42UmbracoGrease/g42errorreportingapi/save", config), 'Failed to save data' 6 | ); 7 | }, 8 | getConfig: function () { 9 | return umbRequestHelper.resourcePromise( 10 | $http.get("/umbraco/backoffice/G42UmbracoGrease/g42errorreportingapi/getConfig"), 'Failed to get config' 11 | ); 12 | } 13 | } 14 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/NodeHelper/js/services.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.services').factory('greaseNodeHelperService', function ($http, $q, umbRequestHelper) { 2 | return { 3 | getNodeHelper: function () { 4 | return umbRequestHelper.resourcePromise( 5 | $http.get("/umbraco/backoffice/G42UmbracoGrease/nodehelperapi/get"), 'Failed to get nodeHelper from Umbraco' 6 | ); 7 | }, 8 | resetNodeHelper: function () { 9 | return umbRequestHelper.resourcePromise( 10 | $http.post("/umbraco/backoffice/G42UmbracoGrease/nodehelperapi/reset", {}), 'Failed to reset nodeHelper in Umbraco' 11 | ); 12 | } 13 | } 14 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Shared/css/styles.css: -------------------------------------------------------------------------------- 1 | .table th.text-right, .table td.text-right { 2 | text-align: right; 3 | } 4 | 5 | body table.table-striped tbody>tr:nth-child(even)>td {background: initial !important; background-color: #eee !important; } 6 | body table.table-striped tbody>tr:nth-child(odd)>td {background: initial !important; background-color: #fff !important; } 7 | 8 | .grease-section-settings { 9 | background-color: #eee; 10 | padding: 10px; 11 | margin-top: 10px; 12 | overflow: hidden; 13 | } 14 | 15 | a.grease-section-settings-button { 16 | color: blue; 17 | cursor: pointer; 18 | } 19 | 20 | .grease-section-settings label { 21 | margin-top: 5px; 22 | } 23 | 24 | .grease-section-settings small { 25 | display: block; 26 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Shared/Controllers/G42GeneralApiController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Shared.Models; 4 | using Umbraco.Web.Editors; 5 | using Umbraco.Web.Mvc; 6 | 7 | namespace G42.UmbracoGrease.Shared.Controllers 8 | { 9 | [PluginController("G42UmbracoGrease")] 10 | public class G42GeneralApiController : UmbracoAuthorizedJsonController 11 | { 12 | [HttpGet] 13 | public object GetConfig() 14 | { 15 | return Grease.Services.G42GeneralService.GetGeneralConfig(); 16 | } 17 | 18 | [HttpPost] 19 | public object Save(G42GeneralConfigModel model) 20 | { 21 | return Grease.Services.G42GeneralService.SaveGeneralConfig(model); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/General/js/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco').controller('G42UmbracoGreaseGeneralDashboardController', function ($scope, $routeParams, greaseGeneralService, notificationsService) { 2 | 3 | $scope.model = {}; 4 | $scope.model.Name = decodeURIComponent($routeParams.id); 5 | 6 | $scope.isSaving = true; 7 | 8 | greaseGeneralService.getConfig().then(function (config) { 9 | $scope.model.config = config; 10 | 11 | $scope.isSaving = false; 12 | }); 13 | 14 | $scope.save = function () { 15 | 16 | $scope.isSaving = true; 17 | 18 | greaseGeneralService.save($scope.model.config).then(function (result) { 19 | $scope.isSaving = false; 20 | notificationsService.success("Configuration saved."); 21 | }); 22 | } 23 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | Thumbs.db 10 | ehthumbs.db 11 | Desktop.ini 12 | $RECYCLE.BIN/ 13 | 14 | app/less/*.css 15 | dist/ 16 | node_modules/ 17 | tmp/ 18 | pkg/*.nupkg 19 | pkg/*.zip 20 | 21 | [Dd]ebug/ 22 | [Bb]uild/ 23 | [Rr]elease/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Pp]ackages/ 27 | VersionInfo.cs 28 | src/G42.UmbracoGrease.Tests/app.config 29 | 30 | *.aps 31 | *.bak 32 | *.backup 33 | *.build.csdef 34 | *.cache 35 | *.ilk 36 | *.lib 37 | *.log 38 | *.meta 39 | *.ncb 40 | *.obj 41 | *.orig 42 | *.pch 43 | *.pdb 44 | *.pgc 45 | *.pgd 46 | *.rsp 47 | *.sbr 48 | *.scc 49 | *.sdf 50 | *.sln.docstates 51 | *.suo 52 | *.tlb 53 | *.tlh 54 | *.tli 55 | *.tmp 56 | *.user 57 | *.vspscc 58 | *.[Pp]ublish.xml 59 | *_i.c 60 | *_p.c 61 | .builds 62 | .svn 63 | .hg 64 | .hgignore -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Controllers/G42SearchReportsApiController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Filters; 4 | using G42.UmbracoGrease.G42SearchHelper.Models; 5 | using Umbraco.Web.Editors; 6 | using Umbraco.Web.Mvc; 7 | 8 | namespace G42.UmbracoGrease.G42SearchHelper.Controllers 9 | { 10 | /// 11 | /// API controller that handles custom section interactions. 12 | /// 13 | [PluginController("G42UmbracoGrease")] 14 | public class G42SearchReportsApiController : UmbracoAuthorizedJsonController 15 | { 16 | [HttpGet] 17 | [CamelCasingFilter] 18 | public object GetKeywords(int countFilter) 19 | { 20 | return new G42GreaseSearchTableModel(Grease.Services.G42SearchService.Get(countFilter)); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/NodeHelper/js/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco').controller('G42UmbracoGreaseNodeHelperDashboardController', function ($scope, $routeParams, greaseNodeHelperService) { 2 | 3 | $scope.model = {}; 4 | $scope.model.Name = decodeURIComponent($routeParams.id); 5 | 6 | $scope.model.data = []; 7 | $scope.model.new = {}; 8 | 9 | $scope.isLoading = true; 10 | 11 | greaseNodeHelperService.getNodeHelper().then(function (data) { 12 | $scope.model.data = data; 13 | $scope.isLoading = false; 14 | }); 15 | 16 | $scope.reset = function() { 17 | if (confirm("Are you sure you want to reset Node Helper?")) { 18 | greaseNodeHelperService.resetNodeHelper().then(function(data) { 19 | console.log(data); 20 | }); 21 | } 22 | } 23 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/ErrorReporting/js/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco').controller('G42UmbracoGreaseErrorReportingDashboardController', function ($scope, $routeParams, greaseErrorReportingService, notificationsService) { 2 | 3 | $scope.model = {}; 4 | $scope.model.Name = decodeURIComponent($routeParams.id); 5 | 6 | $scope.isSaving = true; 7 | 8 | greaseErrorReportingService.getConfig().then(function(config) { 9 | $scope.model.config = config; 10 | 11 | $scope.isSaving = false; 12 | }); 13 | 14 | $scope.save = function () { 15 | 16 | $scope.isSaving = true; 17 | 18 | greaseErrorReportingService.save($scope.model.config).then(function (result) { 19 | $scope.isSaving = false; 20 | notificationsService.success("Configuration saved."); 21 | }); 22 | } 23 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42ErrorReporting/Controllers/G42ErrorReportingApiController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.G42ErrorReporting.Models; 4 | using Umbraco.Web.Editors; 5 | using Umbraco.Web.Mvc; 6 | 7 | namespace G42.UmbracoGrease.G42ErrorReporting.Controllers 8 | { 9 | [PluginController("G42UmbracoGrease")] 10 | public class G42ErrorReportingApiController : UmbracoAuthorizedJsonController 11 | { 12 | [HttpPost] 13 | public object Save(G42ErrorReportingConfigModel model) 14 | { 15 | Grease.Services.G42ErrorReportingService.SaveErrorReportingConfig(model); 16 | 17 | return model; 18 | } 19 | 20 | [HttpGet] 21 | public object GetConfig() 22 | { 23 | return Grease.Services.G42ErrorReportingService.GetErrorReportingConfig(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /config/package.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | G42.UmbracoGrease 5 | <%= version %> 6 | <%= name %> 7 | kgiszewski 8 | kgiszewski 9 | https://github.com/kgiszewski/G42.UmbracoGrease 10 | 11 | umbraco 12 | http://github.com/kgiszewski/G42.UmbracoGrease/raw/master/assets/logo.png 13 | <%= licenseUrl %> 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42MigrationHelper/Migrations/TargetVersionZeroSixTwo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | 4 | namespace G42.UmbracoGrease.G42MigrationHelper.Migrations 5 | { 6 | /// 7 | /// Adds the 'ipAddress' field to an install. 8 | /// 9 | public class TargetVersionZeroSixTwo : MigrationBase 10 | { 11 | public override Version TargetVersion 12 | { 13 | get { return new Version("0.6.2"); } 14 | } 15 | 16 | public override void Excecute() 17 | { 18 | using (var unitOfWork = new PetaPocoUnitOfWork()) 19 | { 20 | var sql = @" 21 | ALTER TABLE [G42Grease404Tracker] 22 | ADD [ipAddress] NVARCHAR(50) NULL 23 | "; 24 | 25 | unitOfWork.Database.Execute(sql); 26 | 27 | unitOfWork.Commit(); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42NodeHelper/Events/NodeHelperCacheUpdated.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Core; 2 | using Umbraco.Core.Cache; 3 | using Umbraco.Core.Logging; 4 | using Umbraco.Web.Cache; 5 | 6 | namespace G42.UmbracoGrease.G42NodeHelper.Events 7 | { 8 | public class NodeHelperCacheUpdated : ApplicationEventHandler 9 | { 10 | protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) 11 | { 12 | base.ApplicationStarted(umbracoApplication, applicationContext); 13 | 14 | CacheRefresherBase.CacheUpdated += _publishedPageCacheRefresherCacheUpdated; 15 | } 16 | 17 | private static void _publishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e) 18 | { 19 | LogHelper.Info("Page cache refreshed."); 20 | NodeHelper.Clear(); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/_404Tracker/js/services.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.services').factory('grease404Service', function ($http, $q, umbRequestHelper) { 2 | return { 3 | get404s: function (countFilter) { 4 | return umbRequestHelper.resourcePromise( 5 | $http.get("/umbraco/backoffice/G42UmbracoGrease/g42404reportsapi/get404s?countFilter=" + countFilter), 'Failed to get 404 data' 6 | ); 7 | }, 8 | save: function (config) { 9 | return umbRequestHelper.resourcePromise( 10 | $http.post("/umbraco/backoffice/G42UmbracoGrease/g42404reportsapi/save", config), 'Failed to save data' 11 | ); 12 | }, 13 | getConfig: function () { 14 | return umbRequestHelper.resourcePromise( 15 | $http.get("/umbraco/backoffice/G42UmbracoGrease/g42404reportsapi/getConfig"), 'Failed to get config' 16 | ); 17 | } 18 | } 19 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Shared/Models/G42GeneralConfigModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using G42.UmbracoGrease.Core; 4 | using G42.UmbracoGrease.G42AppSettings.Models; 5 | using Newtonsoft.Json; 6 | 7 | namespace G42.UmbracoGrease.Shared.Models 8 | { 9 | public class G42GeneralConfigModel 10 | { 11 | [JsonProperty("viewEngineEnable")] 12 | public bool ViewEngineEnable { get; set; } 13 | 14 | public G42GeneralConfigModel() 15 | { 16 | 17 | } 18 | 19 | public G42GeneralConfigModel(IEnumerable settings) 20 | { 21 | var settingsList = settings.ToList(); 22 | 23 | var viewEngineEnabled = settingsList.FirstOrDefault(x => x.Key == Constants.VIEW_ENGINE_ENABLE_KEY); 24 | 25 | if (viewEngineEnabled != null) 26 | { 27 | ViewEngineEnable = (viewEngineEnabled.Value == "1"); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/HideTab/js/directives.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco.directives').directive('hideTab', function($timeout) { 2 | var linker = function (scope, element, attrs) { 3 | 4 | $timeout(function() { 5 | hideTab(element); 6 | }, 10); 7 | } 8 | 9 | function hideTab(element) { 10 | 11 | var $umbPanel = element.closest('.umb-panel'); 12 | 13 | var $tabs = $umbPanel.find(".umb-nav-tabs li"); 14 | 15 | var $thisPane = element.closest('.umb-tab-pane'); 16 | 17 | if ($tabs.length > 1) { 18 | 19 | $thisPane.hide(); 20 | } 21 | else { 22 | var $properties = $thisPane.find(".umb-control-group"); 23 | $properties.hide(); 24 | } 25 | var tab = $tabs.find("a[href=#" + $thisPane.attr('id') + "]"); 26 | 27 | tab.hide(); 28 | 29 | } 30 | 31 | return { 32 | restrict: "E", 33 | link: linker 34 | } 35 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42Slack/Models/SlackSaySomethingModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace G42.UmbracoGrease.G42Slack.Models 5 | { 6 | /// 7 | /// Model that represents a Slack message. 8 | /// 9 | public class SlackSaySomethingModel 10 | { 11 | [JsonProperty(PropertyName = "username")] 12 | public string Username { get; set; } 13 | 14 | [JsonProperty(PropertyName = "icon_emoji")] 15 | public string Emoji { get; set; } 16 | 17 | [JsonProperty(PropertyName = "icon_url")] 18 | public string IconUrl { get; set; } 19 | 20 | [JsonProperty(PropertyName = "channel")] 21 | public string Channel { get; set; } 22 | 23 | [JsonProperty(PropertyName = "text")] 24 | public string Text { get; set; } 25 | 26 | [JsonProperty(PropertyName = "attachments")] 27 | public IEnumerable Attachments { get; set; } 28 | } 29 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "G42.UmbracoGrease", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "grunt": "~0.4.2", 6 | "bower": "~1.2.8", 7 | "grunt-contrib-copy": "~0.5.0", 8 | "grunt-contrib-less": "~0.8.3", 9 | "grunt-contrib-concat": "~0.3.0", 10 | "grunt-contrib-uglify": "~0.2.7", 11 | "grunt-contrib-watch": "~0.5.3", 12 | "grunt-contrib-jshint": "~0.7.2", 13 | "grunt-cli": "~0.1.11", 14 | "grunt-contrib-clean": "~0.5.0", 15 | "grunt-touch": "~0.1.0", 16 | "guid": "0.0.12", 17 | "adm-zip": "~0.4.3", 18 | "rimraf": "~2.2.5", 19 | "grunt-nuget": "~0.1.1", 20 | "grunt-template": "~0.2.2", 21 | "fs-extra": "~0.8.1", 22 | "grunt-msbuild": "~0.1.9", 23 | "grunt-dotnet-assembly-info": "~1.0.9", 24 | "load-grunt-tasks": "~0.4.0", 25 | "grunt-umbraco-package": "0.0.6" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/kgiszewski/G42.UmbracoGrease" 30 | }, 31 | "devDependencies": { 32 | "grunt-msbuild": "^0.1.12" 33 | } 34 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Shared/Services/G42GeneralService.cs: -------------------------------------------------------------------------------- 1 | using G42.UmbracoGrease.Core; 2 | using G42.UmbracoGrease.G42AppSettings.Repositories; 3 | using G42.UmbracoGrease.G42AppSettings.Services; 4 | using G42.UmbracoGrease.Shared.Models; 5 | 6 | namespace G42.UmbracoGrease.Shared.Services 7 | { 8 | public class G42GeneralService 9 | { 10 | public G42GeneralConfigModel GetGeneralConfig() 11 | { 12 | using (var uow = new PetaPocoUnitOfWork()) 13 | { 14 | var rawSettings = G42AppSettingRepository.GetGeneralConfigs(uow); 15 | 16 | return new G42GeneralConfigModel(rawSettings); 17 | } 18 | } 19 | 20 | public bool SaveGeneralConfig(G42GeneralConfigModel model) 21 | { 22 | using (var uow = new PetaPocoUnitOfWork()) 23 | { 24 | G42AppSettingsService.SaveSetting(uow, Constants.VIEW_ENGINE_ENABLE_KEY, (model.ViewEngineEnable) ? "1" : "0"); 25 | 26 | uow.Commit(); 27 | } 28 | 29 | return true; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Helpers/IpHelper.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.Helpers 2 | { 3 | /// 4 | /// Helper class to get IP address related information. 5 | /// 6 | public static class IpHelper 7 | { 8 | /// 9 | /// Gets the IP address. 10 | /// 11 | /// 12 | public static string GetIpAddress() 13 | { 14 | var context = System.Web.HttpContext.Current; 15 | 16 | if (context == null) 17 | { 18 | return "No context"; 19 | } 20 | 21 | var ipAddress = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; 22 | 23 | if (!string.IsNullOrEmpty(ipAddress)) 24 | { 25 | var addresses = ipAddress.Split(','); 26 | 27 | if (addresses.Length != 0) 28 | { 29 | return addresses[0]; 30 | } 31 | } 32 | 33 | return context.Request.ServerVariables["REMOTE_ADDR"]; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /config/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= name %> 6 | <%= version %> 7 | <%= license %> 8 | <%= url %> 9 | 10 | 0 11 | 0 12 | 0 13 | 14 | 15 | 16 | <%= author %> 17 | <%= authorUrl %> 18 | 19 | ]]> 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | <% files.forEach(function(file) { %> 33 | 34 | <%= file.guid %>.<%= file.ext %> 35 | <%= file.dir %> 36 | <%= file.name %> 37 | 38 | <% }); %> 39 | 40 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Models/G42Grease404ConfigModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using G42.UmbracoGrease.Core; 4 | using G42.UmbracoGrease.G42AppSettings.Models; 5 | using Newtonsoft.Json; 6 | 7 | namespace G42.UmbracoGrease.G42404Helper.Models 8 | { 9 | public class G42Grease404ConfigModel 10 | { 11 | [JsonProperty("daysToRetain")] 12 | public int DaysToRetain { get; set; } 13 | 14 | public G42Grease404ConfigModel() 15 | { 16 | 17 | } 18 | 19 | public G42Grease404ConfigModel(IEnumerable settings) 20 | { 21 | var settingsList = settings.ToList(); 22 | 23 | var reportingInterval = settingsList.FirstOrDefault(x => x.Key == Constants._404_TRACKER_DEFAULT_DAYS_TO_RETAIN_KEY); 24 | 25 | if (reportingInterval != null) 26 | { 27 | int reportingIntervalValue; 28 | 29 | int.TryParse(reportingInterval.Value, out reportingIntervalValue); 30 | 31 | DaysToRetain = reportingIntervalValue; 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Core/Services.cs: -------------------------------------------------------------------------------- 1 | using G42.UmbracoGrease.G42404Helper.Services; 2 | using G42.UmbracoGrease.G42AppSettings.Services; 3 | using G42.UmbracoGrease.G42ErrorReporting.Services; 4 | using G42.UmbracoGrease.G42SearchHelper.Services; 5 | using G42.UmbracoGrease.Shared.Services; 6 | 7 | namespace G42.UmbracoGrease.Core 8 | { 9 | public class Services 10 | { 11 | public G42404Service G42404Service; 12 | public G42SearchService G42SearchService; 13 | public G42AppSettingsService G42AppSettingsService; 14 | public G42GeneralService G42GeneralService; 15 | public G42ErrorReportingService G42ErrorReportingService; 16 | 17 | public Services(G42404Service g42404Service, G42SearchService g42SearchService, G42AppSettingsService g42AppSettingsService, G42GeneralService g42GeneralService, G42ErrorReportingService g42ErrorReportingService) 18 | { 19 | G42404Service = g42404Service; 20 | G42SearchService = g42SearchService; 21 | G42AppSettingsService = g42AppSettingsService; 22 | G42GeneralService = g42GeneralService; 23 | G42ErrorReportingService = g42ErrorReportingService; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Core/PetaPocoUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using Umbraco.Core.Persistence; 4 | 5 | namespace G42.UmbracoGrease.Core 6 | { 7 | public class PetaPocoUnitOfWork : IDisposable 8 | { 9 | private readonly Transaction _petaTransaction; 10 | private readonly Database _database; 11 | public static string ConnectionString = "UmbracoDbDSN"; 12 | 13 | public PetaPocoUnitOfWork(string connectionString = "") 14 | { 15 | if (!string.IsNullOrEmpty(connectionString)) 16 | { 17 | _database = new Database(connectionString); 18 | } 19 | else 20 | { 21 | _database = new Database(ConnectionString); 22 | } 23 | 24 | _petaTransaction = new Transaction(_database, IsolationLevel.ReadCommitted); 25 | } 26 | 27 | public void Dispose() 28 | { 29 | _petaTransaction.Dispose(); 30 | } 31 | 32 | public Database Database 33 | { 34 | get { return _database; } 35 | } 36 | 37 | public void Commit() 38 | { 39 | _petaTransaction.Complete(); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42TransformationHelper/TransformationHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Web.Mvc; 3 | 4 | namespace G42.UmbracoGrease.G42TransformationHelper 5 | { 6 | /// 7 | /// Helper class that handles transformations. 8 | /// 9 | public static class TransformationHelper 10 | { 11 | /// 12 | /// Renders a named razor view from a given model. 13 | /// 14 | /// The context. 15 | /// Name of the view. 16 | /// The model. 17 | /// 18 | public static string RenderRazorViewToString(ControllerContext context, string viewName, object model) 19 | { 20 | using (var sw = new StringWriter()) 21 | { 22 | var viewResult = System.Web.Mvc.ViewEngines.Engines.FindPartialView(context, viewName); 23 | var viewContext = new ViewContext(context, viewResult.View, new ViewDataDictionary(model), new TempDataDictionary(), sw); 24 | viewResult.View.Render(viewContext, sw); 25 | 26 | return sw.GetStringBuilder().ToString(); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/_404Tracker/js/controller.js: -------------------------------------------------------------------------------- 1 | angular.module('umbraco').controller('G42UmbracoGrease404TrackerDashboardController', function ($scope, $routeParams, grease404Service, notificationsService) { 2 | 3 | $scope.model = {}; 4 | $scope.model.Name = decodeURIComponent($routeParams.id); 5 | 6 | $scope.model.countFilter = 50; 7 | $scope.showSettings = false; 8 | $scope.isSaving = true; 9 | 10 | $scope.getResults = function () { 11 | $scope.isLoading = true; 12 | 13 | grease404Service.get404s($scope.model.countFilter).then(function (results) { 14 | $scope.model.data = results.data; 15 | $scope.isLoading = false; 16 | }); 17 | } 18 | 19 | $scope.toggleSettings = function() { 20 | $scope.showSettings = !$scope.showSettings; 21 | } 22 | 23 | $scope.save = function() { 24 | $scope.isSaving = true; 25 | 26 | grease404Service.save($scope.model.config).then(function() { 27 | $scope.isSaving = false; 28 | 29 | notificationsService.success("Configuration saved!"); 30 | }); 31 | } 32 | 33 | grease404Service.getConfig().then(function (config) { 34 | $scope.model.config = config; 35 | 36 | $scope.isSaving = false; 37 | }); 38 | }); -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Core/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace G42.UmbracoGrease.Core 2 | { 3 | public class Constants 4 | { 5 | public const string GENERAL_KEY_PREFIX = "general:"; 6 | public const string VIEW_ENGINE_ENABLE_KEY = "general:ViewEngine:Enable"; 7 | 8 | public const string ERROR_REPORTING_KEY_PREFIX = "errorReporting:"; 9 | public const string ERROR_REPORTING_ENABLE_KEY = "errorReporting:Enable"; 10 | public const string ERROR_REPORTING_REPORTING_INTERVAL_KEY = "errorReporting:ReportingInterval"; 11 | 12 | public const string ERROR_REPORTING_SLACK_ENABLE_KEY = "errorReporting:SlackEnable"; 13 | public const string ERROR_REPORTING_SLACK_WEBHOOKURL_KEY = "errorReporting:SlackWebhookUrl"; 14 | public const string ERROR_REPORTING_SLACK_BOTNAME_KEY = "errorReporting:SlackBotname"; 15 | public const string ERROR_REPORTING_SLACK_CHANNEL_KEY = "errorReporting:SlackChannel"; 16 | public const string ERROR_REPORTING_SLACK_EMOJI_KEY = "errorReporting:SlackEmoji"; 17 | 18 | public const int ERROR_REPORTING_DEFAULT_INTERVAL = 15; 19 | 20 | public const string _404_TRACKER_KEY_PREFIX = "_404tracker:"; 21 | public const string _404_TRACKER_DEFAULT_DAYS_TO_RETAIN_KEY = "_404tracker:DaysToRetain"; 22 | public const int _404_TRACKER_DEFAULT_DAYS_TO_RETAIN = 90; 23 | } 24 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Controllers/G42404ReportsApiController.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Http; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Filters; 4 | using G42.UmbracoGrease.G42404Helper.Models; 5 | using Umbraco.Web.Editors; 6 | using Umbraco.Web.Mvc; 7 | 8 | namespace G42.UmbracoGrease.G42404Helper.Controllers 9 | { 10 | /// 11 | /// API controller that handles custom section interactions. 12 | /// 13 | [PluginController("G42UmbracoGrease")] 14 | public class G42404ReportsApiController : UmbracoAuthorizedJsonController 15 | { 16 | /// 17 | /// Returns 404's from the DB. 18 | /// 19 | /// The count filter. 20 | /// 21 | [HttpGet] 22 | [CamelCasingFilter] 23 | public object Get404s(int countFilter) 24 | { 25 | return new G42Grease404TableModel(Grease.Services.G42404Service.GetResults(countFilter)); 26 | } 27 | 28 | [HttpGet] 29 | public object GetConfig() 30 | { 31 | return Grease.Services.G42404Service.Get404TrackerConfig(); 32 | } 33 | 34 | [HttpPost] 35 | public object Save(G42Grease404ConfigModel model) 36 | { 37 | return Grease.Services.G42404Service.Save404TrackerConfig(model); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/ViewEngines/G42ViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Web.Mvc; 4 | using System.IO; 5 | using Umbraco.Core.Logging; 6 | using Umbraco.Core.IO; 7 | 8 | namespace G42.UmbracoGrease.ViewEngines 9 | { 10 | /// 11 | /// A class that extends the RazorViewEngine that allows for nested view folders to be detected automatically. 12 | /// 13 | public class G42ViewEngine : RazorViewEngine 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public G42ViewEngine() 19 | { 20 | var viewsPath = IOHelper.MapPath("~/Views"); 21 | 22 | var directories = Directory.GetDirectories(viewsPath); 23 | 24 | var pathList = new List(); 25 | 26 | foreach (var directory in directories.Where(x => !x.ToLower().Contains("partials"))) 27 | { 28 | var folder = Path.GetFileName(directory); 29 | 30 | var path = string.Format("~/Views/{0}/{{0}}.cshtml", folder); 31 | 32 | pathList.Add(path); 33 | 34 | LogHelper.Info("Registering view engine path: " + folder); 35 | } 36 | 37 | ViewLocationFormats = ViewLocationFormats.Union(pathList).ToArray(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42Slack/Models/SlackAttachmentModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace G42.UmbracoGrease.G42Slack.Models 5 | { 6 | /// 7 | /// Model that represents a Slack attachment. 8 | /// 9 | public class SlackAttachmentModel 10 | { 11 | [JsonProperty("fallback")] 12 | public string Fallback { get; set; } 13 | 14 | [JsonProperty("color")] 15 | public string Color { get; set; } 16 | 17 | [JsonProperty("pretext")] 18 | public string Pretext { get; set; } 19 | 20 | [JsonProperty("author_name")] 21 | public string AuthorName { get; set; } 22 | 23 | [JsonProperty("author_link")] 24 | public string AuthorLink { get; set; } 25 | 26 | [JsonProperty("author_icon")] 27 | public string AuthorIcon { get; set; } 28 | 29 | [JsonProperty("title")] 30 | public string Title { get; set; } 31 | 32 | [JsonProperty("title_link")] 33 | public string TitleLink { get; set; } 34 | 35 | [JsonProperty("text")] 36 | public string Text { get; set; } 37 | 38 | [JsonProperty("fields")] 39 | public IEnumerable Fields { get; set; } 40 | 41 | [JsonProperty("image_url")] 42 | public string ImageUrl { get; set; } 43 | 44 | [JsonProperty("thumb_url")] 45 | public string ThumbUrl { get; set; } 46 | } 47 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42Slack/Models/SlackSlashCommandModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace G42.UmbracoGrease.G42Slack.Models 4 | { 5 | /// 6 | /// Model that represents a CLI message from Slack. 7 | /// 8 | public class SlackSlashCommandModel 9 | { 10 | public string Token { get; set; } 11 | public string TeamId { get; set; } 12 | public string TeamDomain { get; set; } 13 | public string ChannelId { get; set; } 14 | public string ChannelName { get; set; } 15 | public string UserId { get; set; } 16 | public string UserName { get; set; } 17 | public string Command { get; set; } 18 | public string Text { get; set; } 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The KVP. 24 | public SlackSlashCommandModel(NameValueCollection kvp) 25 | { 26 | Token = kvp.Get("token"); 27 | TeamId = kvp.Get("team_id"); 28 | TeamDomain = kvp.Get("team_domain"); 29 | ChannelId = kvp.Get("channel_id"); 30 | ChannelName = kvp.Get("channel_name"); 31 | UserId = kvp.Get("user_id"); 32 | UserName = kvp.Get("user_name"); 33 | Command = kvp.Get("command"); 34 | Text = kvp.Get("text"); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Backoffice/G42UmbracoGreaseTree/general-dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

{{model.Name}}

6 |
7 |
8 | 9 | 10 |
11 |
12 |

This section configures the general features in Grease.

13 | 14 |

View Engine

15 |

In a multi-site install, enabling this feature allows for subfolders to be used in the views folder.

16 |
17 | 18 | Tick this box to enable the custom view engine to allow for nested views. 19 |
20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 |
29 |
-------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42MigrationHelper/Migrations/TargetVersionOneZeroThree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | 4 | namespace G42.UmbracoGrease.G42MigrationHelper.Migrations 5 | { 6 | public class TargetVersionOneZeroThree : MigrationBase 7 | { 8 | public override Version TargetVersion 9 | { 10 | get { return new Version("1.0.3"); } 11 | } 12 | 13 | public override void Excecute() 14 | { 15 | using (var unitOfWork = new PetaPocoUnitOfWork()) 16 | { 17 | var sql = @" 18 | ALTER TABLE [G42Grease404TrackerDomainPaths] 19 | ALTER COLUMN domain NVARCHAR(75) 20 | "; 21 | 22 | unitOfWork.Database.Execute(sql); 23 | 24 | sql = @" 25 | ALTER TABLE [G42Grease404TrackerDomainPaths] 26 | ALTER COLUMN path NVARCHAR(255) 27 | "; 28 | 29 | unitOfWork.Database.Execute(sql); 30 | 31 | sql = @" 32 | ALTER TABLE [G42Grease404TrackerDomainPaths] 33 | ADD CONSTRAINT [IX_G42Grease404TrackerDomainPaths] UNIQUE NONCLUSTERED 34 | ( 35 | [domain] ASC, 36 | [path] ASC 37 | ) 38 | "; 39 | 40 | unitOfWork.Database.Execute(sql); 41 | 42 | unitOfWork.Commit(); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Services/G42SearchService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.G42SearchHelper.Models; 4 | using G42.UmbracoGrease.G42SearchHelper.Repositories; 5 | 6 | namespace G42.UmbracoGrease.G42SearchHelper.Services 7 | { 8 | public class G42SearchService 9 | { 10 | 11 | public IEnumerable Get(int countFilter) 12 | { 13 | using (var uow = new PetaPocoUnitOfWork()) 14 | { 15 | return G42SearchRepository.Get(uow, countFilter); 16 | } 17 | } 18 | 19 | public void AddSearch(string keywords) 20 | { 21 | using (var uow = new PetaPocoUnitOfWork()) 22 | { 23 | G42SearchRepository.AddSearch(uow, keywords); 24 | 25 | uow.Commit(); 26 | } 27 | } 28 | 29 | public void CreateSearchTrackerKeywordsTable() 30 | { 31 | using (var uow = new PetaPocoUnitOfWork()) 32 | { 33 | G42SearchRepository.CreateSearchTrackerKeywordsTable(uow); 34 | 35 | uow.Commit(); 36 | } 37 | } 38 | 39 | public void CreateSearchTrackerSearchesTable() 40 | { 41 | using (var uow = new PetaPocoUnitOfWork()) 42 | { 43 | G42SearchRepository.CreateSearchTrackerSearchesTable(uow); 44 | 45 | uow.Commit(); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("G42.UmbracoGrease")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("University of Notre Dame")] 12 | [assembly: AssemblyProduct("G42.UmbracoGrease")] 13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("3d13303d-cea3-4957-ae11-62d6611a5b1f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.1.0")] 36 | -------------------------------------------------------------------------------- /G42.UmbracoGrease.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.40629.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "G42.UmbracoGrease", "src\G42.UmbracoGrease\G42.UmbracoGrease.csproj", "{97F9C8C3-D631-4EAC-A4BD-018848ED1590}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "G42.UmbracoGrease.Tests", "src\G42.UmbracoGrease.Tests\G42.UmbracoGrease.Tests.csproj", "{5AB7F0B5-FCBE-43FD-8604-E5436158D02B}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {97F9C8C3-D631-4EAC-A4BD-018848ED1590}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {97F9C8C3-D631-4EAC-A4BD-018848ED1590}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {97F9C8C3-D631-4EAC-A4BD-018848ED1590}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {97F9C8C3-D631-4EAC-A4BD-018848ED1590}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {5AB7F0B5-FCBE-43FD-8604-E5436158D02B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {5AB7F0B5-FCBE-43FD-8604-E5436158D02B}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {5AB7F0B5-FCBE-43FD-8604-E5436158D02B}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {5AB7F0B5-FCBE-43FD-8604-E5436158D02B}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Filters/CamelCaseFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Net.Http.Formatting; 3 | using System.Web.Http.Filters; 4 | using Newtonsoft.Json.Serialization; 5 | 6 | namespace G42.UmbracoGrease.Filters 7 | { 8 | /// 9 | /// Filter that forces camelCasing on the output of a controller. 10 | /// 11 | public class CamelCasingFilterAttribute : ActionFilterAttribute 12 | { 13 | private static JsonMediaTypeFormatter _camelCasingFormatter = new JsonMediaTypeFormatter(); 14 | 15 | /// 16 | /// Initializes the class. 17 | /// 18 | static CamelCasingFilterAttribute() 19 | { 20 | _camelCasingFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); 21 | } 22 | 23 | /// 24 | /// Occurs after the action method is invoked. 25 | /// 26 | /// The action executed context. 27 | public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) 28 | { 29 | ObjectContent content = actionExecutedContext.Response.Content as ObjectContent; 30 | if (content != null) 31 | { 32 | if (content.Formatter is JsonMediaTypeFormatter) 33 | { 34 | actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, _camelCasingFormatter); 35 | } 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Core/Grease.cs: -------------------------------------------------------------------------------- 1 | using G42.UmbracoGrease.G42404Helper.Services; 2 | using G42.UmbracoGrease.G42AppSettings.Services; 3 | using G42.UmbracoGrease.G42ErrorReporting.Services; 4 | using G42.UmbracoGrease.G42SearchHelper.Services; 5 | using G42.UmbracoGrease.Shared.Services; 6 | 7 | namespace G42.UmbracoGrease.Core 8 | { 9 | public class Grease 10 | { 11 | private static object _padlock = new object(); 12 | 13 | private static Services _services; 14 | public static Services Services 15 | { 16 | get 17 | { 18 | if (_services == null) 19 | { 20 | lock (_padlock) 21 | { 22 | if (_services == null) 23 | { 24 | _services = new Services( 25 | new G42404Service(), 26 | new G42SearchService(), 27 | new G42AppSettingsService(), 28 | new G42GeneralService(), 29 | new G42ErrorReportingService() 30 | ); 31 | 32 | return _services; 33 | } 34 | 35 | return _services; 36 | } 37 | } 38 | 39 | return _services; 40 | } 41 | 42 | // the idea here is to allow the services to be swapped with a different set for testing 43 | set { _services = value; } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("G42.UmbracoGrease.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("University of Notre Dame")] 12 | [assembly: AssemblyProduct("G42.UmbracoGrease.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © University of Notre Dame 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("43900842-d41c-4259-a64c-363467cd751a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42NodeHelper/Controllers/NodeHelperApiController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Web.Http; 4 | using G42.UmbracoGrease.Filters; 5 | using Umbraco.Core.Logging; 6 | using Umbraco.Web.Editors; 7 | using Umbraco.Web.Mvc; 8 | 9 | namespace G42.UmbracoGrease.G42NodeHelper.Controllers 10 | { 11 | /// 12 | /// API Controller that handled dashboard NodeHelper interactions. 13 | /// 14 | [PluginController("G42UmbracoGrease")] 15 | public class NodeHelperApiController : UmbracoAuthorizedJsonController 16 | { 17 | [HttpGet] 18 | [CamelCasingFilter] 19 | public object Get() 20 | { 21 | var sites = new List(); 22 | 23 | if (NodeHelper.IsInitialized()) 24 | { 25 | foreach (var site in NodeHelper.Instance.Sites.OrderBy(x => x.Domain)) 26 | { 27 | sites.Add(new 28 | { 29 | Domain = site.Domain, 30 | RootId = site.RootId, 31 | CreatedOn = NodeHelper.Instance.CreatedOn 32 | }); 33 | } 34 | } 35 | 36 | return sites; 37 | } 38 | 39 | /// 40 | /// Resets this instance. 41 | /// 42 | /// 43 | [HttpPost] 44 | public object Reset() 45 | { 46 | NodeHelper.Clear(); 47 | 48 | LogHelper.Info("Reset"); 49 | 50 | return "reset"; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/package.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "propertyEditors": [ 3 | { 4 | "name": "Hide Tab", 5 | "alias": "HideTab", 6 | "editor": { 7 | "valueType": "JSON", 8 | "view": "~/App_Plugins/G42UmbracoGrease/HideTab/views/hideTab.html" 9 | }, 10 | "prevalues": { 11 | 12 | 13 | } 14 | } 15 | ], 16 | "css": [ 17 | "~/App_Plugins/G42UmbracoGrease/_404Tracker/css/styles.css", 18 | "~/App_Plugins/G42UmbracoGrease/HideTab/css/styles.css", 19 | "~/App_Plugins/G42UmbracoGrease/SearchTracker/css/styles.css", 20 | "~/App_Plugins/G42UmbracoGrease/Shared/css/styles.css", 21 | "~/App_Plugins/G42UmbracoGrease/General/css/styles.css", 22 | "~/App_Plugins/G42UmbracoGrease/ErrorReporting/css/styles.css" 23 | ], 24 | "javascript": [ 25 | "~/App_Plugins/G42UmbracoGrease/HideTab/js/directives.js", 26 | "~/App_Plugins/G42UmbracoGrease/General/js/controller.js", 27 | "~/App_Plugins/G42UmbracoGrease/General/js/services.js", 28 | "~/App_Plugins/G42UmbracoGrease/SearchTracker/js/controller.js", 29 | "~/App_Plugins/G42UmbracoGrease/SearchTracker/js/services.js", 30 | "~/App_Plugins/G42UmbracoGrease/_404Tracker/js/controller.js", 31 | "~/App_Plugins/G42UmbracoGrease/_404Tracker/js/services.js", 32 | "~/App_Plugins/G42UmbracoGrease/NodeHelper/js/controller.js", 33 | "~/App_Plugins/G42UmbracoGrease/NodeHelper/js/services.js", 34 | "~/App_Plugins/G42UmbracoGrease/ErrorReporting/js/controller.js", 35 | "~/App_Plugins/G42UmbracoGrease/ErrorReporting/js/services.js" 36 | ] 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Events/RegisterViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.ViewEngines; 4 | using Umbraco.Core; 5 | using Umbraco.Core.Logging; 6 | 7 | namespace G42.UmbracoGrease.Events 8 | { 9 | /// 10 | /// Class that allows for nested views in the Umbraco Backoffice file system. 11 | /// 12 | public class RegisterViewEngine : ApplicationEventHandler 13 | { 14 | /// 15 | /// Overridable method to execute when All resolvers have been initialized but resolution is not frozen so they can be modified in this method 16 | /// 17 | /// 18 | /// 19 | protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) 20 | { 21 | try 22 | { 23 | var enabled = Grease.Services.G42AppSettingsService.GetValue(Core.Constants.VIEW_ENGINE_ENABLE_KEY); 24 | 25 | if (enabled) 26 | { 27 | LogHelper.Info("Registering Grease ViewEngine..."); 28 | System.Web.Mvc.ViewEngines.Engines.Add(new G42ViewEngine()); 29 | } 30 | else 31 | { 32 | LogHelper.Info("Disabling Grease ViewEngine..."); 33 | } 34 | } 35 | catch (Exception ex) 36 | { 37 | LogHelper.Error(ex.Message, ex); 38 | } 39 | 40 | base.ApplicationStarting(umbracoApplication, applicationContext); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42Slack/Helpers/SlackHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | using System.Text; 4 | using G42.UmbracoGrease.G42Slack.Models; 5 | using G42.UmbracoGrease.Helpers; 6 | using Newtonsoft.Json; 7 | 8 | namespace G42.UmbracoGrease.G42Slack.Helpers 9 | { 10 | /// 11 | /// Wrapper class to handle posting to Slack. 12 | /// 13 | public static class SlackHelper 14 | { 15 | /// 16 | /// Says something in Slack. 17 | /// 18 | /// The text. 19 | /// The username. 20 | /// The URL. 21 | /// The channel. 22 | /// The emoji. 23 | /// The attachements. 24 | /// The icon URL. 25 | /// 26 | public static string SaySomething(string text, string username, string url, string channel = "", string emoji = "", IEnumerable attachements = null, string iconUrl = "") 27 | { 28 | var slackPost = new SlackSaySomethingModel() 29 | { 30 | Username = username, 31 | Channel = channel, 32 | Emoji = emoji, 33 | IconUrl = iconUrl, 34 | Text = text, 35 | Attachments = attachements 36 | }; 37 | 38 | var postBody = JsonConvert.SerializeObject(slackPost); 39 | 40 | var content = new StringContent(postBody, Encoding.UTF8, "application/json"); 41 | 42 | return WebHelper.Post(url, content).Result; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Views/Partials/ImageTransformations.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Web.Mvc.UmbracoViewPage 2 | @using G42.UmbracoGrease.G42TransformationHelper.Models 3 | 4 | @{ 5 | var meta = (ImageTag)Model.Meta; 6 | 7 | switch (Model.Type) 8 | { 9 | case "image-full-width": 10 |
11 | @meta.Alt 12 | @if (!string.IsNullOrWhiteSpace(meta.Alt)) 13 | { 14 |
15 | @Html.Raw(meta.Alt) 16 |
17 | } 18 |
19 | break; 20 | 21 | case "image-right": 22 |
23 | @meta.Alt 24 | @if (!string.IsNullOrWhiteSpace(meta.Alt)) 25 | { 26 |
27 | @Html.Raw(meta.Alt) 28 |
29 | } 30 |
31 | break; 32 | 33 | case "image-left": 34 |
35 | @meta.Alt 36 | @if (!string.IsNullOrWhiteSpace(meta.Alt)) 37 | { 38 |
39 | @Html.Raw(meta.Alt) 40 |
41 | } 42 |
43 | break; 44 | 45 | default: 46 | @Model.Text 47 | break; 48 | } 49 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Extensions/IntExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Umbraco.Core; 5 | using Umbraco.Core.Models; 6 | 7 | namespace G42.UmbracoGrease.Extensions 8 | { 9 | /// 10 | /// Extensions for integers. 11 | /// 12 | public static class IntExtensions 13 | { 14 | /// 15 | /// Converts an int to the human readable bytes (i.e. 2.00 MB, 10.12 KB, etc). 16 | /// 17 | /// The length. 18 | /// 19 | public static string ToHumanReadableBytes(this int len) 20 | { 21 | var sizes = new[] { "B", "KB", "MB", "GB" }; 22 | var order = 0; 23 | 24 | while (len >= 1024 && order + 1 < sizes.Length) 25 | { 26 | order++; 27 | len = len / 1024; 28 | } 29 | 30 | return String.Format("{0:0.##} {1}", len, sizes[order]); 31 | } 32 | 33 | /// 34 | /// Converts an integer to its ordinal value (i.e. 1st, 2nd, 3rd, etc). 35 | /// 36 | /// The input. 37 | /// 38 | public static string ToOrdinal(this int input) 39 | { 40 | if (input <= 0) return input.ToString(); 41 | 42 | switch (input % 100) 43 | { 44 | case 11: 45 | case 12: 46 | case 13: 47 | return input + "th"; 48 | } 49 | 50 | switch (input % 10) 51 | { 52 | case 1: 53 | return input + "st"; 54 | case 2: 55 | return input + "nd"; 56 | case 3: 57 | return input + "rd"; 58 | default: 59 | return input + "th"; 60 | } 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42ErrorReporting/Services/G42ErrorReportingService.cs: -------------------------------------------------------------------------------- 1 | using G42.UmbracoGrease.Core; 2 | using G42.UmbracoGrease.G42AppSettings.Repositories; 3 | using G42.UmbracoGrease.G42AppSettings.Services; 4 | using G42.UmbracoGrease.G42ErrorReporting.Models; 5 | 6 | namespace G42.UmbracoGrease.G42ErrorReporting.Services 7 | { 8 | public class G42ErrorReportingService 9 | { 10 | public bool SaveErrorReportingConfig(G42ErrorReportingConfigModel model) 11 | { 12 | using (var uow = new PetaPocoUnitOfWork()) 13 | { 14 | //general 15 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_ENABLE_KEY, (model.Enable) ? "1" : "0"); 16 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_REPORTING_INTERVAL_KEY, model.ReportingInterval.ToString()); 17 | 18 | //slack 19 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_SLACK_ENABLE_KEY, (model.SlackEnable) ? "1" : "0"); 20 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_SLACK_WEBHOOKURL_KEY, model.SlackWebhookUrl); 21 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_SLACK_BOTNAME_KEY, model.SlackBotName, "GreaseErrorBot"); 22 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_SLACK_CHANNEL_KEY, model.SlackChannel); 23 | G42AppSettingsService.SaveSetting(uow, Constants.ERROR_REPORTING_SLACK_EMOJI_KEY, model.SlackEmoji, ":rotating_light:"); 24 | 25 | uow.Commit(); 26 | } 27 | 28 | return true; 29 | } 30 | 31 | public G42ErrorReportingConfigModel GetErrorReportingConfig() 32 | { 33 | using (var uow = new PetaPocoUnitOfWork()) 34 | { 35 | var rawSettings = G42AppSettingRepository.GetErrorReportingConfigs(uow); 36 | 37 | return new G42ErrorReportingConfigModel(rawSettings); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Extensions/PublishedContentExtensions.cs: -------------------------------------------------------------------------------- 1 | using G42.UmbracoGrease.G42NodeHelper; 2 | using Umbraco.Core.Models; 3 | using Umbraco.Web; 4 | 5 | namespace G42.UmbracoGrease.Extensions 6 | { 7 | /// 8 | /// IPublishedContent Extensions. 9 | /// 10 | public static class PublishedContentExtensions 11 | { 12 | /// 13 | /// Coalesces the HTML title from a property stored within NodeHelper's SiteSettings property. 14 | /// 15 | /// The content. 16 | /// The prefix property alias. 17 | /// The HTML title alias. 18 | /// 19 | public static string CoalesceHtmlTitle(this IPublishedContent content, string prefixPropertyAlias = "pageTitlePrefix", string htmlTitleAlias = "htmlTitle") 20 | { 21 | if (content == null || NodeHelper.Instance.CurrentSite.SiteSettings == null) 22 | return ""; 23 | 24 | var umbHelper = new UmbracoHelper(); 25 | 26 | return NodeHelper.Instance.CurrentSite.SiteSettings.GetPropertyValue(prefixPropertyAlias) + " " + umbHelper.Coalesce(content.GetPropertyValue(htmlTitleAlias), content.Name); 27 | } 28 | 29 | /// 30 | /// Coalesces the navigation title from the given property alias. (i.e. Use the custom property or default to the node name). 31 | /// 32 | /// The content. 33 | /// The property alias. 34 | /// 35 | public static string CoalesceNavigationTitle(this IPublishedContent content, string propertyAlias = "navigationTitle") 36 | { 37 | if (content == null) 38 | return ""; 39 | 40 | var umbHelper = new UmbracoHelper(); 41 | 42 | return umbHelper.Coalesce(content.GetPropertyValue(propertyAlias), content.Name); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/G42404Tests/FunctionalTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using G42.UmbracoGrease.Core; 4 | using NUnit.Framework; 5 | 6 | namespace G42.UmbracoGrease.Tests.G42404Tests 7 | { 8 | [Category("404")] 9 | [TestFixture] 10 | public class FunctionalTests 11 | { 12 | [TestFixtureSetUp] 13 | public void Setup() 14 | { 15 | PetaPocoUnitOfWork.ConnectionString = "testDb"; 16 | 17 | Grease.Services.G42404Service.Create404TrackerTable(); 18 | Grease.Services.G42404Service.Create404DomainPathsTable(); 19 | 20 | using (var uow = new PetaPocoUnitOfWork()) 21 | { 22 | uow.Database.Execute(@"TRUNCATE TABLE G42Grease404Tracker"); 23 | uow.Database.Execute(@"TRUNCATE TABLE G42Grease404TrackerDomainPaths"); 24 | 25 | uow.Commit(); 26 | } 27 | } 28 | 29 | [TestCase("http://test.local", "/foo", "http://google.com", "TestAgent")] 30 | [TestCase("http://test.local", "/foo2", "http://google.com", "TestAgent")] 31 | [TestCase("http://test.local", "/foo2", "http://google.com", "TestAgent2")] 32 | [TestCase("http://test.local", "/foo2", "http://google.com", "TestAgent3")] 33 | public void Can_Add_404(string domain, string path, string referrer, string agent) 34 | { 35 | Console.WriteLine("Testing..."); 36 | 37 | var before = Grease.Services.G42404Service.GetResults(1) 38 | .FirstOrDefault(x => x.Domain == domain && x.Path == path); 39 | 40 | var beforeCount = 0; 41 | 42 | if (before != null) 43 | { 44 | beforeCount = before.Count; 45 | } 46 | 47 | Grease.Services.G42404Service.Add(domain, path, referrer, agent); 48 | 49 | var after = Grease.Services.G42404Service.GetResults(1) 50 | .FirstOrDefault(x => x.Domain == domain && x.Path == path); 51 | 52 | Assert.AreEqual(beforeCount + 1, after.Count); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Models/G42Grease404TableModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace G42.UmbracoGrease.G42404Helper.Models 6 | { 7 | /// 8 | /// Model that represents the 404 data in a report format. 9 | /// 10 | public class G42Grease404TableModel 11 | { 12 | public IEnumerable Data { get; set; } 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The data. 18 | public G42Grease404TableModel(IEnumerable data) 19 | { 20 | var domains = new List(); 21 | 22 | foreach (var domain in data.GroupBy(x => x.Domain)) 23 | { 24 | var urls = new List(); 25 | 26 | foreach (var url in domain) 27 | { 28 | urls.Add(new PathLastTried() 29 | { 30 | Path = url.Path, 31 | LastVisited = url.LastVisited, 32 | Count = url.Count 33 | }); 34 | } 35 | 36 | domains.Add(new DomainPaths() 37 | { 38 | Domain = domain.Key, 39 | Urls = urls.OrderByDescending(x => x.Count) 40 | }); 41 | } 42 | 43 | Data = domains.OrderBy(x => x.Domain); 44 | } 45 | 46 | /// 47 | /// Model that represents domains and their paths. 48 | /// 49 | public class DomainPaths 50 | { 51 | public string Domain { get; set; } 52 | public IEnumerable Urls { get; set; } 53 | } 54 | 55 | /// 56 | /// Model that represents the count and last time the path was tried. 57 | /// 58 | public class PathLastTried 59 | { 60 | public string Path { get; set; } 61 | public DateTime LastVisited { get; set; } 62 | public int Count { get; set; } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Backoffice/G42UmbracoGreaseTree/node-helper-dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

{{model.Name}}

6 |
7 |
8 | 9 | 10 |
11 |
12 | LOADING... 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
DomainCreatedOnNode Id
There are no results.
{{data.domain}}{{data.createdOn}}{{data.rootId}}
33 |
34 |

Clear NodeHelper

35 |

Clears the NodeHelper variable and forces a refresh. Necessary when managing domains. This will not work in a load-balanced environment, however restarting the app pool will solve this.

36 | 37 |
38 |
39 |
40 |
41 |
42 |
-------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Models/G42GreaseSearchTableModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace G42.UmbracoGrease.G42SearchHelper.Models 6 | { 7 | /// 8 | /// Model that represents search terms used by domain in a report form. 9 | /// 10 | public class G42GreaseSearchTableModel 11 | { 12 | public IEnumerable Data { get; set; } 13 | 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The data. 18 | public G42GreaseSearchTableModel(IEnumerable data) 19 | { 20 | var domains = new List(); 21 | 22 | foreach (var domain in data.GroupBy(x => x.Domain)) 23 | { 24 | var keywords = new List(); 25 | 26 | foreach (var keyword in domain) 27 | { 28 | keywords.Add(new DomainKeyword() 29 | { 30 | Keyword = keyword.Keyword, 31 | Count = keyword.Count, 32 | LastUsedOn = keyword.LastUsedOn 33 | }); 34 | } 35 | 36 | domains.Add(new DomainSearch() 37 | { 38 | Domain = domain.Key, 39 | Keywords = keywords.OrderByDescending(x => x.Count) 40 | }); 41 | } 42 | 43 | Data = domains.OrderBy(x => x.Domain); 44 | } 45 | 46 | /// 47 | /// Model that represents keywords by domain. 48 | /// 49 | public class DomainSearch 50 | { 51 | public string Domain { get; set; } 52 | public IEnumerable Keywords { get; set; } 53 | } 54 | 55 | /// 56 | /// Model that represents the count and last time a keyword was used. 57 | /// 58 | public class DomainKeyword 59 | { 60 | public string Keyword { get; set; } 61 | public int Count { get; set; } 62 | public DateTime LastUsedOn { get; set; } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Helpers/SecurityHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | using System.Web; 4 | 5 | namespace G42.UmbracoGrease.Helpers 6 | { 7 | /// 8 | /// Helper that handles cryptographic and security related tasks. 9 | /// 10 | public static class SecurityHelper 11 | { 12 | /// 13 | /// Calculates the MD5 hash of the given input. 14 | /// 15 | /// The input. 16 | /// 17 | public static string CalculateMd5Hash(string input) 18 | { 19 | var md5 = MD5.Create(); 20 | var inputBytes = Encoding.ASCII.GetBytes(input); 21 | var hash = md5.ComputeHash(inputBytes); 22 | 23 | var sb = new StringBuilder(); 24 | 25 | for (var i = 0; i < hash.Length; i++) 26 | { 27 | sb.Append(hash[i].ToString("X2")); 28 | } 29 | 30 | return sb.ToString(); 31 | } 32 | 33 | /// 34 | /// Ensures the AWS SSL when using a shared SSL environment. 35 | /// 36 | /// if set to true [enable local]. 37 | public static void EnsureAwsSsl(bool enableLocal = false) 38 | { 39 | if (HttpContext.Current.Request.Url.Host.EndsWith(".local") && !enableLocal) 40 | { 41 | return; 42 | } 43 | 44 | //added this due to how the ssl forwarding is working on AWS 45 | if (HttpContext.Current.Request.Headers["X-Forwarded-Proto"] == "http") 46 | { 47 | HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.AbsoluteUri.Replace("http:", "https:")); 48 | } 49 | } 50 | 51 | /// 52 | /// Ensures the SSL on normal setups. 53 | /// 54 | /// if set to true [enable local]. 55 | public static void EnsureSsl(bool enableLocal = false) 56 | { 57 | if (HttpContext.Current.Request.Url.Host.EndsWith(".local") && !enableLocal) 58 | { 59 | return; 60 | } 61 | 62 | if (HttpContext.Current.Request.Url.Port != 443) 63 | { 64 | HttpContext.Current.Response.Redirect(HttpContext.Current.Request.Url.AbsoluteUri.Replace("http:", "https:")); 65 | } 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Helpers/FormHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Mail; 2 | 3 | namespace G42.UmbracoGrease.Helpers 4 | { 5 | /// 6 | /// Helper that handles processing typical web forms. 7 | /// 8 | public static class FormHelper 9 | { 10 | /// 11 | /// Sends an email on behalf of the form. 12 | /// 13 | /// To CSV. 14 | /// From. 15 | /// The subject. 16 | /// The body. 17 | /// if set to true [HTML]. 18 | /// The cc CSV. 19 | /// The BCC CSV. 20 | public static void SendMail(string toCsv, string from, string subject, string body, bool html, string ccCsv = "", string bccCsv = "") 21 | { 22 | using (var message = new MailMessage() 23 | { 24 | From = new MailAddress(from), 25 | Subject = subject, 26 | IsBodyHtml = html 27 | }) 28 | { 29 | 30 | //to 31 | message.To.Add(toCsv); 32 | 33 | //cc 34 | if (!string.IsNullOrEmpty(ccCsv)) 35 | { 36 | message.CC.Add(ccCsv); 37 | } 38 | 39 | //bcc 40 | if (!string.IsNullOrEmpty(bccCsv)) 41 | { 42 | message.Bcc.Add(bccCsv); 43 | } 44 | 45 | message.Body = body; 46 | 47 | var smtp = new SmtpClient(); 48 | smtp.Send(message); 49 | } 50 | } 51 | 52 | /// 53 | /// Basic helper to format a given key/value pair into something useable in an HTML email. 54 | /// 55 | /// The key. 56 | /// The value. 57 | /// if set to true [add extra break]. 58 | /// 59 | public static string FormatField(string key, string value, bool addExtraBreak = false) 60 | { 61 | if (addExtraBreak) 62 | { 63 | return string.Format("{0}:
\n
{1}
", key, value); 64 | } 65 | 66 | return string.Format("{0}: {1}
", key, value); 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42MigrationHelper/MigrationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Reflection; 5 | using G42.UmbracoGrease.G42MigrationHelper.Migrations; 6 | using Umbraco.Core.Logging; 7 | 8 | namespace G42.UmbracoGrease.G42MigrationHelper 9 | { 10 | /// 11 | /// Class that is used to facilitate the migrations. 12 | /// 13 | public class MigrationHelper 14 | { 15 | /// 16 | /// Handles the migrations by using reflection to grab instances of MigrationBase. 17 | /// 18 | /// The current version. 19 | public static void HandleMigrations(Version currentVersion) 20 | { 21 | LogHelper.Info("Handling migrations based on current version =>" + currentVersion.ToString()); 22 | 23 | var migrations = typeof (MigrationBase) 24 | .Assembly.GetTypes() 25 | .Where(t => t.IsSubclassOf(typeof (MigrationBase)) && !t.IsAbstract) 26 | .Select(t => (MigrationBase) Activator.CreateInstance(t)) 27 | .OrderBy(x => x.TargetVersion); 28 | 29 | LogHelper.Info("Total Migrations=>" + migrations.Count()); 30 | 31 | foreach (var migration in migrations) 32 | { 33 | LogHelper.Info("Examining migration =>" + migration.TargetVersion); 34 | 35 | if (migration.TargetVersion > currentVersion) 36 | { 37 | LogHelper.Info("Executing..."); 38 | 39 | try 40 | { 41 | migration.Excecute(); 42 | } 43 | catch (Exception ex) 44 | { 45 | LogHelper.Error("Migration failed: " + migration.GetType() + "\n" + ex.Message, ex); 46 | } 47 | } 48 | else 49 | { 50 | LogHelper.Info("Skipped."); 51 | } 52 | } 53 | } 54 | 55 | /// 56 | /// Helper that gets the DLL version. 57 | /// 58 | /// 59 | public static string GetDllVersion() 60 | { 61 | var asm = Assembly.GetExecutingAssembly(); 62 | var fvi = FileVersionInfo.GetVersionInfo(asm.Location); 63 | 64 | return fvi.FileVersion; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42ErrorReporting/GreaseUmbracoApplication.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using G42.UmbracoGrease.Core; 4 | using G42.UmbracoGrease.Interfaces; 5 | using Umbraco.Core; 6 | using Umbraco.Core.Logging; 7 | using Umbraco.Web; 8 | 9 | namespace G42.UmbracoGrease.G42ErrorReporting 10 | { 11 | /// 12 | /// Custom UmbracoApplication that extends the typical Global.asax implementations on MVC sites. 13 | /// 14 | public class GreaseUmbracoApplication : UmbracoApplication 15 | { 16 | /// 17 | /// Extension point that handles uncaught errors. 18 | /// 19 | /// The source of the event. 20 | /// The instance containing the event data. 21 | protected new void Application_Error(object sender, EventArgs e) 22 | { 23 | base.Application_Error(sender, e); 24 | 25 | var enabled = Grease.Services.G42AppSettingsService.GetValue(Core.Constants.ERROR_REPORTING_ENABLE_KEY); 26 | 27 | if(enabled) 28 | { 29 | var lastError = Server.GetLastError(); 30 | 31 | if (lastError != null) 32 | { 33 | var errorHandlerType = PluginManager.Current.ResolveTypes().FirstOrDefault(x => x != typeof (DefaultErrorHandler)); 34 | 35 | if (errorHandlerType == null) 36 | { 37 | LogHelper.Info("Executing default error handler..."); 38 | 39 | var errorHandler = new DefaultErrorHandler(); 40 | 41 | try 42 | { 43 | errorHandler.Execute(sender, e, lastError); 44 | } 45 | catch (Exception ex) 46 | { 47 | LogHelper.Error(ex.Message, ex); 48 | } 49 | 50 | LogHelper.Info("Finished executing default handler."); 51 | } 52 | else 53 | { 54 | var errorHandler = ((IG42ErrorHandler) Activator.CreateInstance(errorHandlerType)); 55 | 56 | LogHelper.Info("Using " + errorHandler.GetType()); 57 | 58 | errorHandler.Execute(sender, e, lastError); 59 | } 60 | } 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42MigrationHelper/Migrations/TargetVersionOneZeroZero.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.G42404Helper.Repositories; 4 | 5 | namespace G42.UmbracoGrease.G42MigrationHelper.Migrations 6 | { 7 | public class TargetVersionOneZeroZero : MigrationBase 8 | { 9 | 10 | public override Version TargetVersion 11 | { 12 | get { return new Version("1.0.0"); } 13 | } 14 | 15 | public override void Excecute() 16 | { 17 | using (var unitOfWork = new PetaPocoUnitOfWork()) 18 | { 19 | Console.WriteLine("Adding paths table..."); 20 | unitOfWork.Database.Execute(@" 21 | CREATE TABLE [dbo].[G42Grease404TrackerDomainPaths]( 22 | [id] [bigint] IDENTITY(1,1) NOT NULL, 23 | [domain] [nvarchar](max) NOT NULL, 24 | [path] [nvarchar](max) NOT NULL, 25 | [addedOn] [datetime] NOT NULL, 26 | [lastVisited] [datetime] NULL, 27 | CONSTRAINT [PK_G42Grease404TrackerDomainPaths] PRIMARY KEY CLUSTERED 28 | ( 29 | [id] ASC 30 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 31 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 32 | "); 33 | 34 | unitOfWork.Database.Execute(@" 35 | DROP TABLE [G42Grease404Tracker] 36 | "); 37 | 38 | Console.WriteLine("Adding tracker table..."); 39 | 40 | unitOfWork.Database.Execute(@" 41 | CREATE TABLE [dbo].[G42Grease404Tracker]( 42 | [id] [bigint] IDENTITY(1,1) NOT NULL, 43 | [domainPathId] [bigint] NOT NULL, 44 | [referrer] [nvarchar](255) NOT NULL, 45 | [userAgent] [nvarchar](max) NULL, 46 | [addedOn] [datetime] NOT NULL, 47 | [ipAddress] [nvarchar](50) NULL, 48 | CONSTRAINT [PK_G42Grease404Tracker] PRIMARY KEY CLUSTERED 49 | ( 50 | [id] ASC 51 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 52 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 53 | "); 54 | 55 | unitOfWork.Commit(); 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Helpers/WebHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | using System.Web; 4 | 5 | namespace G42.UmbracoGrease.Helpers 6 | { 7 | /// 8 | /// Helper class that performs HTTP requests on behalf of the application. 9 | /// 10 | public static class WebHelper 11 | { 12 | /// 13 | /// Gets the specified URL async and returns the response as a response string. 14 | /// 15 | /// The URL. 16 | /// 17 | public static async Task Get(string url) 18 | { 19 | using (var client = new HttpClient()) 20 | { 21 | return await client.GetStringAsync(url).ConfigureAwait(false); 22 | } 23 | } 24 | 25 | /// 26 | /// Posts to the specified URL async and returns the response as a string. 27 | /// 28 | /// The URL. 29 | /// The content. 30 | /// 31 | public static async Task Post(string url, HttpContent content) 32 | { 33 | using (var client = new HttpClient()) 34 | { 35 | var response = await client.PostAsync(url, content).ConfigureAwait(false); 36 | 37 | return await response.Content.ReadAsStringAsync(); 38 | } 39 | } 40 | 41 | /// 42 | /// Gets the headers of the current request. Optionally add HTML formatting and skip cookies in the output. 43 | /// 44 | /// if set to true use HTML. 45 | /// if set to true skip cookies. 46 | /// 47 | public static string GetHeaders(bool useHtml = true, bool skipCookies = true) 48 | { 49 | var request = HttpContext.Current.Request; 50 | 51 | var headers = ""; 52 | 53 | foreach (var key in request.Headers.AllKeys) 54 | { 55 | if (skipCookies && key.ToLower() == "cookie") 56 | { 57 | continue; 58 | } 59 | 60 | var open = ""; 61 | var close = ""; 62 | 63 | if (useHtml) 64 | { 65 | open = "

"; 66 | close = "

"; 67 | } 68 | 69 | headers += string.Format("{2}{0}=>{1}{3}\n", key, request.Headers[key], open, close); 70 | } 71 | 72 | return headers; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # G42.UmbracoGrease 2 | ![Grease](assets/logo.png) 3 | 4 | ##Manual## 5 | Get to the manual [here](https://github.com/kgiszewski/G42.UmbracoGrease-Manual). 6 | 7 | ##V1 Breaking Changes## 8 | If you're upgrading from the `0.x.x` versions, you'll want to review the huge list of breaking changes: https://github.com/kgiszewski/G42.UmbracoGrease/pull/1 9 | 10 | ##Overview## 11 | Grease is the stuff that's too dirty for the Umbraco core, but adds functionality that lots of ordinary websites can use. By dirty we don't mean (low-quality), we just mean it's a collection of specific one-off type plugins, helpers and extensions. 12 | 13 | For example, Grease currently offers the following functionality: 14 | 15 | * Tab Hider - Place this on a document type tab and it'll hide the entire tab. Useful for document types designed to be data nodes only (no templates) that don't need the `Properties` tab. 16 | * Helpers 17 | * `NodeHelper`, great for multi-site installs like white label sites. Reference your key nodes like `NodeHelper.Instance.CurrentSite.Home` instead of `Model.Content.AncestorOrSelf(2)`. Cached for speed. 18 | * `RedirectHelper`, great for redirecting pages and executes just before a 404 would be issued otherwise. Add redirects by adding a simple piece of content to the content tree. 19 | * `FormHelper`, great for sending emails. 20 | * `TransformationHelper`, render a partial as a string. Great for altering how the RTE would normally render things like image markup. 21 | * `WebHelper` - Easily `GET` a URL and return the resource as a string. 22 | * `IpHelper` - Easily get the IP address of the visitor. 23 | * `DbHelper` - Easily get a reference to the Umbraco DB conntection. 24 | * AppSettings - Store key/value pairs cached (sort of like Redis). 25 | * 404 Tracker - A dashboard of URL's that have 404'd (supports multisite). 26 | * Search Tracker - A dashboard of keyword usage from internal searches (supports multisite). 27 | * Email 500 errors - Debounced at an internval of 15 minutes, admins can get notified when something bad happens. 28 | * String, Int and IPublishedContent Extensions such as (just examples): 29 | * `ToHumanReadableBytes(this int len)` - Outputs 1.25MB 30 | * `TruncateAtWord(this string text, int maxCharacters, string trailingStringIfTextCut = "…")` - Outputs words at a specified character limit but won't split a word in half, adds an ellipsis or custom character at the end. For teasers and search results. 31 | * `ToOrdinal(this int input)` - Outputs `1st, 2nd, 3rd, etc` 32 | * `ToAzureBlobUrl()` - Outputs Azure based blob storage media with properly formatted URL's. 33 | 34 | ##Install 35 | Install with NuGet: `Install-Package G42.UmbracoGrease` https://www.nuget.org/packages/G42.UmbracoGrease/ 36 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/MigrationTests/ZeroSixTwo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Extensions; 4 | using G42.UmbracoGrease.G42MigrationHelper.Migrations; 5 | using NUnit.Framework; 6 | 7 | namespace G42.UmbracoGrease.Tests.MigrationTests 8 | { 9 | [Category("Migrations")] 10 | [TestFixture] 11 | public class ZeroSixTwo 12 | { 13 | [TestFixtureSetUp] 14 | public void Setup() 15 | { 16 | PetaPocoUnitOfWork.ConnectionString = "testDb"; 17 | 18 | using (var uow = new PetaPocoUnitOfWork()) 19 | { 20 | //remove current tables 21 | if (uow.Database.DoesTableExist("G42Grease404Tracker")) 22 | { 23 | Console.WriteLine("Removing Tracker..."); 24 | uow.Database.Execute(@"DROP TABLE [G42Grease404Tracker]"); 25 | } 26 | 27 | //add v0.6.5 table 28 | uow.Database.Execute(@" 29 | CREATE TABLE [dbo].[G42Grease404Tracker]( 30 | [id] [bigint] IDENTITY(1,1) NOT NULL, 31 | [domain] [nvarchar](50) NOT NULL, 32 | [path] [nvarchar](255) NOT NULL, 33 | [referrer] [nvarchar](255) NOT NULL, 34 | [userAgent] [nvarchar](max) NULL, 35 | [updatedOn] [datetime] NOT NULL, 36 | CONSTRAINT [PK_G42Grease404Tracker] PRIMARY KEY CLUSTERED 37 | ( 38 | [id] ASC 39 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 40 | ) 41 | "); 42 | 43 | uow.Commit(); 44 | } 45 | } 46 | 47 | [Test] 48 | public void Can_Migrate_To_Zero_Six_Two() 49 | { 50 | //assert domain/path table does not exists 51 | using (var uow = new PetaPocoUnitOfWork()) 52 | { 53 | Assert.That(uow.Database.DoesTableExist("G42Grease404Tracker")); 54 | } 55 | 56 | var migration = new TargetVersionZeroSixTwo(); 57 | 58 | migration.Excecute(); 59 | 60 | using (var uow = new PetaPocoUnitOfWork()) 61 | { 62 | var sql = @" 63 | SELECT object_id FROM sys.columns 64 | WHERE Name = N'ipAddress' AND Object_ID = Object_ID(N'G42Grease404Tracker') 65 | "; 66 | 67 | var result = uow.Database.ExecuteScalar(sql); 68 | 69 | Assert.That(result != 0); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42NodeHelper/Models/Site.cs: -------------------------------------------------------------------------------- 1 | using umbraco.cms.businesslogic.web; 2 | using Umbraco.Core.Models; 3 | using Umbraco.Web; 4 | 5 | namespace G42.UmbracoGrease.G42NodeHelper.Models 6 | { 7 | /// 8 | /// Model that represents a default website. 9 | /// 10 | public class Site 11 | { 12 | public string Domain { get; set; } 13 | public int RootId { get; set; } 14 | 15 | /// 16 | /// Gets the root. 17 | /// 18 | /// 19 | /// The root IPublishedContent. 20 | /// 21 | public IPublishedContent Root 22 | { 23 | get 24 | { 25 | return GetUmbracoContent(RootId); 26 | } 27 | } 28 | 29 | public int HomeId { get; set; } 30 | /// 31 | /// Gets the home. 32 | /// 33 | /// 34 | /// The home IPublishedContent. 35 | /// 36 | public IPublishedContent Home 37 | { 38 | get 39 | { 40 | return GetUmbracoContent(HomeId); 41 | } 42 | } 43 | 44 | public int SiteSettingsId { get; set; } 45 | /// 46 | /// Gets the site settings. 47 | /// 48 | /// 49 | /// The site settings IPublishedContent. 50 | /// 51 | public IPublishedContent SiteSettings 52 | { 53 | get 54 | { 55 | return GetUmbracoContent(SiteSettingsId); 56 | } 57 | } 58 | 59 | /// 60 | /// Gets the content of the umbraco given an Id. 61 | /// 62 | /// The identifier. 63 | /// 64 | protected IPublishedContent GetUmbracoContent(int id) 65 | { 66 | return new UmbracoHelper(UmbracoContext.Current).TypedContent(id); 67 | } 68 | 69 | /// 70 | /// This method provides extension point to modify the NodeHelper behavior. 71 | /// 72 | /// The site. 73 | /// The domain. 74 | /// The root node. 75 | /// The site root node. 76 | /// The site settings. 77 | /// 78 | public virtual Site MapSite(Site site, Domain domain, IPublishedContent rootNode, IPublishedContent siteRootNode, IPublishedContent siteSettings) 79 | { 80 | return site; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Backoffice/G42UmbracoGreaseTree/search-tracker-dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

{{model.Name}}

6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 |
16 | 17 |
18 | LOADING... 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 48 | 49 | 50 |
DomainKeywords
There are no results.
{{data.domain}} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
KeywordLast TriedCount
{{keyword.keyword}}{{keyword.lastUsedOn}}{{keyword.count}}
47 |
51 |
52 |
53 |
54 |
55 |
-------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Extensions/ExamineExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text.RegularExpressions; 4 | using Examine; 5 | using Umbraco.Core.Logging; 6 | 7 | namespace G42.UmbracoGrease.Extensions 8 | { 9 | /// 10 | /// Extensions for Examine. 11 | /// 12 | public static class ExamineExtensions 13 | { 14 | /// 15 | /// Gets an Exmaine result by Umbraco node Id. 16 | /// 17 | /// The examine manager. 18 | /// The identifier. 19 | /// The search provider collection. 20 | /// 21 | public static SearchResult GetResultById(this ExamineManager examineManager, int id, string searchProviderCollection) 22 | { 23 | var searcher = ExamineManager.Instance.SearchProviderCollection[searchProviderCollection]; 24 | var searchCriteria = searcher.CreateSearchCriteria(Examine.SearchCriteria.BooleanOperation.Or); 25 | 26 | var query = searchCriteria.RawQuery(string.Format("+__NodeId: {0}", id)); 27 | 28 | return searcher.Search(query).FirstOrDefault(); 29 | } 30 | 31 | /// 32 | /// Gets the results by raw query. 33 | /// 34 | /// The examine manager. 35 | /// The examine query. 36 | /// The search provider collection. 37 | /// 38 | public static IEnumerable GetResultsByRawQuery(this ExamineManager examineManager, string examineQuery, string searchProviderCollection) 39 | { 40 | var searcher = ExamineManager.Instance.SearchProviderCollection[searchProviderCollection]; 41 | var searchCriteria = searcher.CreateSearchCriteria(Examine.SearchCriteria.BooleanOperation.Or); 42 | 43 | //LogHelper.Info(examineQuery); 44 | 45 | var query = searchCriteria.RawQuery(examineQuery); 46 | 47 | return searcher.Search(query); 48 | } 49 | 50 | /// 51 | /// Converts a string to an Examine friendly string of alphanumeric, space and dash only. Optionally pass a replacement character. 52 | /// 53 | /// The input. 54 | /// The replacement character. 55 | /// 56 | public static string ToExamineFriendly(this string input, char replacementCharacter = ' ') 57 | { 58 | input = input.Trim(); 59 | 60 | var rgx = new Regex("[^a-zA-Z0-9 -]"); 61 | 62 | return rgx.Replace(input, replacementCharacter.ToString()).Trim(replacementCharacter); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42AppSettings/Repositories/G42AppSettingRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using G42.UmbracoGrease.Core; 4 | using G42.UmbracoGrease.G42AppSettings.Models; 5 | using Umbraco.Core.Logging; 6 | using Umbraco.Core.Persistence; 7 | 8 | namespace G42.UmbracoGrease.G42AppSettings.Repositories 9 | { 10 | internal class G42AppSettingRepository 11 | { 12 | internal static G42AppSetting Get(PetaPocoUnitOfWork unitOfWork, string key) 13 | { 14 | return unitOfWork.Database.SingleOrDefault(@" 15 | WHERE [key] = @0 16 | ", key); 17 | } 18 | 19 | internal static G42AppSetting Save(PetaPocoUnitOfWork unitOfWork, G42AppSetting appSetting) 20 | { 21 | appSetting.UpdatedOn = DateTime.UtcNow; 22 | 23 | unitOfWork.Database.Save(appSetting); 24 | 25 | return appSetting; 26 | } 27 | 28 | internal static IEnumerable GetErrorReportingConfigs(PetaPocoUnitOfWork unitOfWork) 29 | { 30 | return unitOfWork.Database.Fetch(@" 31 | WHERE [key] LIKE @0 32 | ", string.Format("{0}%" , Constants.ERROR_REPORTING_KEY_PREFIX)); 33 | } 34 | 35 | internal static IEnumerable Get404TrackerConfigs(PetaPocoUnitOfWork unitOfWork) 36 | { 37 | return unitOfWork.Database.Fetch(@" 38 | WHERE [key] LIKE @0 39 | ", string.Format("{0}%" , Constants._404_TRACKER_KEY_PREFIX)); 40 | } 41 | 42 | internal static IEnumerable GetGeneralConfigs(PetaPocoUnitOfWork unitOfWork) 43 | { 44 | return unitOfWork.Database.Fetch(@" 45 | WHERE [key] LIKE @0 46 | ", string.Format("{0}%", Constants.GENERAL_KEY_PREFIX)); 47 | } 48 | 49 | internal static void CreateTable(PetaPocoUnitOfWork unitOfWork) 50 | { 51 | if (!unitOfWork.Database.TableExist("G42GreaseAppSettings")) 52 | { 53 | LogHelper.Info("Creating table."); 54 | 55 | unitOfWork.Database.Execute(@" 56 | CREATE TABLE [dbo].[G42GreaseAppSettings]( 57 | [id] [bigint] IDENTITY(1,1) NOT NULL, 58 | [key] [nvarchar](150) NOT NULL, 59 | [value] [nvarchar](150) NOT NULL, 60 | [updatedOn] [datetime] NOT NULL, 61 | CONSTRAINT [PK_GreaseAppSettings] PRIMARY KEY CLUSTERED 62 | ( 63 | [id] ASC 64 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 65 | ) 66 | "); 67 | } 68 | else 69 | { 70 | LogHelper.Info("Table exists."); 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/MigrationTests/OneZeroZero.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Extensions; 4 | using G42.UmbracoGrease.G42MigrationHelper.Migrations; 5 | using NUnit.Framework; 6 | 7 | namespace G42.UmbracoGrease.Tests.MigrationTests 8 | { 9 | [Category("Migrations")] 10 | [TestFixture] 11 | public class OneZeroZero 12 | { 13 | [TestFixtureSetUp] 14 | public void Setup() 15 | { 16 | PetaPocoUnitOfWork.ConnectionString = "testDb"; 17 | 18 | using (var uow = new PetaPocoUnitOfWork()) 19 | { 20 | //remove current tables 21 | if (uow.Database.DoesTableExist("G42Grease404Tracker")) 22 | { 23 | Console.WriteLine("Removing Tracker..."); 24 | uow.Database.Execute(@"DROP TABLE [G42Grease404Tracker]"); 25 | } 26 | 27 | if (uow.Database.DoesTableExist("G42Grease404TrackerDomainPaths")) 28 | { 29 | Console.WriteLine("Removing Domain\\Paths..."); 30 | uow.Database.Execute(@"DROP TABLE [G42Grease404TrackerDomainPaths]"); 31 | } 32 | 33 | //add v0.6.5 table 34 | uow.Database.Execute(@" 35 | CREATE TABLE [dbo].[G42Grease404Tracker]( 36 | [id] [bigint] IDENTITY(1,1) NOT NULL, 37 | [domain] [nvarchar](50) NOT NULL, 38 | [path] [nvarchar](255) NOT NULL, 39 | [referrer] [nvarchar](255) NOT NULL, 40 | [userAgent] [nvarchar](max) NULL, 41 | [ipAddress] [nvarchar](50) DEFAULT NULL, 42 | [updatedOn] [datetime] NOT NULL, 43 | CONSTRAINT [PK_G42Grease404Tracker] PRIMARY KEY CLUSTERED 44 | ( 45 | [id] ASC 46 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 47 | ) 48 | "); 49 | 50 | uow.Commit(); 51 | } 52 | } 53 | 54 | [Test] 55 | public void Can_Migrate_To_One_Zero_Zero() 56 | { 57 | //assert domain/path table does not exist 58 | using (var uow = new PetaPocoUnitOfWork()) 59 | { 60 | Assert.That(uow.Database.DoesTableExist("G42Grease404Tracker")); 61 | Assert.That(!uow.Database.DoesTableExist("G42Grease404TrackerDomainPaths")); 62 | } 63 | 64 | var migration = new TargetVersionOneZeroZero(); 65 | 66 | migration.Excecute(); 67 | 68 | using (var uow = new PetaPocoUnitOfWork()) 69 | { 70 | Assert.That(uow.Database.DoesTableExist("G42Grease404Tracker")); 71 | Assert.That(uow.Database.DoesTableExist("G42Grease404TrackerDomainPaths")); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42AppSettings/Services/G42AppSettingsService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.G42AppSettings.Models; 4 | using G42.UmbracoGrease.G42AppSettings.Repositories; 5 | 6 | namespace G42.UmbracoGrease.G42AppSettings.Services 7 | { 8 | public class G42AppSettingsService 9 | { 10 | public T GetValue(string key) 11 | { 12 | using(var uow = new PetaPocoUnitOfWork()) 13 | { 14 | var setting = G42AppSettingRepository.Get(uow, key); 15 | 16 | if (setting != null) 17 | { 18 | var value = setting.Value; 19 | 20 | var convertingToType = typeof (T); 21 | 22 | if (convertingToType == typeof (bool)) 23 | { 24 | if (value == "1") 25 | { 26 | return (T) (object) true; 27 | } 28 | 29 | if (value == "0") 30 | { 31 | return (T) (object) false; 32 | } 33 | } 34 | 35 | return (T)Convert.ChangeType(value, convertingToType); 36 | } 37 | 38 | return default(T); 39 | } 40 | } 41 | 42 | public void CreateAppSettingsTable() 43 | { 44 | using (var uow = new PetaPocoUnitOfWork()) 45 | { 46 | G42AppSettingRepository.CreateTable(uow); 47 | } 48 | } 49 | 50 | internal static void SaveSetting(PetaPocoUnitOfWork uow, string key, string value, string defaultValue = "") 51 | { 52 | value = value ?? defaultValue; 53 | 54 | //some special cases 55 | if (key == Constants.ERROR_REPORTING_REPORTING_INTERVAL_KEY) 56 | { 57 | int reportingIntervalValue; 58 | 59 | if (!int.TryParse(value, out reportingIntervalValue) || reportingIntervalValue <= 0) 60 | { 61 | value = Constants.ERROR_REPORTING_DEFAULT_INTERVAL.ToString(); 62 | } 63 | } 64 | 65 | if (key == Constants._404_TRACKER_DEFAULT_DAYS_TO_RETAIN_KEY) 66 | { 67 | int reportingIntervalValue; 68 | 69 | if (!int.TryParse(value, out reportingIntervalValue) || reportingIntervalValue <= 0) 70 | { 71 | value = Constants._404_TRACKER_DEFAULT_DAYS_TO_RETAIN.ToString(); 72 | } 73 | } 74 | 75 | 76 | //get on with the saving bit 77 | var setting = G42AppSettingRepository.Get(uow, key); 78 | 79 | if (setting == null) 80 | { 81 | setting = new G42AppSetting 82 | { 83 | Key = key, 84 | Value = value 85 | }; 86 | } 87 | else 88 | { 89 | setting.Value = value; 90 | } 91 | 92 | G42AppSettingRepository.Save(uow, setting); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42ErrorReporting/Models/G42ErrorReportingConfigModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using G42.UmbracoGrease.Core; 4 | using Newtonsoft.Json; 5 | using G42.UmbracoGrease.G42AppSettings.Models; 6 | 7 | namespace G42.UmbracoGrease.G42ErrorReporting.Models 8 | { 9 | public class G42ErrorReportingConfigModel 10 | { 11 | [JsonProperty("enable")] 12 | public bool Enable { get; set; } 13 | [JsonProperty("reportingInterval")] 14 | public int ReportingInterval { get; set; } 15 | [JsonProperty("slackEnable")] 16 | public bool SlackEnable { get; set; } 17 | [JsonProperty("slackWebhookUrl")] 18 | public string SlackWebhookUrl { get; set; } 19 | [JsonProperty("slackBotName")] 20 | public string SlackBotName { get; set; } 21 | [JsonProperty("slackChannel")] 22 | public string SlackChannel { get; set; } 23 | [JsonProperty("slackEmoji")] 24 | public string SlackEmoji { get; set; } 25 | 26 | public G42ErrorReportingConfigModel() 27 | { 28 | 29 | } 30 | 31 | public G42ErrorReportingConfigModel(IEnumerable settings) 32 | { 33 | var settingsList = settings.ToList(); 34 | 35 | var enable = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_ENABLE_KEY); 36 | 37 | if(enable != null) 38 | { 39 | Enable = (enable.Value == "1"); 40 | } 41 | 42 | var reportingInterval = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_REPORTING_INTERVAL_KEY); 43 | 44 | if (reportingInterval != null) 45 | { 46 | int reportingIntervalValue; 47 | 48 | int.TryParse(reportingInterval.Value, out reportingIntervalValue); 49 | 50 | ReportingInterval = reportingIntervalValue; 51 | } 52 | 53 | var slackEnable = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_SLACK_ENABLE_KEY); 54 | 55 | if (slackEnable != null) 56 | { 57 | SlackEnable = (slackEnable.Value == "1"); 58 | } 59 | 60 | var slackWebhookUrl = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_SLACK_WEBHOOKURL_KEY); 61 | 62 | if (slackWebhookUrl != null) 63 | { 64 | SlackWebhookUrl = slackWebhookUrl.Value; 65 | } 66 | 67 | var slackBotname = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_SLACK_BOTNAME_KEY); 68 | 69 | if (slackBotname != null) 70 | { 71 | SlackBotName = slackBotname.Value; 72 | } 73 | 74 | var slackChannel = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_SLACK_CHANNEL_KEY); 75 | 76 | if (slackChannel != null) 77 | { 78 | SlackChannel = slackChannel.Value; 79 | } 80 | 81 | var slackEmoji = settingsList.FirstOrDefault(x => x.Key == Constants.ERROR_REPORTING_SLACK_EMOJI_KEY); 82 | 83 | if (slackEmoji != null) 84 | { 85 | SlackEmoji = slackEmoji.Value; 86 | } 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Backoffice/G42UmbracoGreaseTree/error-reporting-dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

{{model.Name}}

6 |
7 |
8 | 9 | 10 |
11 |
12 |

This section configures the Grease error reporting feature.

13 | 14 |
15 | 16 | Tick this box to enable Error Reporting! 17 |
18 | 19 | 20 | If the same error occurs, notifications will not be sent out again until the interval has elapsed. 21 | 22 |
23 | 24 |

Slack

25 | 26 |

Use this section to send 500 errors your users encounter to a Slack channel.

27 | 28 |
29 | 30 | Tick this box to enable Slack! 31 |
32 |
33 | 34 | 35 | Register a Slack webhook URL and place it here. 36 |
37 |
38 | 39 | 40 | Specify which channel (or user) the errors appear in. 41 |
42 |
43 | 44 | 45 | Name your bot something sweet! 46 |
47 |
48 | 49 | 50 | Choose your emoji icon when posting the message. 51 |
52 | 53 |
54 | 55 |
56 |
57 |
58 |
59 |
60 |
-------------------------------------------------------------------------------- /src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease/Backoffice/G42UmbracoGreaseTree/_404-tracker-dashboard.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 |

{{model.Name}}

6 |
7 |
8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | Settings 16 |
17 | 18 |
19 |
20 |
21 | 22 | 23 | Enter a number of days to retain 404 logging information. 24 |
25 | 26 |
27 |
28 | 29 |
30 | LOADING... 31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 60 | 61 | 62 |
DomainURLS
There are no results.
{{data.domain}} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
PathLast VisitedCount
{{url.path}}{{url.lastVisited}}{{url.count}}
59 |
63 |
64 |
65 |
66 |
67 |
-------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Services/G42404Service.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web; 4 | using G42.UmbracoGrease.Core; 5 | using G42.UmbracoGrease.G42404Helper.Models; 6 | using G42.UmbracoGrease.G42404Helper.Repositories; 7 | using G42.UmbracoGrease.G42AppSettings.Repositories; 8 | using G42.UmbracoGrease.G42AppSettings.Services; 9 | using G42.UmbracoGrease.G42RedirectHelper; 10 | 11 | namespace G42.UmbracoGrease.G42404Helper.Services 12 | { 13 | public class G42404Service 14 | { 15 | public void Add(string domain = "", string path = "", string referrer = "", string userAgent = "") 16 | { 17 | if (string.IsNullOrEmpty(domain)) 18 | { 19 | var request = HttpContext.Current.Request; 20 | 21 | if (request.UrlReferrer != null) 22 | { 23 | try 24 | { 25 | referrer = request.UrlReferrer.AbsoluteUri; 26 | } 27 | catch (Exception ex) 28 | { 29 | referrer = request.UrlReferrer.ToString(); 30 | } 31 | } 32 | 33 | domain = request.Url.Host; 34 | 35 | path = RedirectHelper.GetCurrentPath(); 36 | 37 | userAgent = request.UserAgent; 38 | } 39 | 40 | using (var uow = new PetaPocoUnitOfWork()) 41 | { 42 | var domainPath = G42404Repository.GetDomainPath(uow, domain, path); 43 | 44 | if (domainPath == null) 45 | { 46 | domainPath = G42404Repository.AddDomainPath(uow, domain, path); 47 | } 48 | else 49 | { 50 | G42404Repository.TouchDomainPath(uow, domainPath); 51 | } 52 | 53 | G42404Repository.AddTracker(uow, referrer, userAgent, domainPath); 54 | 55 | uow.Commit(); 56 | } 57 | } 58 | 59 | public IEnumerable GetResults(int countFilter) 60 | { 61 | using (var uow = new PetaPocoUnitOfWork()) 62 | { 63 | return G42404Repository.GetResults(uow, countFilter); 64 | } 65 | } 66 | 67 | public void Create404TrackerTable() 68 | { 69 | using (var uow = new PetaPocoUnitOfWork()) 70 | { 71 | G42404Repository.Create404TrackerTable(uow); 72 | 73 | uow.Commit(); 74 | } 75 | } 76 | 77 | public void Create404DomainPathsTable() 78 | { 79 | using (var uow = new PetaPocoUnitOfWork()) 80 | { 81 | G42404Repository.Create404DomainPathsTable(uow); 82 | 83 | uow.Commit(); 84 | } 85 | } 86 | 87 | public G42Grease404ConfigModel Get404TrackerConfig() 88 | { 89 | using (var uow = new PetaPocoUnitOfWork()) 90 | { 91 | var rawSettings = G42AppSettingRepository.Get404TrackerConfigs(uow); 92 | 93 | return new G42Grease404ConfigModel(rawSettings); 94 | } 95 | } 96 | 97 | public bool Save404TrackerConfig(G42Grease404ConfigModel model) 98 | { 99 | using (var uow = new PetaPocoUnitOfWork()) 100 | { 101 | G42AppSettingsService.SaveSetting(uow, Constants._404_TRACKER_DEFAULT_DAYS_TO_RETAIN_KEY, model.DaysToRetain.ToString(), "90"); 102 | 103 | uow.Commit(); 104 | } 105 | 106 | return true; 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Shared/Controllers/GreaseTreeController.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Formatting; 2 | using Umbraco.Web.Models.Trees; 3 | using Umbraco.Web.Mvc; 4 | using Umbraco.Web.Trees; 5 | 6 | namespace G42.UmbracoGrease.Shared.Controllers 7 | { 8 | /// 9 | /// Tree controller that represents the tree in the custom Umbraco section. 10 | /// 11 | [PluginController("G42UmbracoGrease")] 12 | [Umbraco.Web.Trees.Tree("G42UmbracoGrease", "G42UmbracoGreaseTree", "G42 Grease", iconClosed: "icon-folder")] 13 | public class GreaseTreeController : TreeController 14 | { 15 | /// 16 | /// The method called to render the contents of the tree structure 17 | /// 18 | /// 19 | /// All of the query string parameters passed from jsTree 20 | /// 21 | protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) 22 | { 23 | var nodes = new TreeNodeCollection(); 24 | 25 | if (id == "-1") 26 | { 27 | //general 28 | _addNode(nodes, queryStrings, "General", "", "General", "icon-settings", "/G42UmbracoGrease/G42UmbracoGreaseTree/general-dashboard/General"); 29 | 30 | //404 31 | _addNode(nodes, queryStrings, "404Tracker", "", "404 Tracker", "icon-block", "/G42UmbracoGrease/G42UmbracoGreaseTree/_404-tracker-dashboard/404%20Tracker"); 32 | 33 | //error reporting 34 | _addNode(nodes, queryStrings, "ErrorReporing", "", "Error Reporting", "icon-application-error", "/G42UmbracoGrease/G42UmbracoGreaseTree/error-reporting-dashboard/Error%20Reporting"); 35 | 36 | //nodeHelper 37 | _addNode(nodes, queryStrings, "NodeHelper", "", "Node Helper", "icon-globe", "/G42UmbracoGrease/G42UmbracoGreaseTree/node-helper-dashboard/Node%20Helper"); 38 | 39 | //search 40 | _addNode(nodes, queryStrings, "SearchTracker", "", "Search Tracker", "icon-search", "/G42UmbracoGrease/G42UmbracoGreaseTree/search-tracker-dashboard/Search%20Tracker"); 41 | } 42 | 43 | return nodes; 44 | } 45 | 46 | /// 47 | /// Returns the menu structure for the node given. 48 | /// 49 | /// 50 | /// 51 | /// 52 | protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) 53 | { 54 | var menu = new MenuItemCollection(); 55 | 56 | return menu; 57 | } 58 | 59 | /// 60 | /// Helper that adds the node to the tree. 61 | /// 62 | /// The nodes. 63 | /// The query strings. 64 | /// The identifier. 65 | /// The parent identifier. 66 | /// The title. 67 | /// The icon. 68 | /// The route path. 69 | /// if set to true [has children]. 70 | /// 71 | private TreeNode _addNode(TreeNodeCollection nodes, FormDataCollection queryStrings, string id, string parentId, string title, string icon, string routePath, bool hasChildren = false) 72 | { 73 | var node = CreateTreeNode(id, parentId, queryStrings, title, icon); 74 | node.RoutePath = routePath; 75 | node.HasChildren = hasChildren; 76 | 77 | nodes.Add(node); 78 | 79 | return node; 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Events/PackageActions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Configuration; 3 | using System.Xml; 4 | using G42.UmbracoGrease.Core; 5 | using G42.UmbracoGrease.G42MigrationHelper; 6 | using umbraco.cms.businesslogic.packager; 7 | using Umbraco.Core; 8 | using Umbraco.Core.Logging; 9 | 10 | namespace G42.UmbracoGrease.Events 11 | { 12 | /// 13 | /// A startup class that handles package installation tasks. 14 | /// 15 | public class PackageActions : ApplicationEventHandler 16 | { 17 | private string _dllVersion; 18 | 19 | /// 20 | /// Overridable method to execute when Bootup is completed, this allows you to perform any other bootup logic required for the application. 21 | /// Resolution is frozen so now they can be used to resolve instances. 22 | /// 23 | /// 24 | /// 25 | protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) 26 | { 27 | base.ApplicationStarted(umbracoApplication, applicationContext); 28 | 29 | const string versionAppsettingKey = "G42.UmbracoGrease:Version"; 30 | 31 | _dllVersion = MigrationHelper.GetDllVersion(); 32 | 33 | LogHelper.Info("Determining G42.UmbracoGrease:Version to be: " + _dllVersion); 34 | 35 | if (string.IsNullOrEmpty(ConfigurationManager.AppSettings[versionAppsettingKey])) 36 | { 37 | LogHelper.Info("Running initial setup block, this assumes a fresh install and may cause issues if DB tables already exist."); 38 | _addLanguageKey(); 39 | 40 | Grease.Services.G42404Service.Create404DomainPathsTable(); 41 | Grease.Services.G42404Service.Create404TrackerTable(); 42 | Grease.Services.G42SearchService.CreateSearchTrackerKeywordsTable(); 43 | Grease.Services.G42SearchService.CreateSearchTrackerSearchesTable(); 44 | Grease.Services.G42AppSettingsService.CreateAppSettingsTable(); 45 | 46 | var config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~"); 47 | config.AppSettings.Settings.Add(versionAppsettingKey, _dllVersion); 48 | config.Save(); 49 | } 50 | else 51 | { 52 | var currentVersion = ConfigurationManager.AppSettings[versionAppsettingKey]; 53 | 54 | LogHelper.Info("Grease already installed => " + currentVersion); 55 | 56 | if (currentVersion != _dllVersion) 57 | { 58 | LogHelper.Info(string.Format("Grease upgrading {0} to {1} ", currentVersion, _dllVersion)); 59 | 60 | var config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~"); 61 | config.AppSettings.Settings.Remove(versionAppsettingKey); 62 | config.AppSettings.Settings.Add(versionAppsettingKey, _dllVersion); 63 | config.Save(); 64 | 65 | MigrationHelper.HandleMigrations(new Version(currentVersion)); 66 | } 67 | } 68 | } 69 | 70 | /// 71 | /// Adds the language key to the core XML language files. 72 | /// 73 | private void _addLanguageKey() 74 | { 75 | var xd = new XmlDocument(); 76 | 77 | xd.LoadXml(@""); 78 | 79 | LogHelper.Info("Running Grease language action."); 80 | PackageAction.RunPackageAction("G42.UmbracoGrease", "AddLanguageFileKey", xd.FirstChild); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42RedirectHelper/RedirectHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using G42.UmbracoGrease.G42RedirectHelper.Models; 6 | using Umbraco.Core.Logging; 7 | using Umbraco.Web; 8 | using Umbraco.Core.Models; 9 | 10 | namespace G42.UmbracoGrease.G42RedirectHelper 11 | { 12 | /// 13 | /// Helper class that handles redirects. 14 | /// 15 | public static class RedirectHelper 16 | { 17 | /// 18 | /// Tries to find the redirect based on the requested URL. 19 | /// 20 | /// The root redirect node. 21 | /// The redirect doctype alias. 22 | /// The map redirect function. 23 | /// 24 | public static bool TryRedirect(IPublishedContent rootRedirectNode, string redirectDoctypeAlias, Func mapRedirectFunc) 25 | { 26 | if (HttpContext.Current == null) 27 | return false; 28 | 29 | var context = HttpContext.Current; 30 | 31 | var redirects = _getRedirectConfig(rootRedirectNode, mapRedirectFunc, redirectDoctypeAlias); 32 | 33 | var redirect = redirects.FirstOrDefault(x => x.UrlToRedirect.ToLower() == GetCurrentPath().ToLower()); 34 | 35 | if (redirect != null) 36 | { 37 | LogHelper.Info(string.Format("Redirecting '{0}' to '{1}' with status {2}", redirect.UrlToRedirect, redirect.RedirectToUrl, redirect.StatusCode)); 38 | context.Response.StatusCode = redirect.StatusCode; 39 | context.Response.RedirectLocation = redirect.RedirectToUrl; 40 | context.Response.Flush(); 41 | 42 | return true; 43 | } 44 | 45 | LogHelper.Info(string.Format("No redirect found for '{0}', 404 issued", GetCurrentPath())); 46 | 47 | return false; 48 | } 49 | 50 | /// 51 | /// Sets the HTTP status to the given value. 52 | /// 53 | /// The status code. 54 | public static void SetHttpStatus(int statusCode) 55 | { 56 | if (HttpContext.Current == null) 57 | return; 58 | 59 | var context = HttpContext.Current; 60 | 61 | context.Response.StatusCode = statusCode; 62 | } 63 | 64 | /// 65 | /// Gets the current path which is useful for custom error pages. 66 | /// 67 | /// 68 | public static string GetCurrentPath() 69 | { 70 | if (HttpContext.Current == null) 71 | return ""; 72 | 73 | var context = HttpContext.Current; 74 | 75 | return context.Request.QueryString["aspxerrorpath"] ?? context.Request.RawUrl; 76 | } 77 | 78 | /// 79 | /// Gets the redirect configuration. 80 | /// 81 | /// The root redirect node. 82 | /// The map URL function. 83 | /// The redirect doctype alias. 84 | /// 85 | private static IEnumerable _getRedirectConfig(IPublishedContent rootRedirectNode, Func mapUrlFunc, string redirectDoctypeAlias) 86 | { 87 | var redirects = new List(); 88 | 89 | var redirectNodes = rootRedirectNode.Descendants().Where(x => x.DocumentTypeAlias == redirectDoctypeAlias); 90 | 91 | foreach (var redirect in redirectNodes) 92 | { 93 | redirects.Add(mapUrlFunc(redirect)); 94 | } 95 | 96 | return redirects; 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/ExtensionTests/UnitTests.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using G42.UmbracoGrease.Extensions; 3 | using NUnit.Framework; 4 | using Umbraco.Web; 5 | 6 | namespace G42.UmbracoGrease.Tests.ExtensionTests 7 | { 8 | [Category("Extensions")] 9 | [TestFixture] 10 | public class UnitTests 11 | { 12 | [TestCase("Jon Snow is Dead!", "Jon Snow is Dead", ' ')] 13 | [TestCase("Jon Snow is n@t Dead!", "Jon Snow is n_t Dead", '_')] 14 | public void Can_Make_Examine_Friendly(string input, string expectedOutput, char replacementCharacter) 15 | { 16 | var result = input.ToExamineFriendly(replacementCharacter); 17 | 18 | Assert.AreEqual(expectedOutput, result); 19 | } 20 | 21 | [TestCase(1, "1 B")] 22 | [TestCase(1024, "1 KB")] 23 | [TestCase(1048576, "1 MB")] 24 | [TestCase(1073741824, "1 GB")] 25 | [TestCase(262144000, "250 MB")] 26 | public void Can_Make_Human_Readable_Bytes(int input, string expectedOutput) 27 | { 28 | var result = input.ToHumanReadableBytes(); 29 | 30 | Assert.AreEqual(expectedOutput, result); 31 | } 32 | 33 | [TestCase(1, "1st")] 34 | [TestCase(2, "2nd")] 35 | [TestCase(3, "3rd")] 36 | [TestCase(4, "4th")] 37 | [TestCase(5, "5th")] 38 | [TestCase(6, "6th")] 39 | [TestCase(7, "7th")] 40 | [TestCase(8, "8th")] 41 | [TestCase(9, "9th")] 42 | [TestCase(10, "10th")] 43 | 44 | public void Can_Make_Ordinals(int input, string expectedOutput) 45 | { 46 | var result = input.ToOrdinal(); 47 | 48 | Assert.AreEqual(expectedOutput, result); 49 | } 50 | 51 | [TestCase("The quick brown fox jumped over the lazy dog!", "The quick brown fox jumped over the lazy dog!", 100, null)] 52 | [TestCase("The quick brown fox jumped over the lazy dog!", "The quick…", 10, null)] 53 | [TestCase("The quick brown fox jumped over the lazy dog!", "The...", 10, "...")] 54 | public void Can_Truncate_At_Word(string input, string expectedOutput, int maxCharacters, string suffix) 55 | { 56 | var result = ""; 57 | 58 | if (string.IsNullOrEmpty(suffix)) 59 | { 60 | result = input.TruncateAtWord(maxCharacters); 61 | } 62 | else 63 | { 64 | result = input.TruncateAtWord(maxCharacters, suffix); 65 | } 66 | 67 | Assert.AreEqual(expectedOutput, result); 68 | } 69 | 70 | [TestCase("The quick brown fox jumped over the lazy dog!", "dog", "The quick brown fox jumped over the lazy dog!")] 71 | [TestCase("The quick brown fox jumped over the lazy dog!", "dog fox", "The quick brown fox jumped over the lazy dog!")] 72 | public void Can_Hightlight_Words(string input, string query, string expectedOutput) 73 | { 74 | var result = input.HighlightKeywords(query); 75 | 76 | Assert.AreEqual(expectedOutput, result); 77 | } 78 | 79 | [TestCase("foo", "foo")] 80 | [TestCase("foo&bar", "")] 81 | [TestCase("!@#$%^", "!@#$%^")] 82 | public void Can_Make_An_Xml_Safe_String(string input, string expectedOutput) 83 | { 84 | var result = input.ToXmlSafeString(); 85 | 86 | Assert.AreEqual(expectedOutput, result); 87 | } 88 | 89 | [TestCase("http://foo.local", "https://foo.local")] 90 | [TestCase("https://foo.local", "https://foo.local")] 91 | public void Can_Make_Https_Url(string input, string expectedOutput) 92 | { 93 | var result = input.ToHttpsUrl(); 94 | 95 | Assert.AreEqual(expectedOutput, result); 96 | } 97 | 98 | [TestCase("http://foo.local/bar.jpg", "http://foo.local/bar.jpg?mode=pad", false)] 99 | [TestCase("https://foo.local/bar.jpg", "https://foo.local/bar.jpg?mode=pad", true)] 100 | [TestCase("https://foo.com/bar.jpg", "https://foo.com/remote.axd/foo.com/bar.jpg?mode=pad", true)] 101 | public void Can_Transform_To_Azure_Blob_Url(string input, string expectedOutput, bool useSameProtocolAsRequest) 102 | { 103 | var context = new HttpContext(new HttpRequest(null, input, null), new HttpResponse(null)); 104 | 105 | var result = input.GetCropUrl().ToAzureBlobUrl(useSameProtocolAsRequest, context); 106 | 107 | Assert.AreEqual(expectedOutput, result); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease.Tests/MigrationTests/OneZeroThree.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using G42.UmbracoGrease.Core; 3 | using G42.UmbracoGrease.Extensions; 4 | using G42.UmbracoGrease.G42MigrationHelper.Migrations; 5 | using NUnit.Framework; 6 | 7 | namespace G42.UmbracoGrease.Tests.MigrationTests 8 | { 9 | [TestFixture] 10 | [Category("Migrations")] 11 | public class OneZeroThree 12 | { 13 | [TestFixtureSetUp] 14 | public void Setup() 15 | { 16 | PetaPocoUnitOfWork.ConnectionString = "testDb"; 17 | 18 | using (var uow = new PetaPocoUnitOfWork()) 19 | { 20 | //remove current tables 21 | if (uow.Database.DoesTableExist("G42Grease404Tracker")) 22 | { 23 | Console.WriteLine("Removing Tracker..."); 24 | uow.Database.Execute(@"DROP TABLE [G42Grease404Tracker]"); 25 | } 26 | 27 | if (uow.Database.DoesTableExist("G42Grease404TrackerDomainPaths")) 28 | { 29 | Console.WriteLine("Removing Domain\\Paths..."); 30 | uow.Database.Execute(@"DROP TABLE [G42Grease404TrackerDomainPaths]"); 31 | } 32 | 33 | //add v1.0.0 tables 34 | uow.Database.Execute(@" 35 | CREATE TABLE [dbo].[G42Grease404Tracker]( 36 | [id] [bigint] IDENTITY(1,1) NOT NULL, 37 | [referrer] [nvarchar](max) NULL, 38 | [userAgent] [nvarchar](max) NULL, 39 | [addedOn] [datetime] NOT NULL, 40 | [ipAddress] [nvarchar](50) NULL, 41 | [domainPathId] [bigint] NOT NULL, 42 | CONSTRAINT [PK_G42Grease404Tracker] PRIMARY KEY CLUSTERED 43 | ( 44 | [id] ASC 45 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 46 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 47 | "); 48 | 49 | uow.Database.Execute(@" 50 | CREATE TABLE [dbo].[G42Grease404TrackerDomainPaths]( 51 | [id] [bigint] IDENTITY(1,1) NOT NULL, 52 | [domain] [nvarchar](max) NOT NULL, 53 | [path] [nvarchar](max) NOT NULL, 54 | [addedOn] [datetime] NOT NULL, 55 | [lastVisited] [datetime] NULL, 56 | CONSTRAINT [PK_G42Grease404TrackerDomainPaths] PRIMARY KEY CLUSTERED 57 | ( 58 | [id] ASC 59 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 60 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 61 | "); 62 | 63 | uow.Commit(); 64 | } 65 | } 66 | 67 | [Test] 68 | public void Can_Migrate_To_One_Zero_Three() 69 | { 70 | //assert domain/path table does not exist 71 | using (var uow = new PetaPocoUnitOfWork()) 72 | { 73 | Assert.That(uow.Database.DoesTableExist("G42Grease404Tracker")); 74 | Assert.That(uow.Database.DoesTableExist("G42Grease404TrackerDomainPaths")); 75 | } 76 | 77 | var migration = new TargetVersionOneZeroThree(); 78 | 79 | migration.Excecute(); 80 | 81 | //assert that the columns are altered 82 | 83 | using (var uow = new PetaPocoUnitOfWork()) 84 | { 85 | var sql = @" 86 | SELECT CHARACTER_MAXIMUM_LENGTH 87 | FROM INFORMATION_SCHEMA.COLUMNS 88 | WHERE 89 | TABLE_NAME = 'G42Grease404TrackerDomainPaths' AND 90 | COLUMN_NAME = 'domain' 91 | "; 92 | 93 | var result = uow.Database.ExecuteScalar(sql); 94 | 95 | Assert.AreEqual(75, result); 96 | 97 | sql = @" 98 | SELECT CHARACTER_MAXIMUM_LENGTH 99 | FROM INFORMATION_SCHEMA.COLUMNS 100 | WHERE 101 | TABLE_NAME = 'G42Grease404TrackerDomainPaths' AND 102 | COLUMN_NAME = 'path' 103 | "; 104 | 105 | result = uow.Database.ExecuteScalar(sql); 106 | 107 | Assert.AreEqual(255, result); 108 | 109 | sql = @" 110 | SELECT CONSTRAINT_NAME 111 | FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 112 | WHERE CONSTRAINT_NAME ='IX_G42Grease404TrackerDomainPaths' 113 | "; 114 | 115 | var constraintResult = uow.Database.ExecuteScalar(sql); 116 | 117 | Assert.AreEqual("IX_G42Grease404TrackerDomainPaths", constraintResult); 118 | } 119 | 120 | //assert that the index is added 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | require('load-grunt-tasks')(grunt); 3 | var path = require('path') 4 | 5 | grunt.initConfig({ 6 | pkg: grunt.file.readJSON('package.json'), 7 | pkgMeta: grunt.file.readJSON('config/meta.json'), 8 | dest: grunt.option('target') || 'dist', 9 | basePath: path.join('<%= dest %>', 'App_Plugins', '<%= pkgMeta.name %>'), 10 | 11 | watch: { 12 | options: { 13 | spawn: false, 14 | atBegin: true 15 | }, 16 | app_plugins: { 17 | files: ['src/**/**'], 18 | tasks: ['copy:app_plugins', 'copy:views'] 19 | }, 20 | dll: { 21 | files: ['src/**/*.cs'], 22 | tasks: ['msbuild:dist', 'copy:dll'] 23 | } 24 | }, 25 | 26 | copy: { 27 | app_plugins: { 28 | cwd: 'src/G42.UmbracoGrease/App_Plugins/G42UmbracoGrease', 29 | src: ['**'], 30 | dest: '<%= basePath %>', 31 | expand: true 32 | }, 33 | views: { 34 | cwd: 'src/G42.UmbracoGrease/Views', 35 | src: ['**'], 36 | dest: '<%= dest %>/Views', 37 | expand: true 38 | }, 39 | dll: { 40 | cwd: 'src/G42.UmbracoGrease/bin', 41 | src: 'G42.UmbracoGrease.dll', 42 | dest: '<%= dest %>/bin/', 43 | expand: true 44 | }, 45 | actionsDll: { 46 | cwd: 'lib/', 47 | src: 'PackageActionsContrib.dll', 48 | dest: '<%= dest %>/bin/', 49 | expand: true 50 | }, 51 | nuget: { 52 | files: [ 53 | { 54 | cwd: '<%= dest %>/App_Plugins', 55 | src: ['**/*', '!bin', '!bin/*'], 56 | dest: 'tmp/nuget/content/App_Plugins', 57 | expand: true 58 | }, 59 | { 60 | cwd: '<%= dest %>/G42.UmbracoGrease/', 61 | src: ['**/*'], 62 | dest: 'tmp/nuget/content/G42.UmbracoGrease', 63 | expand: true 64 | }, 65 | { 66 | cwd: '<%= dest %>/bin', 67 | src: ['*.dll'], 68 | dest: 'tmp/nuget/lib/net40', 69 | expand: true 70 | } 71 | ] 72 | }, 73 | umbraco: { 74 | cwd: '<%= dest %>', 75 | src: '**/*', 76 | dest: 'tmp/umbraco', 77 | expand: true 78 | } 79 | }, 80 | nugetpack: { 81 | dist: { 82 | src: 'tmp/nuget/package.nuspec', 83 | dest: 'pkg' 84 | } 85 | }, 86 | template: { 87 | 'nuspec': { 88 | 'options': { 89 | 'data': { 90 | name: '<%= pkgMeta.name %>', 91 | version: '<%= pkgMeta.version %>', 92 | url: '<%= pkgMeta.url %>', 93 | license: '<%= pkgMeta.license %>', 94 | licenseUrl: '<%= pkgMeta.licenseUrl %>', 95 | author: '<%= pkgMeta.author %>', 96 | authorUrl: '<%= pkgMeta.authorUrl %>', 97 | files: [{ path: 'tmp/nuget/content/App_Plugins', target: 'content/App_Plugins'}] 98 | } 99 | }, 100 | 'files': { 101 | 'tmp/nuget/package.nuspec': ['config/package.nuspec'] 102 | } 103 | } 104 | }, 105 | umbracoPackage: { 106 | options: { 107 | name: "<%= pkgMeta.name %>", 108 | version: '<%= pkgMeta.version %>', 109 | url: '<%= pkgMeta.url %>', 110 | license: '<%= pkgMeta.license %>', 111 | licenseUrl: '<%= pkgMeta.licenseUrl %>', 112 | author: '<%= pkgMeta.author %>', 113 | authorUrl: '<%= pkgMeta.authorUrl %>', 114 | manifest: 'config/package.xml', 115 | readme: 'config/readme.txt', 116 | sourceDir: 'tmp/umbraco', 117 | outputDir: 'pkg', 118 | } 119 | }, 120 | clean: { 121 | build: '<%= grunt.config("basePath").substring(0, 4) == "dist" ? "dist/**/*" : "null" %>', 122 | tmp: ['tmp'] 123 | }, 124 | assemblyinfo: { 125 | options: { 126 | files: ['src/G42.UmbracoGrease/G42.UmbracoGrease.csproj'], 127 | filename: 'AssemblyInfo.cs', 128 | info: { 129 | version: '<%= (pkgMeta.version.indexOf("-") ? pkgMeta.version.substring(0, pkgMeta.version.indexOf("-")) : pkgMeta.version) %>', 130 | fileVersion: '<%= pkgMeta.version %>' 131 | } 132 | } 133 | }, 134 | msbuild: { 135 | options: { 136 | stdout: true, 137 | verbosity: 'quiet', 138 | maxCpuCount: 4, 139 | version: 4.0, 140 | buildParameters: { 141 | WarningLevel: 2, 142 | NoWarn: 1607 143 | } 144 | }, 145 | dist: { 146 | src: ['src/G42.UmbracoGrease/G42.UmbracoGrease.csproj'], 147 | options: { 148 | projectConfiguration: 'Debug', 149 | targets: ['Clean', 'Rebuild'], 150 | } 151 | } 152 | } 153 | }); 154 | 155 | grunt.registerTask('default', ['clean', 'assemblyinfo', 'msbuild:dist', 'copy:dll', 'copy:actionsDll', 'copy:app_plugins', 'copy:views']); 156 | grunt.registerTask('nuget', ['clean:tmp', 'default', 'copy:nuget', 'template:nuspec', 'nugetpack']); 157 | grunt.registerTask('umbraco', ['clean:tmp', 'default', 'copy:umbraco', 'umbracoPackage']); 158 | grunt.registerTask('package', ['clean:tmp', 'default', 'copy:nuget', 'template:nuspec', 'nugetpack', 'copy:umbraco', 'umbracoPackage', 'clean:tmp']); 159 | }; -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42NodeHelper/NodeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using Umbraco.Web; 6 | using umbraco.cms.businesslogic.web; 7 | using Umbraco.Core.Logging; 8 | using Umbraco.Core; 9 | using G42.UmbracoGrease.G42NodeHelper.Models; 10 | 11 | namespace G42.UmbracoGrease.G42NodeHelper 12 | { 13 | /// 14 | /// Singleton class that represents a basic website structure and caches the result. 15 | /// 16 | public sealed class NodeHelper 17 | { 18 | private static volatile NodeHelper _instance; 19 | private static object _padLock = new Object(); 20 | private static Type _siteType; 21 | 22 | /// 23 | /// Prevents a default instance of the class from being created. 24 | /// 25 | /// You may only have one Site type. 26 | private NodeHelper() 27 | { 28 | var baseType = typeof (Site); 29 | 30 | var types = PluginManager.Current.ResolveTypes().Where(x => x.IsSubclassOf(baseType)); 31 | 32 | //if more than one, reject 33 | if (types.Count() > 1) 34 | { 35 | throw new Exception("You may only have one Site type."); 36 | } 37 | 38 | //find an implementation of Site 39 | _siteType = types.FirstOrDefault(x => x.IsSubclassOf(baseType)); 40 | 41 | if (_siteType == null) 42 | { 43 | LogHelper.Info("Using default Site Type."); 44 | _siteType = baseType; 45 | } 46 | else 47 | { 48 | LogHelper.Info("Using " + _siteType.ToString()); 49 | } 50 | } 51 | 52 | public List Sites = new List(); 53 | 54 | public DateTime CreatedOn { get; private set; } 55 | 56 | /// 57 | /// Gets the current site. 58 | /// 59 | /// 60 | /// The current site. 61 | /// 62 | public Site CurrentSite 63 | { 64 | get { return Sites.FirstOrDefault(x => x.Domain == HttpContext.Current.Request.Url.Host); } 65 | } 66 | 67 | /// 68 | /// Determines whether this instance is initialized. 69 | /// 70 | /// 71 | public static bool IsInitialized() 72 | { 73 | return _instance != null; 74 | } 75 | 76 | /// 77 | /// Gets the instance. 78 | /// 79 | /// 80 | /// The instance. 81 | /// 82 | public static NodeHelper Instance 83 | { 84 | get 85 | { 86 | if (_instance == null) 87 | { 88 | lock (_padLock) 89 | { 90 | if (_instance == null) 91 | { 92 | _instance = new NodeHelper(); 93 | 94 | _instance.CreatedOn = DateTime.UtcNow; 95 | 96 | var umbHelper = new UmbracoHelper(UmbracoContext.Current); 97 | 98 | foreach (var domain in Domain.GetDomains()) 99 | { 100 | var rootNode = umbHelper.TypedContent(domain.RootNodeId); 101 | 102 | if (rootNode != null) 103 | { 104 | LogHelper.Info("Caching: " + domain.Name); 105 | var siteRootNode = umbHelper.TypedContent(domain.RootNodeId); 106 | 107 | if (siteRootNode != null) 108 | { 109 | var siteSettings = siteRootNode.Siblings().FirstOrDefault(x => x.DocumentTypeAlias.EndsWith("SiteSettings")); 110 | 111 | var site = ((Site)Activator.CreateInstance(_siteType)); 112 | 113 | site.Domain = domain.Name; 114 | site.RootId = rootNode.Id; 115 | site.HomeId = siteRootNode.Id; 116 | site.SiteSettingsId = (siteSettings != null) ? siteSettings.Id : siteRootNode.Id; 117 | 118 | site.MapSite(site, domain, rootNode, siteRootNode, siteSettings); 119 | 120 | _instance.Sites.Add(site); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | return _instance; 128 | } 129 | private set 130 | { 131 | _instance = value; 132 | } 133 | } 134 | /// 135 | /// Clears this instance. 136 | /// 137 | public static void Clear() 138 | { 139 | LogHelper.Info("Clearing..."); 140 | _instance = null; 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42ErrorReporting/DefaultErrorHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web; 4 | using G42.UmbracoGrease.Core; 5 | using G42.UmbracoGrease.G42Slack.Helpers; 6 | using G42.UmbracoGrease.G42Slack.Models; 7 | using G42.UmbracoGrease.Helpers; 8 | using G42.UmbracoGrease.Interfaces; 9 | using Umbraco.Core.Logging; 10 | 11 | namespace G42.UmbracoGrease.G42ErrorReporting 12 | { 13 | /// 14 | /// The default implementation of the Grease error handler. 15 | /// 16 | public class DefaultErrorHandler : IG42ErrorHandler 17 | { 18 | private static Dictionary _errorDictionary = new Dictionary(); 19 | 20 | /// 21 | /// Handles the specific error handling logic. 22 | /// 23 | /// The sender. 24 | /// The instance containing the event data. 25 | /// The ex. 26 | public void Execute(object sender, EventArgs e, Exception ex) 27 | { 28 | var context = HttpContext.Current; 29 | 30 | //computer hash of the url and error message and send email 31 | var hash = SecurityHelper.CalculateMd5Hash(string.Format("{0}{1}", context.Request.Url.AbsoluteUri, ex.Message)); 32 | 33 | var interval = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_REPORTING_INTERVAL_KEY); 34 | 35 | if (_errorDictionary.ContainsKey(hash)) 36 | { 37 | if (DateTime.UtcNow.AddMinutes(interval * -1) > _errorDictionary[hash]) 38 | { 39 | _errorDictionary[hash] = DateTime.UtcNow; 40 | 41 | _saySomethingInSlack(context, ex, hash, interval); 42 | 43 | _sendErrorToLog(context, ex, hash, interval); 44 | } 45 | } 46 | else 47 | { 48 | _errorDictionary.Add(hash, DateTime.UtcNow); 49 | 50 | _saySomethingInSlack(context, ex, hash, interval); 51 | 52 | _sendErrorToLog(context, ex, hash, interval); 53 | } 54 | } 55 | 56 | /// 57 | /// _sends the error to log. 58 | /// 59 | /// The context. 60 | /// The ex. 61 | /// The hash. 62 | /// The interval. 63 | private void _sendErrorToLog(HttpContext context, Exception ex, string hash, int interval) 64 | { 65 | LogHelper.Info(string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n", context.Request.Url.AbsoluteUri, ex.Message, WebHelper.GetHeaders(false), IpHelper.GetIpAddress(), hash, interval)); 66 | } 67 | 68 | /// 69 | /// Say something in Slack based on configured settings. 70 | /// 71 | /// The context. 72 | /// The ex. 73 | /// The hash. 74 | /// The interval. 75 | /// 76 | private string _saySomethingInSlack(HttpContext context, Exception ex, string hash, int interval) 77 | { 78 | var enabled = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_SLACK_ENABLE_KEY); 79 | var hookUrl = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_SLACK_WEBHOOKURL_KEY); 80 | var channel = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_SLACK_CHANNEL_KEY); 81 | var botName = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_SLACK_BOTNAME_KEY); 82 | var emoji = Grease.Services.G42AppSettingsService.GetValue(Constants.ERROR_REPORTING_SLACK_EMOJI_KEY); 83 | 84 | if (enabled && !string.IsNullOrEmpty(hookUrl) && !string.IsNullOrEmpty(channel)) 85 | { 86 | return SlackHelper.SaySomething(ex.Message, botName, hookUrl, channel, emoji, new List 87 | { 88 | new SlackAttachmentModel 89 | { 90 | Title = "Path", 91 | Text = context.Request.Url.AbsoluteUri 92 | }, 93 | 94 | new SlackAttachmentModel 95 | { 96 | Title = "Headers", 97 | Text = WebHelper.GetHeaders(false) 98 | }, 99 | 100 | new SlackAttachmentModel 101 | { 102 | Title = "IP", 103 | Text = IpHelper.GetIpAddress() 104 | }, 105 | 106 | new SlackAttachmentModel 107 | { 108 | Title = "Hash", 109 | Text = hash 110 | }, 111 | 112 | new SlackAttachmentModel 113 | { 114 | Title = "Interval Period in Minutes", 115 | Text = interval.ToString() 116 | } 117 | }); 118 | } 119 | 120 | return "Slack not configured."; 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Web.config: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 |
9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42SearchHelper/Repositories/G42SearchRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web; 4 | using G42.UmbracoGrease.Core; 5 | using G42.UmbracoGrease.G42RedirectHelper; 6 | using G42.UmbracoGrease.G42SearchHelper.Models; 7 | using Umbraco.Core.Logging; 8 | using Umbraco.Core.Persistence; 9 | 10 | namespace G42.UmbracoGrease.G42SearchHelper.Repositories 11 | { 12 | internal class G42SearchRepository 13 | { 14 | /// 15 | /// Adds they keyword to the DB for the given domain. 16 | /// 17 | /// The unit of work. 18 | /// The domain. 19 | /// The keyword. 20 | internal static void AddKeywords(PetaPocoUnitOfWork unitOfWork, string domain, string keyword) 21 | { 22 | var currentKeyword = 23 | unitOfWork 24 | .Database 25 | .SingleOrDefault(@" 26 | SELECT * 27 | FROM G42GreaseSearchTrackerKeywords 28 | WHERE domain = @0 AND keyword = @1", 29 | domain, 30 | keyword); 31 | 32 | if (currentKeyword == null) 33 | { 34 | InsertKeywordsAndDomain(unitOfWork, domain, keyword); 35 | } 36 | else 37 | { 38 | currentKeyword.Count++; 39 | currentKeyword.LastUsedOn = DateTime.UtcNow; 40 | unitOfWork.Database.Save(currentKeyword); 41 | } 42 | } 43 | 44 | /// 45 | /// Gets keywords that have the minimum count specified. 46 | /// 47 | /// The unit of work. 48 | /// The count filter. 49 | /// 50 | internal static IEnumerable Get(PetaPocoUnitOfWork unitOfWork, int countFilter = 1) 51 | { 52 | return 53 | unitOfWork 54 | .Database 55 | .Fetch(@" 56 | SELECT * 57 | FROM G42GreaseSearchTrackerKeywords 58 | WHERE count >= @0", 59 | countFilter); 60 | } 61 | 62 | /// 63 | /// Inserts the specified keyword and domain. 64 | /// 65 | /// The unit of work. 66 | /// The domain. 67 | /// The keyword. 68 | internal static void InsertKeywordsAndDomain(PetaPocoUnitOfWork unitOfWork, string domain, string keyword) 69 | { 70 | unitOfWork 71 | .Database 72 | .Save(new G42GreaseSearchTrackerKeyword 73 | { 74 | Domain = domain, 75 | Keyword = keyword, 76 | Count = 1, 77 | LastUsedOn = DateTime.UtcNow 78 | }); 79 | } 80 | 81 | /// 82 | /// Adds the specified keywords to the DB. 83 | /// 84 | /// The unit of work. 85 | /// The keywords. 86 | internal static void AddSearch(PetaPocoUnitOfWork unitOfWork, string keywords) 87 | { 88 | var context = HttpContext.Current; 89 | 90 | var domain = context.Request.Url.Host; 91 | 92 | foreach (var keyword in keywords.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)) 93 | { 94 | AddKeywords(unitOfWork, domain, keyword); 95 | } 96 | 97 | unitOfWork.Database.Save(new G42GreaseSearchTrackerSearch() 98 | { 99 | Domain = domain, 100 | Path = RedirectHelper.GetCurrentPath(), 101 | Keywords = keywords, 102 | SearchedOn = DateTime.UtcNow 103 | }); 104 | } 105 | 106 | /// 107 | /// Creates the table in the DB. 108 | /// 109 | internal static void CreateSearchTrackerKeywordsTable(PetaPocoUnitOfWork unitOfWork) 110 | { 111 | if (!unitOfWork.Database.TableExist("G42GreaseSearchTrackerKeywords")) 112 | { 113 | LogHelper.Info("Creating table."); 114 | 115 | unitOfWork.Database.Execute(@" 116 | CREATE TABLE [dbo].[G42GreaseSearchTrackerKeywords]( 117 | [id] [int] IDENTITY(1,1) NOT NULL, 118 | [domain] [nvarchar](50) NOT NULL, 119 | [keyword] [nvarchar](50) NOT NULL, 120 | [count] [int] NOT NULL, 121 | [lastUsedOn] [datetime] NOT NULL, 122 | CONSTRAINT [PK_G42GreaseSearchTrackerKeywords] PRIMARY KEY CLUSTERED 123 | ( 124 | [id] ASC 125 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 126 | ) 127 | "); 128 | } 129 | else 130 | { 131 | LogHelper.Info("Table exists."); 132 | } 133 | } 134 | 135 | /// 136 | /// Creates the table in the DB. 137 | /// 138 | internal static void CreateSearchTrackerSearchesTable(PetaPocoUnitOfWork unitOfWork) 139 | { 140 | if (!unitOfWork.Database.TableExist("G42GreaseSearchTrackerSearches")) 141 | { 142 | LogHelper.Info("Creating table."); 143 | 144 | unitOfWork.Database.Execute(@" 145 | CREATE TABLE [dbo].[G42GreaseSearchTrackerSearches]( 146 | [id] [int] IDENTITY(1,1) NOT NULL, 147 | [domain] [nvarchar](50) NOT NULL, 148 | [path] [nvarchar](255) NOT NULL, 149 | [keywords] [nvarchar](255) NOT NULL, 150 | [searchedOn] [datetime] NOT NULL, 151 | CONSTRAINT [PK_G42GreaseSearchTrackerSearches] PRIMARY KEY CLUSTERED 152 | ( 153 | [id] ASC 154 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 155 | ) 156 | "); 157 | } 158 | else 159 | { 160 | LogHelper.Info("Table exists."); 161 | } 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/G42404Helper/Repositories/G42404Repository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Web; 4 | using G42.UmbracoGrease.Core; 5 | using G42.UmbracoGrease.Extensions; 6 | using G42.UmbracoGrease.G42404Helper.Models; 7 | using G42.UmbracoGrease.G42RedirectHelper; 8 | using G42.UmbracoGrease.Helpers; 9 | using Umbraco.Core.Logging; 10 | 11 | namespace G42.UmbracoGrease.G42404Helper.Repositories 12 | { 13 | internal class G42404Repository 14 | { 15 | internal static DateTime LastPurged; 16 | 17 | /// 18 | /// Gets the 404s that have the minimum count specified. 19 | /// 20 | /// The unit of work. 21 | /// The count filter. 22 | /// 23 | internal static IEnumerable GetResults(PetaPocoUnitOfWork unitOfWork, int countFilter = 1) 24 | { 25 | return unitOfWork.Database.Fetch(@" 26 | SELECT count, domain, path, lastVisited 27 | FROM ( 28 | SELECT COUNT(id) AS count, domainPathId 29 | FROM G42Grease404Tracker 30 | GROUP BY domainPathId 31 | HAVING COUNT(id) >= @0 32 | ) AS t1 33 | INNER JOIN G42Grease404TrackerDomainPaths dp on dp.id = t1.domainPathId 34 | ", countFilter); 35 | } 36 | 37 | internal static G42Grease404DomainPath GetDomainPath(PetaPocoUnitOfWork unitOfWork, string domain, string path) 38 | { 39 | return unitOfWork.Database.SingleOrDefault(@" 40 | WHERE domain = @0 AND path = @1 41 | ", domain, path); 42 | } 43 | 44 | internal static void TouchDomainPath(PetaPocoUnitOfWork unitOfWork, G42Grease404DomainPath domainPath) 45 | { 46 | domainPath.LastVisited = DateTime.UtcNow; 47 | 48 | unitOfWork.Database.Save(domainPath); 49 | } 50 | 51 | internal static G42Grease404DomainPath AddDomainPath(PetaPocoUnitOfWork unitOfWork, string domain, string path) 52 | { 53 | var domainPath = new G42Grease404DomainPath 54 | { 55 | Domain = domain, 56 | Path = path, 57 | AddedOn = DateTime.UtcNow, 58 | LastVisited = DateTime.UtcNow 59 | }; 60 | 61 | unitOfWork.Database.Insert(domainPath); 62 | 63 | return domainPath; 64 | } 65 | 66 | /// 67 | /// Adds a 404 to the DB. 68 | /// 69 | internal static void AddTracker(PetaPocoUnitOfWork unitOfWork, string referrer, string userAgent, G42Grease404DomainPath domainPath) 70 | { 71 | try 72 | { 73 | unitOfWork.Database.Save(new G42Grease404Tracker() 74 | { 75 | DomainPathId = domainPath.Id, 76 | Referrer = referrer, 77 | UserAgent = userAgent, 78 | IpAddress = IpHelper.GetIpAddress(), 79 | AddedOn = DateTime.UtcNow 80 | }); 81 | } 82 | catch (Exception ex) 83 | { 84 | LogHelper.Error(ex.Message, ex); 85 | } 86 | 87 | //would like this to not run for every 404, but for now it'll do 88 | PurgeTable(unitOfWork); 89 | } 90 | 91 | /// 92 | /// Purges the table of old items. 93 | /// 94 | internal static void PurgeTable(PetaPocoUnitOfWork unitOfWork) 95 | { 96 | if (LastPurged == DateTime.MinValue) 97 | { 98 | LastPurged = DateTime.UtcNow; 99 | } 100 | 101 | if (LastPurged > DateTime.UtcNow.AddDays(-1)) 102 | { 103 | return; 104 | } 105 | 106 | var customDays = Grease.Services.G42AppSettingsService.GetValue(Constants._404_TRACKER_DEFAULT_DAYS_TO_RETAIN_KEY); 107 | 108 | var date = DateTime.UtcNow.AddDays(customDays * -1); 109 | 110 | LogHelper.Info("Purging 404's " + customDays + " days prior beginning =>" + date.ToString("R")); 111 | 112 | unitOfWork.Database.Execute(@" 113 | DELETE 114 | FROM G42Grease404Tracker 115 | WHERE addedOn < @0 116 | ", date); 117 | 118 | LastPurged = DateTime.UtcNow; 119 | } 120 | 121 | /// 122 | /// Creates the 404 table. 123 | /// 124 | internal static void Create404TrackerTable(PetaPocoUnitOfWork unitOfWork) 125 | { 126 | if (!unitOfWork.Database.DoesTableExist("G42Grease404Tracker")) 127 | { 128 | LogHelper.Info("Creating table..."); 129 | 130 | unitOfWork.Database.Execute(@" 131 | CREATE TABLE [dbo].[G42Grease404Tracker]( 132 | [id] [bigint] IDENTITY(1,1) NOT NULL, 133 | [domainPathId] [bigint] NOT NULL, 134 | [referrer] [nvarchar](255) NOT NULL, 135 | [userAgent] [nvarchar](max) NULL, 136 | [addedOn] [datetime] NOT NULL, 137 | [ipAddress] [nvarchar](50) NULL, 138 | CONSTRAINT [PK_G42Grease404Tracker] PRIMARY KEY CLUSTERED 139 | ( 140 | [id] ASC 141 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 142 | ) 143 | "); 144 | } 145 | else 146 | { 147 | LogHelper.Info("Table exists."); 148 | } 149 | } 150 | 151 | /// 152 | /// Creates the 404 table. 153 | /// 154 | internal static void Create404DomainPathsTable(PetaPocoUnitOfWork unitOfWork) 155 | { 156 | if (!unitOfWork.Database.DoesTableExist("G42Grease404TrackerDomainPaths")) 157 | { 158 | LogHelper.Info("Creating table..."); 159 | 160 | unitOfWork.Database.Execute(@" 161 | CREATE TABLE [dbo].[G42Grease404TrackerDomainPaths]( 162 | [id] [bigint] IDENTITY(1,1) NOT NULL, 163 | [domain] [nvarchar](75) NOT NULL, 164 | [path] [nvarchar](255) NOT NULL, 165 | [addedOn] [datetime] NOT NULL, 166 | [lastVisited] [datetime] NULL, 167 | CONSTRAINT [PK_G42Grease404TrackerDomainPaths] PRIMARY KEY CLUSTERED 168 | ( 169 | [id] ASC 170 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON), 171 | CONSTRAINT [IX_G42Grease404TrackerDomainPaths] UNIQUE NONCLUSTERED 172 | ( 173 | [domain] ASC, 174 | [path] ASC 175 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 176 | ) 177 | "); 178 | } 179 | else 180 | { 181 | LogHelper.Info("Table exists."); 182 | } 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /src/G42.UmbracoGrease/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using System.Security; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using G42.UmbracoGrease.G42TransformationHelper; 9 | using G42.UmbracoGrease.G42TransformationHelper.Models; 10 | using HtmlAgilityPack; 11 | using Umbraco.Core; 12 | using Umbraco.Core.Logging; 13 | 14 | namespace G42.UmbracoGrease.Extensions 15 | { 16 | /// 17 | /// String Extensions. 18 | /// 19 | public static class StringExtensions 20 | { 21 | /// 22 | /// Truncates a string after the given character count without breaking in between words. Optionally change the ellipses character. 23 | /// 24 | /// The text. 25 | /// The maximum characters. 26 | /// The trailing string if text cut. 27 | /// 28 | public static string TruncateAtWord(this string text, int maxCharacters, string trailingStringIfTextCut = "…") 29 | { 30 | if (text == null || (text = text.Trim()).Length <= maxCharacters) 31 | return text; 32 | 33 | var trailLength = trailingStringIfTextCut.StartsWith("&") ? 1 : trailingStringIfTextCut.Length; 34 | maxCharacters = maxCharacters - trailLength >= 0 ? maxCharacters - trailLength : 0; 35 | var pos = text.LastIndexOf(" ", maxCharacters); 36 | if (pos >= 0) 37 | return text.Substring(0, pos) + trailingStringIfTextCut; 38 | 39 | return string.Empty; 40 | } 41 | 42 | /// 43 | /// Highlights the given keywords as <strong> tags. 44 | /// 45 | /// The text. 46 | /// The query. 47 | /// 48 | public static string HighlightKeywords(this string text, string query) 49 | { 50 | var keywords = HttpUtility.UrlDecode(query).Split(new []{' '}, StringSplitOptions.RemoveEmptyEntries); 51 | 52 | text = text.StripHtml(); 53 | 54 | foreach (var keyword in keywords) 55 | { 56 | LogHelper.Info("Highlighting=>" + keyword); 57 | 58 | var pattern = string.Format(@"(\b{0}\b)(?![^<]*>|[^<>]*<\/)", Regex.Escape(keyword)); 59 | 60 | text = Regex.Replace(text, pattern, m => string.Format("{0}", m.Value), RegexOptions.IgnoreCase); 61 | } 62 | 63 | return text; 64 | } 65 | 66 | /// 67 | /// Wraps unsafe text into a CDATA string. 68 | /// 69 | /// The input. 70 | /// 71 | public static string ToXmlSafeString(this string input) 72 | { 73 | var escapedString = SecurityElement.Escape(input); 74 | 75 | return input == escapedString ? input : ""; 76 | } 77 | 78 | /// 79 | /// Converts a string with http to https 80 | /// 81 | /// The input. 82 | /// 83 | public static string ToHttpsUrl(this string input) 84 | { 85 | return input.Replace("http:", "https:"); 86 | } 87 | 88 | /// 89 | /// Converts a string to a proper Azure blob URL if using Dirk Seefeld's project: https://our.umbraco.org/projects/backoffice-extensions/azure-blob-storage-provider/ 90 | /// 91 | /// The input. 92 | /// if set to true [use same protocol as request]. 93 | /// 94 | public static string ToAzureBlobUrl(this string input, bool useSameProtocolAsRequest = true, HttpContext context = null) 95 | { 96 | HttpRequest request; 97 | 98 | if (context != null) 99 | { 100 | request = context.Request; 101 | } 102 | else 103 | { 104 | request = HttpContext.Current.Request; 105 | } 106 | 107 | var requestProtocol = (request.Url.AbsoluteUri.StartsWith("http://")) ? "http" : "https"; 108 | 109 | var domain = request.Url.Host; 110 | 111 | if (domain.EndsWith(".local")) 112 | { 113 | return input.ToDesiredProtocol(requestProtocol, useSameProtocolAsRequest); 114 | } 115 | 116 | return input.Replace("://", string.Format("://{0}/remote.axd/", domain)).ToDesiredProtocol(requestProtocol, useSameProtocolAsRequest); 117 | } 118 | 119 | /// 120 | /// Converts a URL as a string to the desired protocol. 121 | /// 122 | /// The input. 123 | /// The desired protocol. 124 | /// if set to true [perform replacement]. 125 | /// 126 | public static string ToDesiredProtocol(this string input, string desiredProtocol = "http", bool performReplacement = true) 127 | { 128 | if (!performReplacement) 129 | { 130 | return input; 131 | } 132 | 133 | var formattedProtocol = string.Format("{0}://", desiredProtocol); 134 | 135 | if (input.StartsWith("http://")) 136 | { 137 | return input.Replace("http://", formattedProtocol); 138 | } 139 | 140 | return input.Replace("https://", formattedProtocol); 141 | } 142 | 143 | /// 144 | /// Converts a double represented as a string to an int. 145 | /// 146 | /// The input. 147 | /// 148 | public static int ToIntFromDoubleString(this string input) 149 | { 150 | return Convert.ToInt32(Math.Truncate(Convert.ToDouble(input))); 151 | } 152 | 153 | /// 154 | /// Transforms image tags embedded by the Umbraco RTE to use different HTML as defined in a partial. 155 | /// 156 | /// The input. 157 | /// The context. 158 | /// The classes to transform. 159 | /// The path to partial. 160 | /// 161 | public static IHtmlString TransformImages(this string input, ControllerContext context, string[] classesToTransform, string pathToPartial = "~/Views/Partials/ImageTransformations.cshtml") 162 | { 163 | if (String.IsNullOrWhiteSpace(input)) 164 | { 165 | return new HtmlString(input); 166 | } 167 | 168 | var document = new HtmlDocument(); 169 | document.LoadHtml(input); 170 | 171 | var spans = document.DocumentNode.SelectNodes("//span"); 172 | if (spans == null) 173 | return new HtmlString(input); 174 | 175 | foreach (var span in spans) 176 | { 177 | var classAttribute = span.Attributes["class"]; 178 | 179 | if (classAttribute != null && !String.IsNullOrWhiteSpace(classAttribute.Value)) 180 | { 181 | var spanClasses = classAttribute.Value.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries); 182 | 183 | var classValue = classAttribute.Value.Trim(); 184 | 185 | if (classesToTransform.Intersect(spanClasses).Any()) 186 | { 187 | var img = span.Descendants().FirstOrDefault(x => x.Name == "img"); 188 | 189 | if (img != null) 190 | { 191 | var renderedTemplate = ""; 192 | 193 | //remove style attr 194 | var styleAttr = img.Attributes["style"]; 195 | if (styleAttr != null) 196 | { 197 | styleAttr.Value = ""; 198 | } 199 | 200 | var relAttr = img.Attributes["rel"]; 201 | 202 | var classAttr = img.Attributes["class"]; 203 | 204 | var cropUrl = img.Attributes["src"].Value; 205 | 206 | //for some reason the httputility decoders won't do this 207 | cropUrl = cropUrl.Replace("&", "&"); 208 | 209 | var queryString = cropUrl.Substring(cropUrl.IndexOf('?') + 1); 210 | 211 | var parameterDictionary = new Dictionary(); 212 | 213 | var pairs = queryString.Split(new[] { "&" }, StringSplitOptions.RemoveEmptyEntries); 214 | 215 | foreach (var pair in pairs) 216 | { 217 | var kvp = pair.Split(new[] { "=" }, StringSplitOptions.RemoveEmptyEntries); 218 | 219 | parameterDictionary.Add(kvp[0], kvp[1].Replace("px", "")); 220 | } 221 | 222 | cropUrl = string.Format("{0}?width={1}&height={2}", cropUrl.Substring(0, cropUrl.IndexOf('?')) .ToAzureBlobUrl(), parameterDictionary["width"].ToIntFromDoubleString(), parameterDictionary["height"].ToIntFromDoubleString()); 223 | 224 | var inlineStyle = new ImageTransformation() 225 | { 226 | Type = classValue, 227 | Text = span.InnerText, 228 | Html = span.InnerHtml, 229 | Meta = new ImageTag() 230 | { 231 | Src = cropUrl, 232 | Title = img.Attributes["alt"].Value, 233 | Alt = img.Attributes["alt"].Value, 234 | Classes = (classAttr != null) ? classAttr.Value : "", 235 | Rel = (relAttr != null) ? relAttr.Value : "" 236 | } 237 | }; 238 | 239 | renderedTemplate = TransformationHelper.RenderRazorViewToString(context, pathToPartial, inlineStyle).Trim(); 240 | 241 | var newNode = HtmlNode.CreateNode(renderedTemplate); 242 | span.ParentNode.ReplaceChild(newNode, span); 243 | } 244 | } 245 | } 246 | } 247 | 248 | return new HtmlString(document.DocumentNode.OuterHtml); 249 | } 250 | 251 | /// 252 | /// Cleans a string to be only alphanumeric, space, dash and underscore. 253 | /// 254 | /// The input. 255 | /// if set to true [allow spaces]. 256 | /// if set to true [allow dashes]. 257 | /// if set to true [allow underscore]. 258 | /// The replacement string. 259 | /// 260 | public static string ToOnlyAlphanumeric(this string input, bool allowSpaces = true, bool allowDashes = true, bool allowUnderscore = true, string replacementString = "") 261 | { 262 | var rgx = new Regex("[^a-zA-Z0-9 _-]"); 263 | var output = rgx.Replace(Regex.Escape(input), replacementString); 264 | 265 | if (!allowSpaces) 266 | { 267 | output = output.Replace(" ", replacementString); 268 | } 269 | 270 | if (!allowDashes) 271 | { 272 | output = output.Replace("-", replacementString); 273 | } 274 | 275 | if (!allowUnderscore) 276 | { 277 | output = output.Replace("_", replacementString); 278 | } 279 | 280 | return output; 281 | } 282 | 283 | /// 284 | /// Converts RTE text URL's from http to https. 285 | /// 286 | /// The input. 287 | /// The xpath. 288 | /// Name of the attribute. 289 | /// 290 | public static IHtmlString ToAbsoluteUrls(this string input, string xpath = "//a", string attributeName = "href") 291 | { 292 | if (String.IsNullOrWhiteSpace(input)) 293 | { 294 | return new HtmlString(input); 295 | } 296 | 297 | var document = new HtmlDocument(); 298 | document.LoadHtml(input); 299 | 300 | var elements = document.DocumentNode.SelectNodes(xpath); 301 | 302 | if (elements == null) 303 | { 304 | return new HtmlString(input); 305 | } 306 | 307 | var hostname = HttpContext.Current.Request.Url.Host; 308 | 309 | var requestProtocol = (HttpContext.Current.Request.Url.AbsoluteUri.StartsWith("http://")) ? "http" : "https"; 310 | 311 | foreach (var element in elements) 312 | { 313 | var attribute = element.Attributes[attributeName]; 314 | 315 | if (attribute != null) 316 | { 317 | if (!attribute.Value.StartsWith("http")) 318 | { 319 | attribute.Value = string.Format("{0}://{1}{2}", requestProtocol, hostname, attribute.Value); 320 | } 321 | } 322 | } 323 | 324 | return new HtmlString(document.DocumentNode.OuterHtml); 325 | } 326 | 327 | /// 328 | /// Determines whether an email is valid based on regex. 329 | /// 330 | /// The string. 331 | /// 332 | public static bool IsValidEmail(this string str) 333 | { 334 | if (str == null) 335 | return false; 336 | 337 | var regex = new Regex(@"^([\w\.\-\+]+)@([\w\-]+)((\.(\w){2,3})+)$"); 338 | var match = regex.Match(str); 339 | return match.Success; 340 | } 341 | } 342 | } --------------------------------------------------------------------------------