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);'

  • This forum is in read-only mode while we transition to the new forum.

    You can continue this topic on the new forum by tapping the "Continue discussion" link below.

Please Sign in or register to post replies