The URL returned a 404 (not found) form Api post in Plugin
Hello.
I'm developing a plugin to enable the Editors, that don't have access to the Settings section of the Backoffice. The purpose is to allow the Editor to Add or Remove an item from a DropDown List. I have the functions in a Service that perform the operation functional and tested them through a test surface controller.
I have built a Section that is displayed as needed:
Following are my Section, Component, Composer and Controller:
FiltersSection.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Models.Sections;
namespace ConsapCms.Filters
{
/// <summary>
/// Custom Filters Interface, see FiltersComposer for how
/// this is registered in umbraco, FiltersComponent ads the
/// section to the admin group so it appears by default.
/// </summary>
public class FiltersSection : ISection
{
/// <summary>
/// Alias for the section
/// </summary>
/// <remarks>
/// It makes sense to make this a constant (you don't have to)
/// because you end up refrencing it in things like tree's and
/// dashboards.
/// </remarks>
public const string SectionAlias = "Filtri";
#region ISection Interface
/// <summary>
/// Alias of section, used in urls, trees etc
/// </summary>
public string Alias => SectionAlias;
/// <summary>
/// name of the section - this isn't what the user sees
/// they see a value from the lang file
/// </summary>
public string Name => "Filtri";
#endregion
}
}
FiltersSectionComponent.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Services;
using ConsapCms.Services;
using System.Web.Mvc;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Models.ContentEditing;
using System.Web.Http;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json;
using System.Net.Http.Formatting;
namespace ConsapCms.Filters
{
/// <summary>
/// Umbraco Component (runs at startup)
/// </summary>
/// <remarks>This doesn't run unless it's registered in the Composer</remarks>
public class FiltersSectionComponent : IComponent
{
private readonly IUserService userService;
private readonly IKeyValueService keyValueService;
private readonly IProfilingLogger logger;
private const string setupKey = "Filters_installed";
public FiltersSectionComponent(
IUserService userService,
IKeyValueService keyValueService,
IProfilingLogger logger)
{
this.userService = userService;
this.keyValueService = keyValueService;
this.logger = logger;
}
public void Initialize()
{
GlobalConfiguration.Configuration.MapHttpAttributeRoutes();
var installed = keyValueService.GetValue(setupKey);
if (installed == null || installed != "installed")
{
AddSection("admin", FiltersSection.SectionAlias);
keyValueService.SetValue(setupKey, "installed");
}
}
/// <summary>
/// add the given section to the given user group
/// </summary>
/// <remarks>
/// if umbraco throws an exception here, we capture it
/// because if we don't umbraco won't startup and that
/// might be a bad thing :(
/// </remarks>
/// <param name="groupAlias"></param>
/// <param name="sectionAlias"></param>
private void AddSection(string groupAlias, string sectionAlias)
{
using (logger.DebugDuration<FiltersSectionComponent>($"Adding Section {sectionAlias} to {groupAlias}"))
{
var group = userService.GetUserGroupByAlias(groupAlias);
if (group != null)
{
if (!group.AllowedSections.Contains(sectionAlias))
{
group.AddAllowedSection(sectionAlias);
try
{
userService.Save(group);
logger.Info<FiltersSectionComponent>($"Section {sectionAlias} added to {groupAlias} group");
}
catch (Exception ex)
{
logger.Warn<FiltersSectionComponent>("Error adding section {0} to group {1} [{2}]",
sectionAlias, groupAlias, ex.Message);
}
}
}
}
}
public void Terminate()
{
// umbraco is shutting down -
}
}
}
FiltersSectinComposer:
using ConsapCms.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Web;
using Umbraco.Web.Sections;
namespace ConsapCms.Filters
{
public class FiltersSectionComposer : IUserComposer
{
public void Compose(Composition composition)
{
// Register the component that adds the section to the admin group
composition.Components().Insert<FiltersSectionComponent>();
// Register the section
composition.Sections().InsertBefore<SettingsSection, FiltersSection>();
//// Register the component that adds the section to the admin group
composition.Components().Append<FiltersSectionComponent>();
// Register the tree controller
//composition.Register<FiltersTreeController>();
//// remove section
//composition.Sections().Remove<FiltersSection>();
}
}
}
and FiltersTreeController.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Umbraco.Web.Trees;
using Umbraco.Web.Mvc;
using System.Net.Http.Formatting;
using System.Web.Http.ModelBinding;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Web.Actions;
using ConsapCms.Services;
using System.Web.Http.Results;
using ModelBinderAttribute = System.Web.Http.ModelBinding.ModelBinderAttribute;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using System.Web.Http;
using Umbraco.Web.WebApi;
using Umbraco.Web.Services;
using Umbraco.Core.Services.Implement;
namespace ConsapCms.Filters
{
/// <summary>
/// Tree Controller
/// </summary>
/// <remarks>
/// Unlike most things in v8 tree controllers don't need registering
/// in a composer/component - they use the attribute data to build
/// the tree and put it in the right section.
/// </remarks>
[Tree(FiltersSection.SectionAlias, "FiltersTree",
TreeGroup = "Filters",
TreeTitle = "Filtri Attività ",
SortOrder = 10)]
[PluginController("FiltersTree")]
public class FiltersTreeController : TreeController
{
private readonly IDataTypeValueService _dataTypeService;
public FiltersTreeController(IDataTypeValueService dataTypeService)
{
_dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService));
}
protected override MenuItemCollection GetMenuForNode(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormDataCollection queryStrings)
{
var menu = new MenuItemCollection();
switch (id)
{
case "-1": // root node
var addAction = new MenuItem("add", Services.TextService)
{
Icon = "add",
};
// Set the JavaScript function to execute on click
addAction.LaunchDialogView("/app_plugins/filters/backoffice/filterstree/add.html", "Aggiungi Filtro");
menu.Items.Add(addAction);
menu.Items.Add(new RefreshNode(Services.TextService, true));
break;
default: // add 'remove' to all non-root nodes
var removeAction = new MenuItem("remove", Services.TextService)
{
Icon = "trash",
};
// Set the JavaScript function to execute on click
removeAction.AdditionalData["jsAction"] = "filtersController.removeNode";
removeAction.AdditionalData["nodeId"] = id;
menu.Items.Add(removeAction);
break;
}
//// all nodes get a refresh (because we want to)
//menu.Items.Add(new RefreshNode(Services.TextService, true));
return menu;
}
/// <summary>
/// Returns the TreeNode Collection in "DDL_Attivita"
/// </summary>
/// <returns></returns>
protected override TreeNodeCollection GetTreeNodes(string id, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormDataCollection queryStrings)
{
// Retrieve real data from your DataTypeValueService
var tree = new TreeNodeCollection();
// Retrieve real data from your DataTypeValueService
var items = _dataTypeService.GetItemsFromValueListDataType("DDL_Attivita", null);
foreach (var item in items)
{
tree.Add(CreateTreeNode(item.Value, "-1", null, item.Text, "icon-filter"));
}
return tree;
}
}
[PluginController("Filters")]
//[RoutePrefix("api/filters")]
public class FiltersTreeApiController : UmbracoAuthorizedApiController //UmbracoApiController
{
private readonly FiltersTreeService _treeService;
public FiltersTreeApiController(FiltersTreeService treeService)
{
_treeService = treeService ?? throw new ArgumentNullException(nameof(treeService));
}
/// <summary>
/// Adds new value to list
/// </summary>
/// <param name="newValue"></param>
/// <returns></returns>
[HttpPost]
[Route("AddValueToValueListDataType")]
public IHttpActionResult AddValueToValueListDataType(string newValue)
{
try
{
// Add value to the list
bool bOk = _treeService.AddValueToValueListDataType(newValue);
if (bOk)
{
// update the FiltersTree
//var updatedTreeNodes = _treeService.BuildTreeNodes();
// Return the updated tree structure
//return Ok(new { success = true, message = "Valore aggiunto correttamente.", treeNodes = updatedTreeNodes });
return Ok(new { success = true, message = "Valore aggiunto correttamente." });
}
else
{
return Ok(new { success = false, message = "Valore NON aggiunto." });
}
}
catch (Exception ex)
{
// Log the exception
return InternalServerError(ex);
}
}
/// <summary>
/// Removes value from list
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
[HttpPost]
[Route("RemoveValueToValueListDataType")]
public IHttpActionResult RemoveValueToValueListDataType(string oldValue)
{
try
{
// Add value to the list
bool bOk = _treeService.RemoveValueToValueListDataType(oldValue);
if (bOk)
{
// Return the updated tree structure
return Ok(new { success = true, message = "Filtro eliminato correttamente." });
}
else
{
return Ok(new { success = false, message = "Filtro NON eliminato." });
}
}
catch (Exception ex)
{
// Log the exception
return InternalServerError(ex);
}
}
}
public class FiltersTreeService
{
private readonly IDataTypeValueService _dataTypeService;
public FiltersTreeService(IDataTypeValueService dataTypeService)
{
_dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService));
}
/// <summary>
/// Adds new value to DDL_Attivita
/// </summary>
/// <param name="newValue"></param>
/// <returns></returns>
public bool AddValueToValueListDataType(string newValue)
{
bool bOK = _dataTypeService.AddValueToValueListDataType("DDL_Attivita", newValue);
if (bOK) return true;
else return false;
}
/// <summary>
/// Removes value from DDL_Attivita
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
public bool RemoveValueToValueListDataType(string oldValue)
{
bool bOK = _dataTypeService.RemoveValueToValueListDataType("DDL_Attivita", oldValue);
if (bOK) return true;
else return false;
}
}
}
(function () {
'use strict';
// Define the AngularJS module
var app = angular.module('umbraco');
// Define the service within the same module
app.service('filtersService', ['$http', function ($http) {
this.addValue = function (data) {
if (!data) {
return Promise.reject(new Error('Data is undefined or null'));
}
return $http.post('/umbraco/backoffice/Filters/FiltersTreeApiController/AddValueToValueListDataType/', data);
};
this.removeNode = function (data) {
if (!data) {
return Promise.reject(new Error('Data is undefined or null'));
}
return $http.post('/umbraco/backoffice/Filters/FiltersTreeApiController/RemoveValueToValueListDataType/', data);
};
}]);
})();
filtersController.js:
(function () {
'use strict';
app.controller('filtersTreeController', ['$scope', '$routeParams', 'notificationsService', 'filtersService', function ($scope, $routeParams, notificationsService, filtersService) {
var vm = this;
vm.loading = false; // Initialize to false
vm.buttonState = 'init';
vm.page = {
title: 'Aggiungi Filtro',
description: 'Inserisci un nuovo filtro'
};
// Data model
vm.newFilterValue = '';
vm.oldFilterValue = '';
// Functions
vm.addFilter = addFilter;
vm.removeNode = delFilter;
// Initialization
init();
/////////////
function addFilter() {
// Call the addValue method from the service
filtersService.addValue({ dataTypeName: 'DDL_Attivita', newValue: vm.newFilterValue })
.then(function (response) {
// Handle success
notificationsService.success('Add Filter', 'Filter added: ' + vm.newFilterValue);
vm.buttonState = 'success';
})
.catch(function (error) {
// Handle error
notificationsService.error('Add Filter', 'Filter NOT added: ' + vm.newFilterValue);
console.error(error);
});
}
function delFilter() {
// Call the removeNode method from the service
filtersService.removeNode({ dataTypeName: 'DDL_Attivita', oldValue: vm.oldFilterValue })
.then(function (response) {
// Handle success
notificationsService.success('Remove Filter', 'Filter removed: ' + vm.oldFilterValue);
vm.buttonState = 'success';
})
.catch(function (error) {
// Handle error
notificationsService.error('Remove Filter', 'Filter NOT removed: ' + vm.oldFilterValue);
console.error(error);
});
}
function init() {
vm.loading = false; // Set to false to indicate that loading is complete
}
}]);
})();
and add.html:
<div ng-controller="filtersTreeController as vm">
<umb-editor-view>
<umb-editor-container>
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<div ng-if="!vm.loading">
<input id="ibox" type="text" ng-model="vm.newFilterValue" placeholder="Inserisci il valore del filtro" class="w-100">
</div>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<button type="button" ng-click="vm.addFilter()" class="btn btn-success">Save</button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</div>
Whatever I try I can't reach the bcakoffice controller. With the browser dev tools I can debug until the return $http.post('/umbraco/backoffice/Filters/FiltersTreeApiController/AddValueToValueListDataType/', data);
but I always get 'Request error: The URL returned a 404 (not found):
/umbraco/backoffice/Filters/FiltersTreeApiController/AddValueToValueListDataType/' whatever path I use.
I'm sure that it must be something I'm missing but can't figure out what I'm doing wrong.
Solved by myself using 'UmbracoApiController' and path in plugin service as 'return $http.post('/umbraco/api/FiltersTreeApi/AddValueToValueListDataType', data);'
The URL returned a 404 (not found) form Api post in Plugin
Hello. I'm developing a plugin to enable the Editors, that don't have access to the Settings section of the Backoffice. The purpose is to allow the Editor to Add or Remove an item from a DropDown List. I have the functions in a Service that perform the operation functional and tested them through a test surface controller. I have built a Section that is displayed as needed:
Following are my Section, Component, Composer and Controller: FiltersSection.cs:
FiltersSectionComponent.cs
FiltersSectinComposer:
and FiltersTreeController.cs:
I have my App_Plugins organized as follows:
and these are the files: package.manifest:
filtersService.js:
filtersController.js:
and add.html:
Whatever I try I can't reach the bcakoffice controller. With the browser dev tools I can debug until the
return $http.post('/umbraco/backoffice/Filters/FiltersTreeApiController/AddValueToValueListDataType/', data);
but I always get 'Request error: The URL returned a 404 (not found): /umbraco/backoffice/Filters/FiltersTreeApiController/AddValueToValueListDataType/' whatever path I use.I'm sure that it must be something I'm missing but can't figure out what I'm doing wrong.
Any help will be appreciated.
Thanks in advance.
Solved by myself using 'UmbracoApiController' and path in plugin service as 'return $http.post('/umbraco/api/FiltersTreeApi/AddValueToValueListDataType', data);'
is working on a reply...