Skip to content

Commit

Permalink
URL Rewriting Module (#16687)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Arkadiusz Wójcik <[email protected]>
Co-authored-by: Mike Alhayek <[email protected]>
  • Loading branch information
3 people authored Oct 22, 2024
1 parent 9252778 commit 1fb3287
Show file tree
Hide file tree
Showing 91 changed files with 3,394 additions and 0 deletions.
21 changes: 21 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Queries.Core",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Sms.Azure", "src\OrchardCore.Modules\OrchardCore.Sms.Azure\OrchardCore.Sms.Azure.csproj", "{013C8BBF-6879-4B47-80C9-A466923E45E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.UrlRewriting", "src\OrchardCore.Modules\OrchardCore.UrlRewriting\OrchardCore.UrlRewriting.csproj", "{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.UrlRewriting.Abstractions", "src\OrchardCore\OrchardCore.UrlRewriting.Abstractions\OrchardCore.UrlRewriting.Abstractions.csproj", "{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.UrlRewriting.Core", "src\OrchardCore\OrchardCore.UrlRewriting.Core\OrchardCore.UrlRewriting.Core.csproj", "{7B18DD99-A7BB-4297-8679-D87289758756}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1395,6 +1401,18 @@ Global
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{013C8BBF-6879-4B47-80C9-A466923E45E5}.Release|Any CPU.Build.0 = Release|Any CPU
{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0F8B342-BDA8-44CB-AA43-7A65C79636A2}.Release|Any CPU.Build.0 = Release|Any CPU
{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{675C8A76-C64F-47EC-B4F5-06D4F2D9662A}.Release|Any CPU.Build.0 = Release|Any CPU
{7B18DD99-A7BB-4297-8679-D87289758756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B18DD99-A7BB-4297-8679-D87289758756}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B18DD99-A7BB-4297-8679-D87289758756}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B18DD99-A7BB-4297-8679-D87289758756}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1636,6 +1654,9 @@ Global
{4BAA08A2-878C-4B96-86BF-5B3DB2B6C2C7} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{61B358F2-702C-40AA-9DF7-7121248FE6DE} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{013C8BBF-6879-4B47-80C9-A466923E45E5} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{D0F8B342-BDA8-44CB-AA43-7A65C79636A2} = {A066395F-6F73-45DC-B5A6-B4E306110DCE}
{675C8A76-C64F-47EC-B4F5-06D4F2D9662A} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
{7B18DD99-A7BB-4297-8679-D87289758756} = {F23AC6C2-DE44-4699-999D-3C478EF3D691}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46A1D25A-78D1-4476-9CBF-25B75E296341}
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ nav:
- Workflows: reference/modules/Workflows/README.md
- DataProtection (Azure Storage): reference/modules/DataProtection.Azure/README.md
- Background Tasks: reference/modules/BackgroundTasks/README.md
- URL Rewriting: reference/modules/UrlRewriting/README.md
- Reverse Proxy: reference/modules/ReverseProxy/README.md
- Resources:
- Learning: resources/README.md
Expand Down
30 changes: 30 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.UrlRewriting/AdminMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.Extensions.Localization;
using OrchardCore.Navigation;

namespace OrchardCore.UrlRewriting;

public sealed class AdminMenu : AdminNavigationProvider
{
internal readonly IStringLocalizer S;

public AdminMenu(IStringLocalizer<AdminMenu> stringLocalizer)
{
S = stringLocalizer;
}

protected override ValueTask BuildAsync(NavigationBuilder builder)
{
builder
.Add(S["Configuration"], configuration => configuration
.Add(S["URL Rewriting"], S["URL Rewriting"].PrefixPosition(), rewriting => rewriting
.AddClass("url-rewriting")
.Id("urlRewriting")
.Permission(UrlRewritingPermissions.ManageUrlRewritingRules)
.Action("Index", "Admin", "OrchardCore.UrlRewriting")
.LocalNav()
)
);

return ValueTask.CompletedTask;
}
}
14 changes: 14 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.UrlRewriting/Assets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"inputs": [
"Assets/js/sortable-rules.js"
],
"output": "wwwroot/Scripts/sortable-rules.js"
},
{
"inputs": [
"Assets/js/admin-ui.js"
],
"output": "wwwroot/Scripts/admin-ui.js"
}
]
169 changes: 169 additions & 0 deletions src/OrchardCore.Modules/OrchardCore.UrlRewriting/Assets/js/admin-ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
urlRewritingAdmin = function () {

const initialize = (url, errorLabel, selectedLabel) => {

// Create the sortable UI.
sortingListManager.create('#rewrite-rules-sortable-list', url, errorLabel);

let searchBox = document.getElementById('search-box');
let searchAlert = document.getElementById('list-alert');

const filterElements = document.querySelectorAll('[data-filter-value]');

// If the user press Enter, don't submit.
searchBox.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
}
});

searchBox.addEventListener('keyup', e => {

var search = e.target.value.toLowerCase();
// On ESC, clear the search box and display all rules.
if (e.key == 'Escape' || search == '') {
searchAlert.classList.add('d-none');
searchBox.value = '';
for (let i = 0; i < filterElements.length; i++) {
filterElements[i].classList.remove("d-none");
filterElements[i].classList.remove("first-child-visible");
filterElements[i].classList.remove("last-child-visible");
}

if (filterElements.length > 0) {
filterElements[0].classList.add('first-child-visible');
filterElements[filterElements.length - 1].classList.add('last-child-visible');
}
} else {
let visibleElements = [];
for (let i = 0; i < filterElements.length; i++) {
let filter = filterElements[i];

var text = filter.getAttribute('data-filter-value');

if (!text) {
filter.classList.add("d-none");
continue;
}

var found = text.indexOf(search) > -1;

if (found) {
filter.classList.remove("d-none");
filter.classList.remove("first-child-visible");
filter.classList.remove("last-child-visible");
visibleElements.push(filter);
} else {
filter.classList.add("d-none");
}
}

if (visibleElements.length > 0) {
visibleElements[0].classList.add('first-child-visible');
visibleElements[visibleElements.length - 1].classList.add('last-child-visible');
searchAlert.classList.add('d-none');
} else {
searchAlert.classList.remove('d-none');
}
}
});

var actions = document.getElementById('actions');
var items = document.getElementById('items');
var filters = document.querySelectorAll('.filter');
var selectAllCtrl = document.getElementById('select-all');
var selectedItems = document.getElementById('selected-items');
var itemsCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']");

function displayActionsOrFilters() {
// Select all checked checkboxes with name 'ruleIds'
var checkedCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked");

if (checkedCheckboxes.length > 1) {
actions.classList.remove('d-none');
for (let i = 0; i < filters.length; i++) {
filters[i].classList.add('d-none');
}
selectedItems.classList.remove('d-none');
items.classList.add('d-none');
} else {
actions.classList.add('d-none');

for (let i = 0; i < filters.length; i++) {
filters[i].classList.remove('d-none');
}
selectedItems.classList.add('d-none');
items.classList.remove('d-none');
}
}

var dropdownItems = document.querySelectorAll(".dropdown-menu .dropdown-item");

// Add click event listeners to each dropdown item
dropdownItems.forEach(function (item) {
// Check if the item has a data-action attribute
if (item.dataset.action) {
item.addEventListener("click", function () {
// Get all checked checkboxes
var checkedCheckboxes = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked");

// Check if more than one checkbox is checked
if (checkedCheckboxes.length > 1) {
// Get data attributes from the clicked item
var actionData = Object.assign({}, item.dataset);

confirmDialog({
...actionData,
callback: function (r) {
if (r) {
// Set the value of the BulkAction option
document.querySelector("[name='Options.BulkAction']").value = actionData.action;
// Trigger the submit action
document.querySelector("[name='submit.BulkAction']").click();
}
}
});
}
});
}
});

selectAllCtrl.addEventListener("click", function () {
itemsCheckboxes.forEach(function (checkbox) {
if (checkbox !== selectAllCtrl) {
checkbox.checked = selectAllCtrl.checked; // Set the checked state of all checkboxes
}
});

// Update the selected items text
updateSelectedItemsText();
displayActionsOrFilters();
});

// Event listener for individual checkboxes
itemsCheckboxes.forEach(function (checkbox) {
checkbox.addEventListener("click", function () {
var itemsCount = itemsCheckboxes.length;
var selectedItemsCount = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length;

// Update selectAllCtrl state
selectAllCtrl.checked = selectedItemsCount === itemsCount;
selectAllCtrl.indeterminate = selectedItemsCount > 0 && selectedItemsCount < itemsCount;

// Update the selected items text
updateSelectedItemsText();
displayActionsOrFilters();
});
});

// Function to update selected items text
function updateSelectedItemsText() {
var selectedCount = document.querySelectorAll("input[type='checkbox'][name='ruleIds']:checked").length;
selectedItems.textContent = selectedCount + ' ' + selectedLabel;
}
}

return {
initialize: initialize
}
}();
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
sortingListManager = function () {

const saveOrders = (evt, url, errorMessage) => {

var data = {
oldIndex: evt.oldIndex,
newIndex: evt.newIndex
};
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).catch(error => {
console.log(error);
alert(errorMessage || 'Unable to sort the list');
});
}

const create = (selector, sortUrl, errorMessage) => {

var sortable = document.querySelector(selector);

if (!sortable) {
console.log('Unable to find the sortable element. The given selector is: ' + selector);

return;
}

if (sortUrl) {
orderUrl = sortUrl;
} else {
orderUrl = sortable.getAttribute('data-sort-uri');
}

if (!orderUrl) {
console.log('Unable to determine the sort post URI. Either pass it to the create function or set it as data-sort-uri to the sorting element.');

return;
}

var sortable = Sortable.create(sortable, {
handle: ".ui-sortable-handle",
animation: 150,
filter: ".ignore-elements",
draggable: ".item",
onUpdate: function (evt) {
saveOrders(evt, orderUrl, errorMessage);
}
});
};

return {
create: create
}
}();
Loading

0 comments on commit 1fb3287

Please sign in to comment.