Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • ReneRam 21 posts 186 karma points
    Feb 08, 2024 @ 14:48
    ReneRam
    0

    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: enter image description here

    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;
            }
    
        }
    
    }
    

    I have my App_Plugins organized as follows: enter image description here

    and these are the files: package.manifest:

        {
      "javascript": [
        "~/App_Plugins/Filters/backoffice/FiltersTree/filtersController.js",
        "~/App_Plugins/Filters/backoffice/FiltersTree/filtersService.js"
      ]
    }
    

    filtersService.js:

        (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.

    Any help will be appreciated.

    Thanks in advance.

  • ReneRam 21 posts 186 karma points
    Feb 14, 2024 @ 22:31
    ReneRam
    100

    Solved by myself using 'UmbracoApiController' and path in plugin service as 'return $http.post('/umbraco/api/FiltersTreeApi/AddValueToValueListDataType', data);'

Please Sign in or register to post replies

Write your reply to:

Draft