VYPR
High severityNVD Advisory· Published Nov 16, 2021· Updated Apr 30, 2025

Piranha CMS - Site-wide Cross-Site Request Forgery (CSRF)

CVE-2021-25976

Description

In PiranhaCMS, versions 4.0.0-alpha1 to 9.2.0 are vulnerable to cross-site request forgery (CSRF) when performing various actions supported by the management system, such as deleting a user, deleting a role, editing a post, deleting a media folder etc., when an ID is known.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

PiranhaCMS 4.0.0-alpha1 to 9.2.0 is vulnerable to CSRF, enabling an attacker to perform privileged actions like user deletion if the target's ID is known.

Vulnerability

PiranhaCMS versions 4.0.0-alpha1 through 9.2.0 are vulnerable to cross-site request forgery (CSRF) in the management system interface. The flaw affects multiple administrative actions—such as deleting a user, deleting a role, editing a post, or deleting a media folder—when the attacker knows the target resource's ID. No anti-CSRF token or origin validation is enforced on the corresponding endpoints [1][4].

Exploitation

An attacker must trick an authenticated administrator into visiting a malicious page while the admin has an active session with the PiranhaCMS manager. The attacker must also know the numeric or string ID of the resource to be targeted (e.g., a user ID). The malicious page then submits a forged request (e.g., via a form or image tag) to the vulnerable endpoint, causing the action to be executed with the admin's privileges [1][4].

Impact

Successful exploitation allows an attacker to perform arbitrary management actions on behalf of an authenticated administrator, including deleting users, deleting roles, editing posts, or deleting media folders. This can lead to unauthorized data loss, disruption of content, and privilege escalation if critical accounts are removed [1][4].

Mitigation

A fix was introduced in commit e42abacdd0dd880ce9cf6607efcc24646ac82eda which adds validation to the login flow and removes unsafe modal comments; however, a fully patched release version is not explicitly named in the available references. Users should upgrade to any version later than 9.2.0 if available, or apply the relevant security patches from the official repository. If no newer release exists, administrators should implement additional CSRF protections such as anti-forgery tokens or restrict management interface access to trusted networks [2][3]. No KEV listing has been published for this CVE.

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
PiranhaNuGet
>= 4.0.0-alpha1, < 10.0-alpha110.0-alpha1

Affected products

2
  • ghsa-coords
    Range: >= 4.0.0-alpha1, < 10.0-alpha1
  • PiranhaCMS/Piranhav5
    Range: 4.0.0-alpha1

Patches

1
e42abacdd0dd

Merge pull request #1742 from PiranhaCMS/features/manager-security-update

https://github.com/PiranhaCMS/piranha.coreHåkan EdlingOct 25, 2021via ghsa
73 files changed · +1051 630
  • core/Piranha.Manager/Areas/Manager/Pages/Index.cshtml.cs+1 1 modified
    @@ -24,7 +24,7 @@ public IndexModel(IAuthorizationService service)
             {
                 _service = service;
             }
    -        public async Task<IActionResult> OnGet()
    +        public async Task<IActionResult> OnGet(string returnUrl = null)
             {
                 var items = await Menu.Items.GetForUser(HttpContext.User, _service);
     
    
  • core/Piranha.Manager/Areas/Manager/Shared/_Layout.cshtml+6 1 modified
    @@ -1,4 +1,5 @@
    -@{
    +@inject Microsoft.Extensions.Options.IOptions<ManagerOptions> Options
    +@{
         var module = Piranha.App.Modules.Get<Piranha.Manager.Module>();
         var prerelease = Piranha.Utils.IsPreRelease(typeof(Piranha.Manager.Module).Assembly) ? "pre-release" : "";
         var isRightToLeft =  @System.Globalization.CultureInfo.CurrentCulture.TextInfo.IsRightToLeft;
    @@ -53,6 +54,10 @@
             var piranha = {};
             window.piranha = piranha;
             piranha.baseUrl = "@Url.Content("~/")";
    +        piranha.antiForgery = {
    +            cookieName: "@Options.Value.XsrfCookieName",
    +            headerName: "@Options.Value.XsrfHeaderName"
    +        };
         </script>
     
         <partial name="~/Areas/Manager/Shared/Partial/_EditorConfig.cshtml" />
    
  • core/Piranha.Manager/Areas/Manager/Shared/Partial/_ContentPickerModal.cshtml+0 1 modified
    @@ -1,6 +1,5 @@
     @inject ManagerLocalizer Localizer
     
    -<!-- The Modal -->
     <div class="modal modal-panel fade" id="contentpicker">
         <div class="modal-dialog modal-lg">
             <div class="modal-content">
    
  • core/Piranha.Manager/Areas/Manager/Shared/Partial/_MediaPickerModal.cshtml+0 1 modified
    @@ -1,7 +1,6 @@
     @inject IAuthorizationService Auth
     @inject ManagerLocalizer Localizer
     
    -<!-- The Modal -->
     <div class="modal modal-panel fade" id="mediapicker">
         <div class="modal-dialog modal-lg">
             <div class="modal-content">
    
  • core/Piranha.Manager/Areas/Manager/Shared/Partial/_PagePickerModal.cshtml+0 1 modified
    @@ -1,6 +1,5 @@
     @inject ManagerLocalizer Localizer
     
    -<!-- The Modal -->
     <div class="modal modal-panel fade" id="pagepicker">
         <div class="modal-dialog modal-lg">
             <div class="modal-content">
    
  • core/Piranha.Manager/Areas/Manager/Shared/Partial/_PostPickerModal.cshtml+0 1 modified
    @@ -1,6 +1,5 @@
     @inject ManagerLocalizer Localizer
     
    -<!-- The Modal -->
     <div class="modal modal-panel fade" id="postpicker">
         <div class="modal-dialog modal-lg">
             <div class="modal-content">
    
  • core/Piranha.Manager/Areas/Manager/Shared/Partial/_PreviewModal.cshtml+0 1 modified
    @@ -1,7 +1,6 @@
     @inject IAuthorizationService Auth
     @inject ManagerLocalizer Localizer
     
    -<!-- The Modal -->
     <div class="modal fade" id="previewModal">
         <div class="modal-dialog modal-lg">
             <div class="modal-content">
    
  • core/Piranha.Manager/assets/dist/css/full.min.css+5 5 modified
  • core/Piranha.Manager/assets/dist/css/slim.min.css+5 5 modified
  • core/Piranha.Manager/assets/dist/js/piranha.alias.js+43 36 modified
    @@ -44,43 +44,45 @@
                 }
     
                 fetch(piranha.baseUrl + "manager/api/alias/save", {
    -                    method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    -                    body: JSON.stringify({
    -                        id: piranha.alias.model.id,
    -                        siteId: piranha.alias.siteId,
    -                        aliasUrl: piranha.alias.model.aliasUrl,
    -                        redirectUrl: piranha.alias.model.redirectUrl,
    -                        isPermanent: piranha.alias.model.isPermanent != null ? piranha.alias.model.isPermanent : false
    -                    })
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: piranha.alias.model.id,
    +                    siteId: piranha.alias.siteId,
    +                    aliasUrl: piranha.alias.model.aliasUrl,
    +                    redirectUrl: piranha.alias.model.redirectUrl,
    +                    isPermanent: piranha.alias.model.isPermanent != null ? piranha.alias.model.isPermanent : false
                     })
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status.type === "success")
    -                    {
    -                        // Remove validation class
    -                        form.classList.remove("was-validated");
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status.type === "success") {
    +                    // Remove validation class
    +                    form.classList.remove("was-validated");
     
    -                        // Close modal
    -                        $("#aliasModal").modal("hide");
    +                    // Close modal
    +                    $("#aliasModal").modal("hide");
     
    -                        // Clear modal
    -                        piranha.alias.model.id = null;
    -                        piranha.alias.model.aliasUrl = null;
    -                        piranha.alias.model.redirectUrl = null;
    -                        piranha.alias.model.isPermanent = true;
    +                    // Clear modal
    +                    piranha.alias.model.id = null;
    +                    piranha.alias.model.aliasUrl = null;
    +                    piranha.alias.model.redirectUrl = null;
    +                    piranha.alias.model.isPermanent = true;
     
    -                        piranha.alias.items = result.items;
    -                    }
    +                    piranha.alias.items = result.items;
    +                }
     
    +                if (result.status !== 400) {
                         // Push status to notification hub
                         piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) {
    -                    console.log("error:", error);
    -                });
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
    +            })
    +            .catch(function (error) {
    +                console.log("error:", error);
    +            });
             },
             remove: function (id) {
                 var self = this;
    @@ -94,17 +96,22 @@
                     onConfirm: function () {
                         fetch(piranha.baseUrl + "manager/api/alias/delete", {
                             method: "delete",
    -                        headers: {
    -                            "Content-Type": "application/json"
    -                        },
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(id)
                         })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
    -                        self.items = result.items;
    +                        if (result.status.type === "success") {
    +                            self.items = result.items;
    +                        }
     
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    +                        if (result.status !== 400) {
    +                            // Push status to notification hub
    +                            piranha.notifications.push(result.status);
    +                        } else {
    +                            // Unauthorized request
    +                            piranha.notifications.unauthorized();
    +                        }
                         })
                         .catch(function (error) { console.log("error:", error ); });
                     }
    
  • core/Piranha.Manager/assets/dist/js/piranha.alias.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.alias=new Vue({el:"#alias",data:{loading:!0,siteId:null,siteTitle:null,sites:[],items:[],model:{id:null,aliasUrl:null,redirectUrl:null,isPermanent:!0}},methods:{load:function(a){var e=this;a||(a=""),fetch(piranha.baseUrl+"manager/api/alias/list/"+a).then(function(a){return a.json()}).then(function(a){e.siteId=a.siteId,e.siteTitle=a.siteTitle,e.sites=a.sites,e.items=a.items}).catch(function(a){console.log("error:",a)})},save:function(){var a=document.getElementById("aliasForm");!1!==a.checkValidity()?fetch(piranha.baseUrl+"manager/api/alias/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:piranha.alias.model.id,siteId:piranha.alias.siteId,aliasUrl:piranha.alias.model.aliasUrl,redirectUrl:piranha.alias.model.redirectUrl,isPermanent:null!=piranha.alias.model.isPermanent&&piranha.alias.model.isPermanent})}).then(function(a){return a.json()}).then(function(e){"success"===e.status.type&&(a.classList.remove("was-validated"),$("#aliasModal").modal("hide"),piranha.alias.model.id=null,piranha.alias.model.aliasUrl=null,piranha.alias.model.redirectUrl=null,piranha.alias.model.isPermanent=!0,piranha.alias.items=e.items),piranha.notifications.push(e.status)}).catch(function(a){console.log("error:",a)}):a.classList.add("was-validated")},remove:function(a){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deleteAliasConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/alias/delete",{method:"delete",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)}).then(function(a){return a.json()}).then(function(a){e.items=a.items,piranha.notifications.push(a.status)}).catch(function(a){console.log("error:",a)})}})}},updated:function(){this.loading=!1}}),$(document).on("shown.bs.modal","#aliasModal",function(a){$(this).find("#aliasUrl").focus()});
    \ No newline at end of file
    +piranha.alias=new Vue({el:"#alias",data:{loading:!0,siteId:null,siteTitle:null,sites:[],items:[],model:{id:null,aliasUrl:null,redirectUrl:null,isPermanent:!0}},methods:{load:function(a){var i=this;a||(a=""),fetch(piranha.baseUrl+"manager/api/alias/list/"+a).then(function(a){return a.json()}).then(function(a){i.siteId=a.siteId,i.siteTitle=a.siteTitle,i.sites=a.sites,i.items=a.items}).catch(function(a){console.log("error:",a)})},save:function(){var a=document.getElementById("aliasForm");!1!==a.checkValidity()?fetch(piranha.baseUrl+"manager/api/alias/save",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({id:piranha.alias.model.id,siteId:piranha.alias.siteId,aliasUrl:piranha.alias.model.aliasUrl,redirectUrl:piranha.alias.model.redirectUrl,isPermanent:null!=piranha.alias.model.isPermanent&&piranha.alias.model.isPermanent})}).then(function(a){return a.json()}).then(function(i){"success"===i.status.type&&(a.classList.remove("was-validated"),$("#aliasModal").modal("hide"),piranha.alias.model.id=null,piranha.alias.model.aliasUrl=null,piranha.alias.model.redirectUrl=null,piranha.alias.model.isPermanent=!0,piranha.alias.items=i.items),400!==i.status?piranha.notifications.push(i.status):piranha.notifications.unauthorized()}).catch(function(a){console.log("error:",a)}):a.classList.add("was-validated")},remove:function(a){var i=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deleteAliasConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/alias/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(a)}).then(function(a){return a.json()}).then(function(a){"success"===a.status.type&&(i.items=a.items),400!==a.status?piranha.notifications.push(a.status):piranha.notifications.unauthorized()}).catch(function(a){console.log("error:",a)})}})}},updated:function(){this.loading=!1}}),$(document).on("shown.bs.modal","#aliasModal",function(a){$(this).find("#aliasUrl").focus()});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.comment.js+53 29 modified
    @@ -44,32 +44,50 @@ piranha.comment = new Vue({
             approve: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/approve/" + id + (self.contentId != null ? "/" + self.contentId : ""))
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status) {
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    -                    }
    -                    self.contentId = result.contentId;
    -                    self.items = result.comments;
    +            fetch(piranha.baseUrl + "manager/api/comment/approve", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: id,
    +                    parentId: self.contentId 
                     })
    -                .catch(function (error) { console.log("error:", error ); });
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                }
    +                self.contentId = result.contentId;
    +                self.items = result.comments;
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             unapprove: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/unapprove/" + id + (self.contentId != null ? "/" + self.contentId : ""))
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status) {
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    -                    }
    -                    self.contentId = result.contentId;
    -                    self.items = result.comments;
    +            fetch(piranha.baseUrl + "manager/api/comment/unapprove", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: id,
    +                    parentId: self.contentId 
                     })
    -                .catch(function (error) { console.log("error:", error ); });
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                }
    +                self.contentId = result.contentId;
    +                self.items = result.comments;
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             toggleApproved: function (item) {
                 item.isApproved = !item.isApproved;
    @@ -83,16 +101,22 @@ piranha.comment = new Vue({
             remove: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/delete/" + id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result);
    +            fetch(piranha.baseUrl + "manager/api/comment/delete", {
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                // Push status to notification hub
    +                piranha.notifications.push(result);
     
    -                    // Refresh the list
    -                    self.load(self.contentId);
    -                })
    -                .catch(function (error) { console.log("error:", error ); });
    +                // Refresh the list
    +                self.load(self.contentId);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             setStatus: function (status) {
                 this.state = status;
    
  • core/Piranha.Manager/assets/dist/js/piranha.comment.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.comment=new Vue({el:"#comments",data:{loading:!0,contentId:null,items:[],state:"all"},computed:{filteredItems:function(){var n=this;return this.items.filter(function(t){return"all"===n.state||("pending"===n.state?!t.isApproved:t.isApproved)})}},methods:{load:function(n){var t=this;n||(n=""),fetch(piranha.baseUrl+"manager/api/comment/"+n).then(function(n){return n.json()}).then(function(n){t.contentId=n.contentId,t.items=n.comments}).catch(function(n){console.log("error:",n)})},approve:function(n){var t=this;fetch(piranha.baseUrl+"manager/api/comment/approve/"+n+(null!=t.contentId?"/"+t.contentId:"")).then(function(n){return n.json()}).then(function(n){n.status&&piranha.notifications.push(n.status),t.contentId=n.contentId,t.items=n.comments}).catch(function(n){console.log("error:",n)})},unapprove:function(n){var t=this;fetch(piranha.baseUrl+"manager/api/comment/unapprove/"+n+(null!=t.contentId?"/"+t.contentId:"")).then(function(n){return n.json()}).then(function(n){n.status&&piranha.notifications.push(n.status),t.contentId=n.contentId,t.items=n.comments}).catch(function(n){console.log("error:",n)})},toggleApproved:function(n){n.isApproved=!n.isApproved,n.isApproved?this.approve(n.id):this.unapprove(n.id)},remove:function(n){var t=this;fetch(piranha.baseUrl+"manager/api/comment/delete/"+n).then(function(n){return n.json()}).then(function(n){piranha.notifications.push(n),t.load(t.contentId)}).catch(function(n){console.log("error:",n)})},setStatus:function(n){this.state=n}},updated:function(){this.loading=!1}});
    \ No newline at end of file
    +piranha.comment=new Vue({el:"#comments",data:{loading:!0,contentId:null,items:[],state:"all"},computed:{filteredItems:function(){var t=this;return this.items.filter(function(n){return"all"===t.state||("pending"===t.state?!n.isApproved:n.isApproved)})}},methods:{load:function(t){var n=this;t||(t=""),fetch(piranha.baseUrl+"manager/api/comment/"+t).then(function(t){return t.json()}).then(function(t){n.contentId=t.contentId,n.items=t.comments}).catch(function(t){console.log("error:",t)})},approve:function(t){var n=this;fetch(piranha.baseUrl+"manager/api/comment/approve",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({id:t,parentId:n.contentId})}).then(function(t){return t.json()}).then(function(t){t.status&&piranha.notifications.push(t.status),n.contentId=t.contentId,n.items=t.comments}).catch(function(t){console.log("error:",t)})},unapprove:function(t){var n=this;fetch(piranha.baseUrl+"manager/api/comment/unapprove",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({id:t,parentId:n.contentId})}).then(function(t){return t.json()}).then(function(t){t.status&&piranha.notifications.push(t.status),n.contentId=t.contentId,n.items=t.comments}).catch(function(t){console.log("error:",t)})},toggleApproved:function(t){t.isApproved=!t.isApproved,t.isApproved?this.approve(t.id):this.unapprove(t.id)},remove:function(t){var n=this;fetch(piranha.baseUrl+"manager/api/comment/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(t)}).then(function(t){return t.json()}).then(function(t){piranha.notifications.push(t),n.load(n.contentId)}).catch(function(t){console.log("error:",t)})},setStatus:function(t){this.state=t}},updated:function(){this.loading=!1}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.components.js+5 1 modified
    @@ -111,7 +111,11 @@ Vue.component("post-archive", {
             confirmIcon: "fas fa-trash",
             confirmText: piranha.resources.texts.delete,
             onConfirm: function () {
    -          fetch(piranha.baseUrl + "manager/api/post/delete/" + postId).then(function (response) {
    +          fetch(piranha.baseUrl + "manager/api/post/delete", {
    +            method: "delete",
    +            headers: piranha.utils.antiForgeryHeaders(),
    +            body: JSON.stringify(postId)
    +          }).then(function (response) {
                 return response.json();
               }).then(function (result) {
                 piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/dist/js/piranha.components.min.js+1 1 modified
  • core/Piranha.Manager/assets/dist/js/piranha.config.js+8 5 modified
    @@ -56,9 +56,7 @@ piranha.config = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/config/save", {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             hierarchicalPageSlugs: self.model.hierarchicalPageSlugs,
                             expandedSitemapLevels: self.model.expandedSitemapLevels,
    @@ -80,8 +78,13 @@ piranha.config = new Vue({
                     })
                     .then(function (response) { return response.json(); })
                     .then(function (result) {
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result.status);
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    
  • core/Piranha.Manager/assets/dist/js/piranha.config.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.config=new Vue({el:"#config",data:{loading:!0,model:{hierarchicalPageSlugs:null,expandedSitemapLevels:null,managerPageSize:null,archivePageSize:null,commentsApprove:null,commentsCloseAfterDays:null,commentsEnabledForPages:null,commentsEnabledForPosts:null,commentsPageSize:null,pagesExpires:null,postsExpires:null,mediaCDN:null,pageRevisions:null,postRevisions:null,defaultCollapsedBlocks:!1,defaultCollapsedBlockGroupHeaders:!1}},methods:{load:function(){self=this,fetch(piranha.baseUrl+"manager/api/config").then(function(e){return e.json()}).then(function(e){self.model.hierarchicalPageSlugs=e.hierarchicalPageSlugs,self.model.expandedSitemapLevels=e.expandedSitemapLevels,self.model.managerPageSize=e.managerPageSize,self.model.archivePageSize=e.archivePageSize,self.model.commentsApprove=e.commentsApprove,self.model.commentsCloseAfterDays=e.commentsCloseAfterDays,self.model.commentsEnabledForPages=e.commentsEnabledForPages,self.model.commentsEnabledForPosts=e.commentsEnabledForPosts,self.model.commentsPageSize=e.commentsPageSize,self.model.pagesExpires=e.pagesExpires,self.model.postsExpires=e.postsExpires,self.model.mediaCDN=e.mediaCDN,self.model.pageRevisions=e.pageRevisions,self.model.postRevisions=e.postRevisions,self.model.defaultCollapsedBlocks=e.defaultCollapsedBlocks,self.model.defaultCollapsedBlockGroupHeaders=e.defaultCollapsedBlockGroupHeaders}).catch(function(e){console.log("error:",e)})},save:function(){self=this,fetch(piranha.baseUrl+"manager/api/config/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify({hierarchicalPageSlugs:self.model.hierarchicalPageSlugs,expandedSitemapLevels:self.model.expandedSitemapLevels,managerPageSize:self.model.managerPageSize,archivePageSize:self.model.archivePageSize,commentsApprove:self.model.commentsApprove,commentsCloseAfterDays:self.model.commentsCloseAfterDays,commentsEnabledForPages:self.model.commentsEnabledForPages,commentsEnabledForPosts:self.model.commentsEnabledForPosts,commentsPageSize:self.model.commentsPageSize,pagesExpires:self.model.pagesExpires,postsExpires:self.model.postsExpires,mediaCDN:self.model.mediaCDN,pageRevisions:self.model.pageRevisions,postRevisions:self.model.postRevisions,defaultCollapsedBlocks:self.model.defaultCollapsedBlocks,defaultCollapsedBlockGroupHeaders:self.model.defaultCollapsedBlockGroupHeaders})}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e.status)}).catch(function(e){console.log("error:",e)})}},created:function(){this.load()},updated:function(){this.loading=!1}});
    \ No newline at end of file
    +piranha.config=new Vue({el:"#config",data:{loading:!0,model:{hierarchicalPageSlugs:null,expandedSitemapLevels:null,managerPageSize:null,archivePageSize:null,commentsApprove:null,commentsCloseAfterDays:null,commentsEnabledForPages:null,commentsEnabledForPosts:null,commentsPageSize:null,pagesExpires:null,postsExpires:null,mediaCDN:null,pageRevisions:null,postRevisions:null,defaultCollapsedBlocks:!1,defaultCollapsedBlockGroupHeaders:!1}},methods:{load:function(){self=this,fetch(piranha.baseUrl+"manager/api/config").then(function(e){return e.json()}).then(function(e){self.model.hierarchicalPageSlugs=e.hierarchicalPageSlugs,self.model.expandedSitemapLevels=e.expandedSitemapLevels,self.model.managerPageSize=e.managerPageSize,self.model.archivePageSize=e.archivePageSize,self.model.commentsApprove=e.commentsApprove,self.model.commentsCloseAfterDays=e.commentsCloseAfterDays,self.model.commentsEnabledForPages=e.commentsEnabledForPages,self.model.commentsEnabledForPosts=e.commentsEnabledForPosts,self.model.commentsPageSize=e.commentsPageSize,self.model.pagesExpires=e.pagesExpires,self.model.postsExpires=e.postsExpires,self.model.mediaCDN=e.mediaCDN,self.model.pageRevisions=e.pageRevisions,self.model.postRevisions=e.postRevisions,self.model.defaultCollapsedBlocks=e.defaultCollapsedBlocks,self.model.defaultCollapsedBlockGroupHeaders=e.defaultCollapsedBlockGroupHeaders}).catch(function(e){console.log("error:",e)})},save:function(){self=this,fetch(piranha.baseUrl+"manager/api/config/save",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({hierarchicalPageSlugs:self.model.hierarchicalPageSlugs,expandedSitemapLevels:self.model.expandedSitemapLevels,managerPageSize:self.model.managerPageSize,archivePageSize:self.model.archivePageSize,commentsApprove:self.model.commentsApprove,commentsCloseAfterDays:self.model.commentsCloseAfterDays,commentsEnabledForPages:self.model.commentsEnabledForPages,commentsEnabledForPosts:self.model.commentsEnabledForPosts,commentsPageSize:self.model.commentsPageSize,pagesExpires:self.model.pagesExpires,postsExpires:self.model.postsExpires,mediaCDN:self.model.mediaCDN,pageRevisions:self.model.pageRevisions,postRevisions:self.model.postRevisions,defaultCollapsedBlocks:self.model.defaultCollapsedBlocks,defaultCollapsedBlockGroupHeaders:self.model.defaultCollapsedBlockGroupHeaders})}).then(function(e){return e.json()}).then(function(e){400!==e.status?piranha.notifications.push(e.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}},created:function(){this.load()},updated:function(){this.loading=!1}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.contentedit.js+6 4 modified
    @@ -195,9 +195,7 @@ piranha.contentedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -233,7 +231,11 @@ piranha.contentedit = new Vue({
                     onConfirm: function () {
                         var groupId = self.groupId;
     
    -                    fetch(piranha.baseUrl + "manager/api/content/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/content/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/dist/js/piranha.contentedit.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.contentedit=new Vue({el:"#contentedit",data:{loading:!0,id:null,languageId:null,typeId:null,typeTitle:null,groupId:null,groupTitle:null,title:null,excerpt:null,state:"new",blocks:[],regions:[],editors:[],categories:[],tags:[],useBlocks:!1,useCategory:!1,useTags:!1,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,useTranslations:!1,permissions:[],languages:[],primaryImage:{id:null,media:null},selectedPermissions:[],saving:!1,selectedRegion:"",selectedSetting:"uid-settings",selectedCategory:null,selectedTags:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl("~/manager/api/media/url/"+this.primaryImage.id+"/448/200"):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},currentLanguage:function(){if(null===this.languages)return{id:"",title:""};var e=this;return e.languages.find(function(t){return t.id===e.languageId})}},mounted(){},beforeDestroy(){},methods:{bind:function(e){this.id=e.id,this.languageId=e.languageId,this.typeId=e.typeId,this.typeTitle=e.typeTitle,this.groupId=e.groupId,this.groupTitle=e.groupTitle,this.title=e.title,this.excerpt=e.excerpt,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.categories=e.categories,this.tags=e.tags,this.languages=e.languages,this.useBlocks=e.useBlocks,this.useCategory=e.useCategory,this.useTags=e.useTags,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.useTranslations=e.useTranslations,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.selectedPermissions=e.selectedPermissions,this.selectedCategory=e.selectedCategory,this.selectedTags=e.selectedTags,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e,t){var i=this,n=piranha.baseUrl+"manager/api/content/"+e;null!=t&&(n+="/"+t),fetch(n).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/content/create/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.save())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/content/save")},saveInternal:function(e){var t=this,i={id:t.id,languageId:t.languageId,typeId:t.typeId,title:t.title,excerpt:t.excerpt,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions,selectedCategory:t.selectedCategory,selectedTags:JSON.parse(JSON.stringify(t.selectedTags)),primaryImage:{id:t.primaryImage.id}};fetch(e,{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.state=e.state,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit content",piranha.baseUrl+"manager/content/edit/"+e.typeId+"/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){var t=e.groupId;fetch(piranha.baseUrl+"manager/api/content/delete/"+e.id).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),window.location=piranha.baseUrl+"manager/content/"+t}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){piranha.contentedit.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectSetting:function(e){this.selectedSetting=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){var e=this;this.loading&&(this.useBlocks&&sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(t){e.moveBlock(t.detail.origin.index,t.detail.destination.index)}),this.useCategory&&($("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedCategory").on("change",function(){var t=$(this).find("option:selected").text();e.selectedCategory=t})),this.useTags&&($("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags}),$("#selectedTags").on("change",function(){var t=$(this).find("option:selected");e.selectedTags=[];for(var i=0;i<t.length;i++)e.selectedTags.push(t[i].text)}))),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    +piranha.contentedit=new Vue({el:"#contentedit",data:{loading:!0,id:null,languageId:null,typeId:null,typeTitle:null,groupId:null,groupTitle:null,title:null,excerpt:null,state:"new",blocks:[],regions:[],editors:[],categories:[],tags:[],useBlocks:!1,useCategory:!1,useTags:!1,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,useTranslations:!1,permissions:[],languages:[],primaryImage:{id:null,media:null},selectedPermissions:[],saving:!1,selectedRegion:"",selectedSetting:"uid-settings",selectedCategory:null,selectedTags:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl("~/manager/api/media/url/"+this.primaryImage.id+"/448/200"):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},currentLanguage:function(){if(null===this.languages)return{id:"",title:""};var e=this;return e.languages.find(function(t){return t.id===e.languageId})}},mounted(){},beforeDestroy(){},methods:{bind:function(e){this.id=e.id,this.languageId=e.languageId,this.typeId=e.typeId,this.typeTitle=e.typeTitle,this.groupId=e.groupId,this.groupTitle=e.groupTitle,this.title=e.title,this.excerpt=e.excerpt,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.categories=e.categories,this.tags=e.tags,this.languages=e.languages,this.useBlocks=e.useBlocks,this.useCategory=e.useCategory,this.useTags=e.useTags,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.useTranslations=e.useTranslations,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.selectedPermissions=e.selectedPermissions,this.selectedCategory=e.selectedCategory,this.selectedTags=e.selectedTags,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e,t){var i=this,s=piranha.baseUrl+"manager/api/content/"+e;null!=t&&(s+="/"+t),fetch(s).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/content/create/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.save())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/content/save")},saveInternal:function(e){var t=this,i={id:t.id,languageId:t.languageId,typeId:t.typeId,title:t.title,excerpt:t.excerpt,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions,selectedCategory:t.selectedCategory,selectedTags:JSON.parse(JSON.stringify(t.selectedTags)),primaryImage:{id:t.primaryImage.id}};fetch(e,{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.state=e.state,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit content",piranha.baseUrl+"manager/content/edit/"+e.typeId+"/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){var t=e.groupId;fetch(piranha.baseUrl+"manager/api/content/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),window.location=piranha.baseUrl+"manager/content/"+t}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){piranha.contentedit.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectSetting:function(e){this.selectedSetting=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){var e=this;this.loading&&(this.useBlocks&&sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(t){e.moveBlock(t.detail.origin.index,t.detail.destination.index)}),this.useCategory&&($("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedCategory").on("change",function(){var t=$(this).find("option:selected").text();e.selectedCategory=t})),this.useTags&&($("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags}),$("#selectedTags").on("change",function(){var t=$(this).find("option:selected");e.selectedTags=[];for(var i=0;i<t.length;i++)e.selectedTags.push(t[i].text)}))),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.contentlist.js+5 1 modified
    @@ -49,7 +49,11 @@ piranha.contentlist = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/content/delete/" + id)
    +                    fetch(piranha.baseUrl + "manager/api/content/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/dist/js/piranha.contentlist.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.contentlist=new Vue({el:"#contentlist",data:{loading:!0,group:null,items:[],types:[]},methods:{bind:function(n){this.group=n.group,this.types=n.types,this.items=n.items.map(function(t){var e=n.types.find(function(n){return n.id===t.typeId});return t.type=e.title||t.typeId,t})},load:function(n){var t=this;piranha.permissions.load(function(){fetch(piranha.baseUrl+"manager/api/content/"+n+"/list").then(function(n){return n.json()}).then(function(n){t.bind(n),t.loading=!1}).catch(function(n){console.log("error:",n)})})},remove:function(n){var t=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/content/delete/"+n).then(function(n){return n.json()}).then(function(n){piranha.notifications.push(n),t.load(t.group.id)}).catch(function(n){console.log("error:",n)})}})}},created:function(){}});
    \ No newline at end of file
    +piranha.contentlist=new Vue({el:"#contentlist",data:{loading:!0,group:null,items:[],types:[]},methods:{bind:function(n){this.group=n.group,this.types=n.types,this.items=n.items.map(function(t){var e=n.types.find(function(n){return n.id===t.typeId});return t.type=e.title||t.typeId,t})},load:function(n){var t=this;piranha.permissions.load(function(){fetch(piranha.baseUrl+"manager/api/content/"+n+"/list").then(function(n){return n.json()}).then(function(n){t.bind(n),t.loading=!1}).catch(function(n){console.log("error:",n)})})},remove:function(n){var t=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/content/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(n)}).then(function(n){return n.json()}).then(function(n){piranha.notifications.push(n),t.load(t.group.id)}).catch(function(n){console.log("error:",n)})}})}},created:function(){}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.js+61 23 modified
    @@ -155,6 +155,7 @@ piranha.dropzone = new function () {
             var defaultOptions = {
                 paramName: 'Uploads',
                 url: piranha.baseUrl + "manager/api/media/upload",
    +            headers: piranha.utils.antiForgeryHeaders(false),
                 thumbnailWidth: 70,
                 thumbnailHeight: 70,
                 previewsContainer: selector + " .media-list",
    @@ -296,7 +297,28 @@ piranha.utils = {
         },
         strLength: function (str) {
             return str != null ? str.length : 0;
    -    }
    +    },
    +    antiForgery: function () {
    +        const cookies = document.cookie.split(";");
    +        for (let i = 0; i < cookies.length; i++) {
    +            let c = cookies[i].trim().split("=");
    +            if (c[0] === piranha.antiForgery.cookieName) {
    +                return c[1];
    +            }
    +        }
    +        return "";
    +    },
    +    antiForgeryHeaders: function (isJson) {
    +        var headers = {};
    +
    +        if (isJson === undefined || isJson === true)
    +        {
    +            headers["Content-Type"] = "application/json";
    +        }
    +        headers[piranha.antiForgery.headerName] = piranha.utils.antiForgery();
    +
    +        return headers;
    +    }    
     };
     
     Date.prototype.addDays = function(days) {
    @@ -479,6 +501,13 @@ piranha.notifications = new Vue({
             items: [],
         },
         methods: {
    +        unauthorized: function() {
    +            this.push({
    +                type: "danger",
    +                body: "Request sender could not be verified by the server.",
    +                hide: true
    +            });
    +        },
             push: function (notification) {
     
                 notification.style = {
    @@ -753,9 +782,7 @@ piranha.mediapicker = new Vue({
                 if (self.folderName !== "") {
                     fetch(piranha.baseUrl + "manager/api/media/folder/save" + (self.filter ? "?filter=" + self.filter : ""), {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             parentId: self.currentFolderId,
                             name: self.folderName
    @@ -772,8 +799,13 @@ piranha.mediapicker = new Vue({
                             self.items = result.media;
                         }
     
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result.status);
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    @@ -1123,22 +1155,25 @@ piranha.languageedit = new Vue({
                     self.loading = true;
                     fetch(piranha.baseUrl + "manager/api/language", {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             items: JSON.parse(JSON.stringify(self.items))
                         })
                     })
                     .then(function (response) { return response.json(); })
                     .then(function (result) {
    -                    //if (result.status.type === "success")
    -                    //{
    +                    if (result.status.type === "success") {
                             self.bind(result);
    -                    //}
    -
    -                    // Push status to notification hub
    -                    // piranha.notifications.push(result.status);
    +                    }
    +                    
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                        self.loading = false;
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    @@ -1151,20 +1186,23 @@ piranha.languageedit = new Vue({
                 self.loading = true;
                 fetch(piranha.baseUrl + "manager/api/language/" + item.id, {
                     method: "delete",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(item)
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
    -                //if (result.status.type === "success")
    -                //{
    +                if (result.status.type === "success") {
                         self.bind(result);
    -                //}
    +                }
     
    -                // Push status to notification hub
    -                // piranha.notifications.push(result.status);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                    self.loading = false;
    +                }
                 })
                 .catch(function (error) {
                     console.log("error:", error);
    
  • core/Piranha.Manager/assets/dist/js/piranha.media.js+52 31 modified
    @@ -119,18 +119,22 @@ piranha.media = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/media/move/" + (folderId || ""), {
                     method: "POST",
    -                headers: {
    -                    'Content-Type': 'application/json'
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(selections)
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
                     if (result.type === "success") {
                         piranha.media.refresh();
                     }
    -                // Push status to notification hub
    -                piranha.notifications.push(result);
    +
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) { console.log("error:", error); });
             },
    @@ -170,7 +174,6 @@ piranha.media = new Vue({
                 piranha.media.load(piranha.media.currentFolderId);
             },
             addFolder: function () {
    -            //this.saveFolder("#mediaFolderModal", "mediaFolderForm", {
                 this.saveFolder(null, null, {
                     parentId: this.currentFolderId,
                     name: this.folder.name
    @@ -197,9 +200,7 @@ piranha.media = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/media/folder/save", {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(folder)
                 })
                 .then(function (response) { return response.json(); })
    @@ -219,8 +220,13 @@ piranha.media = new Vue({
                         self.refresh();
                     }
     
    -                // Push status to notification hub
    -                piranha.notifications.push(result.status);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) {
                     console.log("error:", error);
    @@ -230,19 +236,22 @@ piranha.media = new Vue({
                 var self = this;
     
                 fetch(piranha.baseUrl + "manager/api/media/delete", {
    -                method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify([id])
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
                     // Refresh
                     self.refresh();
     
    -                // Push status to notification hub
    -                piranha.notifications.push(result);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) { console.log("error:", error ); });
             },
    @@ -258,19 +267,22 @@ piranha.media = new Vue({
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
                         fetch(piranha.baseUrl + "manager/api/media/delete", {
    -                        method: "post",
    -                        headers: {
    -                            "Content-Type": "application/json",
    -                        },
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(selections)
                         })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             // Refresh
                             self.refresh();
     
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result);
    +                        if (result.status !== 400) {
    +                            // Push status to notification hub
    +                            piranha.notifications.push(result.status);
    +                        } else {
    +                            // Unauthorized request
    +                            piranha.notifications.unauthorized();
    +                        }
                         })
                         .catch(function (error) { console.log("error:", error); });
                     }
    @@ -279,18 +291,27 @@ piranha.media = new Vue({
             removeFolder: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/media/folder/delete/" + id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/media/folder/delete", {
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    history.pushState({ folderId: id }, "", piranha.baseUrl + "manager/media" + (id ? "/" + id : ""));
    -                    document.title = result.currentFolderName ? result.currentFolderName : "Media";
    +                history.pushState({ folderId: id }, "", piranha.baseUrl + "manager/media" + (id ? "/" + id : ""));
    +                document.title = result.currentFolderName ? result.currentFolderName : "Media";
     
    +                if (result.status !== 400) {
                         // Push status to notification hub
                         piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error ); });
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
    +            })
    +            .catch(function (error) { console.log("error:", error ); });
             }
         },
         computed: {
    
  • core/Piranha.Manager/assets/dist/js/piranha.media.min.js+1 1 modified
    @@ -1 +1 @@
    -Vue.component("folder-item",{props:["item","selected"],template:'\n<li class="dd-item expanded" :class="{ active: item.id === selected, expanded: item.isExpanded || item.items.length === 0 }" :data-id="item.id">\n    <a v-if="!item.edit" class="droppable" v-on:click.prevent="piranha.media.load(item.id)" href="#" draggable="true" v-on:dragstart="piranha.media.drag($event, item)" v-on:dragover="piranha.media.dragover" v-on:dragleave="piranha.media.dragleave" v-on:drop="piranha.media.drop($event, item.id)">\n        <i class="fas fa-folder"></i>{{ item.name }}\n        <span class="badge badge-light float-right">{{ item.mediaCount }}</span>\n    </a>\n    <form v-else v-on:submit.prevent="piranha.media.updateFolder()" class="d-flex">\n        <i class="fas fa-folder"></i>\n        <input :id="\'folder-\' + item.id" type="text" v-on:keyup.esc="piranha.media.cancelEditFolder()" v-model="piranha.media.currentFolderName" class="form-control form-control-sm">\n    </form>\n    <ol v-if="selected === item.id && piranha.media.isAdding" class="dd-list">\n        <form v-on:submit.prevent="piranha.media.addFolder()" class="d-flex">\n            <i class="fas fa-folder"></i><input id="add-folder" type="text" v-on:keyup.esc="piranha.media.isAdding = false" v-model="piranha.media.folder.name" class="form-control form-control-sm">\n        </form>\n    </ol>\n    <ol v-if="item.items.length > 0" class="dd-list">\n        <folder-item v-for="child in item.items" v-bind:key="child.id" v-bind:selected="selected" v-bind:item="child"></folder-item>\n    </ol>\n</li>\n'}),piranha.media=new Vue({el:"#media",data:{loading:!0,listView:!0,currentFolder:null,currentFolderId:null,currentFolderName:null,parentFolderId:null,folders:[],items:[],structure:[],rootCount:null,totalCount:null,canDelete:!1,isAdding:!1,newFolderName:null,folder:{name:null},dropzone:null},methods:{bind:function(e){this.currentFolderId=e.currentFolderId,this.currentFolderName=e.currentFolderName,this.parentFolderId=e.parentFolderId,this.initFolders(e.structure),this.folders=e.folders,this.items=e.media.map(function(e){return e.selected=!1,e}),this.structure=e.structure,this.rootCount=e.rootCount,this.totalCount=e.totalCount,this.canDelete=e.canDelete,this.listView="list"===e.viewMode},initFolders:function(e){for(var t=0;t<e.length;t++)e[t].edit=!1,e[t].id===this.currentFolderId&&(this.currentFolder=e[t]),e[t].items.length>0&&this.initFolders(e[t].items)},editFolder:function(){this.currentFolder.edit=!0,this.$nextTick(function(){document.getElementById("folder-"+this.currentFolderId).focus()})},editAddFolder:function(){this.isAdding=!0,this.$nextTick(function(){document.getElementById("add-folder").focus()})},cancelEditFolder:function(){this.currentFolder.edit=!1,this.currentFolderName=this.currentFolder.name},drag:function(e,t){e.dataTransfer.setData("mediaId",t.id)},dragover:function(e){e.preventDefault();var t=$(e.target).closest(".droppable");t.hasClass("active")||t.addClass("active")},dragleave:function(e){e.preventDefault();var t=$(e.target).closest(".droppable");t.hasClass("active")&&t.removeClass("active")},drop:function(e,t){e.preventDefault();var n=$(e.target).closest(".droppable");n.hasClass("active")&&n.removeClass("active");var i=e.dataTransfer.getData("mediaId");if(i!==t){var a=this.items.filter(e=>e.selected).map(e=>e.id);a.includes(i)||(a=[]).push(i),fetch(piranha.baseUrl+"manager/api/media/move/"+(t||""),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)}).then(function(e){return e.json()}).then(function(e){"success"===e.type&&piranha.media.refresh(),piranha.notifications.push(e)}).catch(function(e){console.log("error:",e)})}},onItemClick:function(e,t){e.shiftKey?t.selected=!t.selected:piranha.preview.openItem(t)},showList:function(){this.listView=!0},showGallery:function(){this.listView=!1},load:function(e,t){var n=this;t||history.pushState({folderId:e},"",piranha.baseUrl+"manager/media"+(e?"/"+e:"")),fetch(piranha.baseUrl+"manager/api/media/list"+(e?"/"+e:"")+"/?width=210&height=160").then(function(e){return e.json()}).then(function(e){n.bind(e),document.title=e.currentFolderName?e.currentFolderName:"Media"}).catch(function(e){console.log("error:",e)})},getThumbnailUrl:function(e){return null!==e.altVersionUrl?e.altVersionUrl:piranha.baseUrl+"manager/api/media/url/"+e.id+"/210/160"},refresh:function(){piranha.media.load(piranha.media.currentFolderId)},addFolder:function(){this.saveFolder(null,null,{parentId:this.currentFolderId,name:this.folder.name}),this.isAdding=!1},updateFolder:function(){this.saveFolder(null,null,{id:this.currentFolderId,name:this.currentFolderName})},saveFolder:function(e,t,n){var i=this;if(null!=t&&!1===(t=document.getElementById(t)).checkValidity())return void t.classList.add("was-validated");fetch(piranha.baseUrl+"manager/api/media/folder/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)}).then(function(e){return e.json()}).then(function(t){"success"===t.status.type&&(null!=e&&$(e).modal("hide"),i.folder.name=null,i.items=t.media,i.refresh()),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},remove:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/delete",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify([e])}).then(function(e){return e.json()}).then(function(e){t.refresh(),piranha.notifications.push(e)}).catch(function(e){console.log("error:",e)})},removeSelection:function(){var e=this,t=this.items.filter(e=>e.selected).map(e=>e.id);piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deleteMediaSelectionConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/media/delete",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(t){e.refresh(),piranha.notifications.push(t)}).catch(function(e){console.log("error:",e)})}})},removeFolder:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/folder/delete/"+e).then(function(e){return e.json()}).then(function(n){t.bind(n),history.pushState({folderId:e},"",piranha.baseUrl+"manager/media"+(e?"/"+e:"")),document.title=n.currentFolderName?n.currentFolderName:"Media",piranha.notifications.push(n.status)}).catch(function(e){console.log("error:",e)})}},computed:{hasSelection:function(){return this.items.some(function(e){return e.selected})}},updated:function(){this.loading&&(piranha.permissions.media.add&&(this.dropzone=piranha.dropzone.init("#media-upload-container",{uploadMultiple:!1}),this.dropzone.on("complete",function(e){"success"===e.status&&setTimeout(function(){piranha.media.dropzone.removeFile(e)},3e3)}),this.dropzone.on("queuecomplete",function(){piranha.media.refresh()})),this.loading=!1)},mounted:function(){var e=this;window.onpopstate=function(t){e.load(t.state&&t.state.folderId?t.state.folderId:"",!0)}}}),$(document).on("shown.bs.modal","#mediaFolderModal",function(e){$("#mediaFolderName").focus()});
    \ No newline at end of file
    +Vue.component("folder-item",{props:["item","selected"],template:'\n<li class="dd-item expanded" :class="{ active: item.id === selected, expanded: item.isExpanded || item.items.length === 0 }" :data-id="item.id">\n    <a v-if="!item.edit" class="droppable" v-on:click.prevent="piranha.media.load(item.id)" href="#" draggable="true" v-on:dragstart="piranha.media.drag($event, item)" v-on:dragover="piranha.media.dragover" v-on:dragleave="piranha.media.dragleave" v-on:drop="piranha.media.drop($event, item.id)">\n        <i class="fas fa-folder"></i>{{ item.name }}\n        <span class="badge badge-light float-right">{{ item.mediaCount }}</span>\n    </a>\n    <form v-else v-on:submit.prevent="piranha.media.updateFolder()" class="d-flex">\n        <i class="fas fa-folder"></i>\n        <input :id="\'folder-\' + item.id" type="text" v-on:keyup.esc="piranha.media.cancelEditFolder()" v-model="piranha.media.currentFolderName" class="form-control form-control-sm">\n    </form>\n    <ol v-if="selected === item.id && piranha.media.isAdding" class="dd-list">\n        <form v-on:submit.prevent="piranha.media.addFolder()" class="d-flex">\n            <i class="fas fa-folder"></i><input id="add-folder" type="text" v-on:keyup.esc="piranha.media.isAdding = false" v-model="piranha.media.folder.name" class="form-control form-control-sm">\n        </form>\n    </ol>\n    <ol v-if="item.items.length > 0" class="dd-list">\n        <folder-item v-for="child in item.items" v-bind:key="child.id" v-bind:selected="selected" v-bind:item="child"></folder-item>\n    </ol>\n</li>\n'}),piranha.media=new Vue({el:"#media",data:{loading:!0,listView:!0,currentFolder:null,currentFolderId:null,currentFolderName:null,parentFolderId:null,folders:[],items:[],structure:[],rootCount:null,totalCount:null,canDelete:!1,isAdding:!1,newFolderName:null,folder:{name:null},dropzone:null},methods:{bind:function(e){this.currentFolderId=e.currentFolderId,this.currentFolderName=e.currentFolderName,this.parentFolderId=e.parentFolderId,this.initFolders(e.structure),this.folders=e.folders,this.items=e.media.map(function(e){return e.selected=!1,e}),this.structure=e.structure,this.rootCount=e.rootCount,this.totalCount=e.totalCount,this.canDelete=e.canDelete,this.listView="list"===e.viewMode},initFolders:function(e){for(var t=0;t<e.length;t++)e[t].edit=!1,e[t].id===this.currentFolderId&&(this.currentFolder=e[t]),e[t].items.length>0&&this.initFolders(e[t].items)},editFolder:function(){this.currentFolder.edit=!0,this.$nextTick(function(){document.getElementById("folder-"+this.currentFolderId).focus()})},editAddFolder:function(){this.isAdding=!0,this.$nextTick(function(){document.getElementById("add-folder").focus()})},cancelEditFolder:function(){this.currentFolder.edit=!1,this.currentFolderName=this.currentFolder.name},drag:function(e,t){e.dataTransfer.setData("mediaId",t.id)},dragover:function(e){e.preventDefault();var t=$(e.target).closest(".droppable");t.hasClass("active")||t.addClass("active")},dragleave:function(e){e.preventDefault();var t=$(e.target).closest(".droppable");t.hasClass("active")&&t.removeClass("active")},drop:function(e,t){e.preventDefault();var n=$(e.target).closest(".droppable");n.hasClass("active")&&n.removeClass("active");var i=e.dataTransfer.getData("mediaId");if(i!==t){var a=this.items.filter(e=>e.selected).map(e=>e.id);a.includes(i)||(a=[]).push(i),fetch(piranha.baseUrl+"manager/api/media/move/"+(t||""),{method:"POST",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(a)}).then(function(e){return e.json()}).then(function(e){"success"===e.type&&piranha.media.refresh(),400!==e.status?piranha.notifications.push(e.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}},onItemClick:function(e,t){e.shiftKey?t.selected=!t.selected:piranha.preview.openItem(t)},showList:function(){this.listView=!0},showGallery:function(){this.listView=!1},load:function(e,t){var n=this;t||history.pushState({folderId:e},"",piranha.baseUrl+"manager/media"+(e?"/"+e:"")),fetch(piranha.baseUrl+"manager/api/media/list"+(e?"/"+e:"")+"/?width=210&height=160").then(function(e){return e.json()}).then(function(e){n.bind(e),document.title=e.currentFolderName?e.currentFolderName:"Media"}).catch(function(e){console.log("error:",e)})},getThumbnailUrl:function(e){return null!==e.altVersionUrl?e.altVersionUrl:piranha.baseUrl+"manager/api/media/url/"+e.id+"/210/160"},refresh:function(){piranha.media.load(piranha.media.currentFolderId)},addFolder:function(){this.saveFolder(null,null,{parentId:this.currentFolderId,name:this.folder.name}),this.isAdding=!1},updateFolder:function(){this.saveFolder(null,null,{id:this.currentFolderId,name:this.currentFolderName})},saveFolder:function(e,t,n){var i=this;if(null!=t&&!1===(t=document.getElementById(t)).checkValidity())return void t.classList.add("was-validated");fetch(piranha.baseUrl+"manager/api/media/folder/save",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(n)}).then(function(e){return e.json()}).then(function(t){"success"===t.status.type&&(null!=e&&$(e).modal("hide"),i.folder.name=null,i.items=t.media,i.refresh()),400!==t.status?piranha.notifications.push(t.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})},remove:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify([e])}).then(function(e){return e.json()}).then(function(e){t.refresh(),400!==e.status?piranha.notifications.push(e.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})},removeSelection:function(){var e=this,t=this.items.filter(e=>e.selected).map(e=>e.id);piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deleteMediaSelectionConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/media/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(t)}).then(function(e){return e.json()}).then(function(t){e.refresh(),400!==t.status?piranha.notifications.push(t.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}})},removeFolder:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/folder/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(n){t.bind(n),history.pushState({folderId:e},"",piranha.baseUrl+"manager/media"+(e?"/"+e:"")),document.title=n.currentFolderName?n.currentFolderName:"Media",400!==n.status?piranha.notifications.push(n.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}},computed:{hasSelection:function(){return this.items.some(function(e){return e.selected})}},updated:function(){this.loading&&(piranha.permissions.media.add&&(this.dropzone=piranha.dropzone.init("#media-upload-container",{uploadMultiple:!1}),this.dropzone.on("complete",function(e){"success"===e.status&&setTimeout(function(){piranha.media.dropzone.removeFile(e)},3e3)}),this.dropzone.on("queuecomplete",function(){piranha.media.refresh()})),this.loading=!1)},mounted:function(){var e=this;window.onpopstate=function(t){e.load(t.state&&t.state.folderId?t.state.folderId:"",!0)}}}),$(document).on("shown.bs.modal","#mediaFolderModal",function(e){$("#mediaFolderName").focus()});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.min.js+1 1 modified
    @@ -1 +1 @@
    -Vue.prototype.eventBus=new Vue,piranha.accessibility=new function(){"use strict";var e=this;e.focus=null,e.removeBlock=function(e){var t=$(":focus").parents(".block");if(1===t.length){if(tinymce){var i=tinymce.activeEditor;i&&i.inline&&i.destroy()}t.find(".block-remove").click()}},$(document).on("show.bs.modal",".modal",function(){e.focus=$(":focus")}),$(document).on("hidden.bs.modal",".modal",function(){e.focus&&(e.focus.focus(),e.focus=null)}),$(window).on("keydown",function(t){$(".main-nav");t.altKey&&(67===t.keyCode?$("#contentSelector").click():8===t.keyCode&&e.removeBlock(t))})},piranha.alert=new Vue({el:"#alert",data:{title:null,body:null,confirmCss:null,confirmIcon:null,confirmText:null,cancelCss:null,cancelIcon:null,cancelText:null,onConfirm:null,onCancel:null,verifyPhrase:null,verifyPlaceholder:null,verifyText:null,verifyInput:null},methods:{open:function(e){e&&(this.title=e.title,this.body=e.body,this.confirmCss=e.confirmCss?e.confirmCss:"btn-success",this.confirmIcon=e.confirmIcon,this.confirmText=e.confirmText?e.confirmText:piranha.resources.texts.ok,this.cancelCss=e.cancelCss?e.cancelCss:"btn-secondary",this.cancelIcon=e.cancelIcon,this.cancelText=e.cancelText?e.cancelText:piranha.resources.texts.cancel,this.onConfirm=e.onConfirm,this.onCancel=e.onCancel,this.verifyPhrase=e.verifyPhrase,this.verifyPlaceholder=e.verifyPlaceholder,this.verifyText=e.verifyText,$("#alert").modal("show"))},confirm:function(){this.onConfirm&&(this.onConfirm(),this.clear()),$("#alert").modal("hide")},cancel:function(){this.onCancel&&(this.onCancel(),this.clear()),$("#alert").modal("hide")},canConfirm:function(){return!this.verifyPhrase||this.verifyPhrase===this.verifyInput},clear:function(){this.onCancel=null,this.onConfirm=null,this.verifyInput=null}}}),Dropzone.autoDiscover=!1,piranha.dropzone=new function(){this.init=function(e,t){t||(t={});var i={paramName:"Uploads",url:piranha.baseUrl+"manager/api/media/upload",thumbnailWidth:70,thumbnailHeight:70,previewsContainer:e+" .media-list",previewTemplate:document.querySelector("#media-upload-template").innerHTML,uploadMultiple:!0,init:function(){t.addedfile||(t.addedfile=function(e){}),t.removedfile||(t.removedfile=function(e){}),t.error||(t.error=function(e){}),t.complete||(t.complete=function(e){if("success"!==e.status&&""!==e.xhr.responseText){var t=JSON.parse(e.xhr.responseText);e.previewElement.querySelector("[data-dz-errormessage]").innerText=t.body}}),t.queuecomplete||(t.queuecomplete=function(){}),this.on("addedfile",t.addedfile),this.on("removedfile",t.removedfile),this.on("complete",t.complete),this.on("queuecomplete",t.queuecomplete)}},n=Object.assign(i,t);return new Dropzone(e+" form",n)}},piranha.permissions={loaded:!1,aliases:{edit:!1,delete:!1},comments:{approve:!1,delete:!1},media:{add:!1,addFolder:!1,delete:!1,deleteFolder:!1,edit:!1},pages:{add:!1,delete:!1,edit:!1,preview:!1,publish:!1,save:!1},posts:{add:!1,delete:!1,edit:!1,preview:!1,publish:!1,save:!1},sites:{add:!1,delete:!1,edit:!1,save:!1},load:function(e){var t=this;this.loaded?e&&e():fetch(piranha.baseUrl+"manager/api/permissions").then(function(e){return e.json()}).then(function(i){t.aliases=i.aliases,t.comments=i.comments,t.media=i.media,t.pages=i.pages,t.posts=i.posts,t.sites=i.sites,t.loaded=!0,e&&e()}).catch(function(t){console.log("error:",t),e&&e()})}},piranha.utils={formatUrl:function(e){return e.replace("~/",piranha.baseUrl)},isEmptyHtml:function(e){return null==e||""==e.replace(/(<([^>]+)>)/gi,"").replace(/\s/g,"")&&-1===e.indexOf("<img")},isEmptyText:function(e){return null==e||""==e.replace(/\s/g,"")||"<br>"==e.replace(/\s/g,"")},strLength:function(e){return null!=e?e.length:0}},Date.prototype.addDays=function(e){var t=new Date(this.valueOf());return t.setDate(t.getDate()+e),t},Date.prototype.toDateString=function(e){var t=new Date(this.valueOf());return t.getFullYear()+"-"+String(t.getMonth()+1).padStart(2,"0")+"-"+String(t.getDate()).padStart(2,"0")},$(document).ready(function(){$(".block-header .danger").hover(function(){$(this).closest(".block").addClass("danger")},function(){$(this).closest(".block").removeClass("danger")}),$("form.validate").submit(function(e){!1===$(this).get(0).checkValidity()&&(e.preventDefault(),e.stopPropagation(),$(this).addClass("was-validated"))})}),$(document).on("mouseenter",".block-header .danger",function(){$(this).closest(".block").addClass("danger")}),$(document).on("mouseleave",".block-header .danger",function(){$(this).closest(".block").removeClass("danger")}),$(document).on("shown.bs.collapse",".collapse",function(){$(this).parent().addClass("active")}),$(document).on("hide.bs.collapse",".collapse",function(){$(this).parent().removeClass("active")}),$(document).on("hidden.bs.modal",".modal",function(){$(".modal:visible").length&&$(document.body).addClass("modal-open")}),$(window).scroll(function(){var e=$(this).scrollTop();$(".app .component-toolbar").each(function(){var t=$(this).parent(),i=t.offset().top;if(e>i){var n=t.outerHeight();e>i+n?$(this).css({top:n+"px"}):$(this).css({top:e-i+"px"})}else $(this).removeAttr("style")})}),piranha.blockpicker=new Vue({el:"#blockpicker",data:{filter:"",categories:[],index:0,callback:null},computed:{filteredCategories:function(){var e=this;return this.categories.filter(function(t){return e.filterBlockTypes(t).length>0})}},methods:{open:function(e,t,i){var n=this,r=piranha.baseUrl+"manager/api/content/blocktypes";piranha.pageedit?r+="/page/"+piranha.pageedit.typeId:piranha.postedit&&(r+="/post/"+piranha.postedit.typeId),fetch(r+(null!=i?"/"+i:"")).then(function(e){return e.json()}).then(function(i){i.typeCount>1?(n.filter="",n.index=t,n.callback=e,n.categories=i.categories,$("#blockpicker").modal("show")):e(i.categories[0].items[0].type,t)}).catch(function(e){console.log("error:",e)})},select:function(e){this.callback(e.type,this.index),this.index=0,this.callback=null,$("#blockpicker").modal("hide")},selectSingleItem:function(){var e=this.filteredCategories;if(1===e.length){var t=this.filterBlockTypes(e[0]);1===t.length&&this.select(t[0])}},filterBlockTypes:function(e){var t=this;return e.items.filter(function(e){return e.name.toLowerCase().indexOf(t.filter.toLowerCase())>-1})}},created:function(){}}),$(document).ready(function(){$("#blockpicker").on("shown.bs.modal",function(){$("#blockpickerSearch").trigger("focus")})}),piranha.notifications=new Vue({el:"#notification-hub",data:{items:[]},methods:{push:function(e){e.style={visible:!1,"notification-info":"info"===e.type,"notification-danger":"danger"===e.type,"notification-success":"success"===e.type,"notification-warning":"warning"===e.type},piranha.notifications.items.push(e),setTimeout(function(){e.style.visible=!0,e.hide&&setTimeout(function(){e.style.visible=!1,setTimeout(function(){piranha.notifications.items.shift()},200)},5e3)},200)}}}),piranha.contentpicker=new Vue({el:"#contentpicker",data:{search:"",groups:[],items:[],currentGroupId:null,currentGroupTitle:null,currentGroupIcon:null,filter:null,callback:null},computed:{filteredItems:function(){var e=this;return this.items.filter(function(t){return!(e.search.length>0)||t.title.toLowerCase().indexOf(e.search.toLowerCase())>-1})}},methods:{bind:function(e,t){this.currentGroupId=e.group.id,this.currentGroupTitle=e.group.title,this.currentGroupIcon=e.group.icon,this.types=e.types,this.items=e.items.map(function(t){var i=e.types.find(function(e){return e.id===t.typeId});return t.type=i.title||t.typeId,t}),t||(this.groups=e.groups)},load:function(e,t){var i=piranha.baseUrl+"manager/api/content/"+(e?e+"/":"")+"list",n=this;fetch(i).then(function(e){return e.json()}).then(function(e){n.bind(e,t)}).catch(function(e){console.log("error:",e)})},loadGroups:function(){},refresh:function(){this.load(piranha.contentpicker.currentGroupId,!0)},open:function(e,t){this.search="",this.callback=t,this.load(e),$("#contentpicker").modal("show")},onEnter:function(){1==this.filteredItems.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#contentpicker").modal("hide")}}}),$(document).ready(function(){$("#contentpicker").on("shown.bs.modal",function(){$("#contentpickerSearch").trigger("focus")})}),piranha.mediapicker=new Vue({el:"#mediapicker",data:{search:"",folderName:"",listView:!0,currentFolderId:null,parentFolderId:null,currentDocumentFolderId:null,parentDocumentFolderId:null,currentImageFolderId:null,parentImageFolderId:null,currentVideoFolderId:null,parentVideoFolderId:null,currentFolderBreadcrumb:null,folders:[],items:[],folder:{name:null},filter:null,callback:null,dropzone:null},computed:{filteredFolders:function(){return this.folders.filter(function(e){return!(piranha.mediapicker.search.length>0)||e.name.toLowerCase().indexOf(piranha.mediapicker.search.toLowerCase())>-1})},filteredItems:function(){return this.items.filter(function(e){return!(piranha.mediapicker.search.length>0)||e.filename.toLowerCase().indexOf(piranha.mediapicker.search.toLowerCase())>-1})}},methods:{toggle:function(){this.listView=!this.listView},load:function(e){var t=this,i=piranha.baseUrl+"manager/api/media/list"+(e?"/"+e:"")+"/?width=210&height=160";t.filter&&(i+="&filter="+t.filter),fetch(i).then(function(e){return e.json()}).then(function(e){if(t.currentFolderId=e.currentFolderId,t.parentFolderId=e.parentFolderId,t.folders=e.folders,t.items=e.media,t.listView="list"===e.viewMode,t.search="",t.currentFolderBreadcrumb=e.currentFolderBreadcrumb,t.filter)switch(t.filter.toLowerCase()){case"document":t.currentDocumentFolderId=e.currentFolderId,t.parentDocumentFolderId=e.parentFolderId;break;case"image":t.currentImageFolderId=e.currentFolderId,t.parentImageFolderId=e.parentFolderId;break;case"video":t.currentVideoFolderId=e.currentFolderId,t.parentVideoFolderId=e.parentFolderId}}).catch(function(e){console.log("error:",e)})},getThumbnailUrl:function(e){return null!==e.altVersionUrl?e.altVersionUrl:piranha.baseUrl+"manager/api/media/url/"+e.id+"/210/160"},refresh:function(){piranha.mediapicker.load(piranha.mediapicker.currentFolderId)},open:function(e,t,i){this.search="",this.callback=e,this.filter=t,this.load(i),$("#mediapicker").modal("show")},openCurrentFolder:function(e,t){this.callback=e,this.filter=t;var i=this.currentFolderId;if(t)switch(t.toLowerCase()){case"document":i=this.currentDocumentFolderId?this.currentDocumentFolderId:i;break;case"image":i=this.currentImageFolderId?this.currentImageFolderId:i;break;case"video":i=this.currentVideoFolderId?this.currentVideoFolderId:i}this.load(i),$("#mediapicker").modal("show")},onEnter:function(){0===this.filteredItems.length&&1===this.filteredFolders.length&&(this.load(this.filteredFolders[0].id),this.search=""),1===this.filteredItems.length&&0===this.filteredFolders.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#mediapicker").modal("hide")},savefolder:function(){var e=this;""!==e.folderName&&fetch(piranha.baseUrl+"manager/api/media/folder/save"+(e.filter?"?filter="+e.filter:""),{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify({parentId:e.currentFolderId,name:e.folderName})}).then(function(e){return e.json()}).then(function(t){"success"===t.status.type&&(e.folderName=null,e.folders=t.folders,e.items=t.media),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})}},mounted:function(){var e=this;piranha.permissions.load(function(){piranha.permissions.media.add&&(e.dropzone=piranha.dropzone.init("#mediapicker-upload-container"),e.dropzone.on("complete",function(t){"success"===t.status&&setTimeout(function(){e.dropzone.removeFile(t)},3e3)}),e.dropzone.on("queuecomplete",function(){piranha.mediapicker.refresh()}))})}}),$(document).ready(function(){$("#mediapicker").on("shown.bs.modal",function(){$("#mediapickerSearch").trigger("focus")})}),piranha.pagepicker=new Vue({el:"#pagepicker",data:{search:"",sites:[],items:[],currentSiteId:null,currentSiteTitle:null,filter:null,callback:null},computed:{filteredItems:function(){var e=this;return this.items.filter(function(t){return!(e.search.length>0)||t.title.toLowerCase().indexOf(e.search.toLowerCase())>-1})}},methods:{load:function(e){var t=piranha.baseUrl+"manager/api/page/sitemap"+(e?"/"+e:""),i=this;fetch(t).then(function(e){return e.json()}).then(function(e){i.currentSiteId=e.siteId,i.currentSiteTitle=e.siteTitle,i.sites=e.sites,i.items=e.items}).catch(function(e){console.log("error:",e)})},refresh:function(){this.load(piranha.pagepicker.currentSiteId)},open:function(e,t){this.search="",this.callback=e,this.load(t),$("#pagepicker").modal("show")},onEnter:function(){1==this.filteredItems.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#pagepicker").modal("hide")}}}),$(document).ready(function(){$("#pagepicker").on("shown.bs.modal",function(){$("#pagepickerSearch").trigger("focus")})}),piranha.postpicker=new Vue({el:"#postpicker",data:{search:"",sites:[],archives:[],posts:[],currentSiteId:null,currentArchiveId:null,currentSiteTitle:null,currentArchiveTitle:null,filter:null,callback:null},computed:{filteredPosts:function(){return this.posts.filter(function(e){return!(piranha.postpicker.search.length>0)||e.title.toLowerCase().indexOf(piranha.postpicker.search.toLowerCase())>-1})}},methods:{load:function(e,t){var i=piranha.baseUrl+"manager/api/post/modal";e&&(i+="?siteId="+e,t&&(i+="&archiveId="+t)),fetch(i).then(function(e){return e.json()}).then(function(e){piranha.postpicker.sites=e.sites,piranha.postpicker.archives=e.archives,piranha.postpicker.posts=e.posts,piranha.postpicker.currentSiteId=e.siteId,piranha.postpicker.currentArchiveId=e.archiveId,piranha.postpicker.currentSiteTitle=e.siteTitle,piranha.postpicker.currentArchiveTitle=e.archiveTitle}).catch(function(e){console.log("error:",e)})},refresh:function(){this.load(this.currentSiteId,this.currentArchiveId)},open:function(e,t,i,n){this.search="",this.callback=e,this.load(t,i),$("#postpicker").modal("show")},onEnter:function(){1==this.filteredPosts.length&&this.select(this.filteredPosts[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#postpicker").modal("hide")}}}),$(document).ready(function(){$("#postpicker").on("shown.bs.modal",function(){$("#postpickerSearch").trigger("focus")})}),piranha.preview=new Vue({el:"#previewModal",data:{empty:{filename:null,type:null,contentType:null,publicUrl:null,size:null,width:null,height:null,title:null,altText:null,description:null,lastModified:null},media:null,dropzone:null},methods:{openItem:function(e){piranha.preview.media=e,piranha.preview.show()},open:function(e){piranha.preview.load(e),piranha.preview.show()},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/"+e).then(function(e){return e.json()}).then(function(e){t.media=e}).catch(function(e){console.log("error:",e)})},saveMeta:function(e){var t=this,i={id:e.id,title:e.title,altText:e.altText,description:e.description,properties:e.properties};fetch(piranha.baseUrl+"manager/api/media/meta/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),"success"===e.type&&t.close()}).catch(function(e){console.log("error:",e)})},show:function(){$("#previewModal").modal("show")},close:function(){$("#previewModal").modal("hide"),setTimeout(function(){piranha.preview.clear()},300)},clear:function(){this.media=this.empty}},created:function(){this.clear()},mounted:function(){this.dropzone=piranha.dropzone.init("#media-update-container",{uploadMultiple:!1}),this.dropzone.on("complete",function(e){setTimeout(function(){piranha.preview.dropzone.removeFile(e)},3e3)}),this.dropzone.on("queuecomplete",function(){piranha.preview.load(piranha.preview.media.id),piranha.media.refresh()})}}),piranha.languageedit=new Vue({el:"#languageedit",data:{loading:!0,items:[],originalDefault:null,currentDefault:null,showDefaultInfo:!1,currentDelete:null,showDeleteInfo:!1},methods:{bind:function(e){for(var t=0;t<e.items.length;t++)e.items[t].errorTitle=!1,e.items[t].isDefault&&(this.originalDefault=this.currentDefault=e.items[t]);this.items=e.items,this.showDefaultInfo=!1,this.showDeleteInfo=!1,this.currentDelete=null,this.loading=!1},load:function(){var e=this;e.loading=!0,fetch(piranha.baseUrl+"manager/api/language").then(function(e){return e.json()}).then(function(t){e.bind(t)}).catch(function(t){console.log("error:",t),e.loading=!1})},save:function(){if(this.validate()){var e=this;e.loading=!0,fetch(piranha.baseUrl+"manager/api/language",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify({items:JSON.parse(JSON.stringify(e.items))})}).then(function(e){return e.json()}).then(function(t){e.bind(t)}).catch(function(e){console.log("error:",e)})}},remove:function(e){var t=this;t.loading=!0,fetch(piranha.baseUrl+"manager/api/language/"+e.id,{method:"delete",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},open:function(){this.load(),$("#languageedit").modal("show")},close:function(){$("#languageedit").modal("hide")},addItem:function(){this.items.push({id:"00000000-0000-0000-0000-000000000000",title:"",culture:"",isDefault:!1})},setDefault:function(e){if(!e.isDefault){for(var t=0;t<this.items.length;t++)this.items[t].id!=e.id&&(this.items[t].isDefault=!1);e.isDefault=!0,this.currentDefault=e,this.originalDefault!=e&&(this.showDefaultInfo=!0)}},setDefaultConfirm:function(e){this.showDefaultInfo=!1},setDefaultCancel:function(e){this.setDefault(this.originalDefault),this.currentDefault=this.originalDefault,this.showDefaultInfo=!1},removeItem:function(e){this.currentDelete=e,this.showDeleteInfo=!0},removeConfirm:function(){this.remove(this.currentDelete)},removeCancel:function(){this.currentDelete=null,this.showDeleteInfo=!1},validate:function(e){isValid=!0;for(var t=0;t<this.items.length;t++)null===this.items[t].title||""===this.items[t].title?(this.items[t].errorTitle=!0,isValid=!1):this.items[t].errorTitle=!1,Vue.set(this.items,t,this.items[t]);return isValid}}}),piranha.resources=new function(){"use strict";var e=this;this.texts={},this.init=function(t){e.texts=t}},piranha.editor={editors:[],addInline:function(e,t){console.log("No HTML editor registered.")},addInlineMarkdown:function(e,t,i){var n=$("#"+e).parent().find(".markdown-preview"),r=new SimpleMDE({element:document.getElementById(e),status:!1,spellChecker:!0,hideIcons:["preview","guide"],initialValue:t,toolbar:["bold","italic","heading","|","quote","unordered-list","ordered-list","|","link",{name:"image",action:function(e){piranha.mediapicker.openCurrentFolder(function(t){var i=e.codemirror,n=r.getState(i).image,o=i.getCursor("start"),a=i.getCursor("end");n?(text=i.getLine(o.line),i.replaceRange("!["+t.filename+"]("+t.publicUrl+")",{line:o.line,ch:0})):i.replaceSelection("!["+t.filename+"]("+t.publicUrl+")"),i.setSelection(o,a),i.focus()},"Image")},className:"fa fa-picture-o",title:"Image"},"side-by-side","fullscreen"],renderingConfig:{singleLineBreaks:!1}});r.codemirror.on("change",function(){n.html(r.markdown(r.value())),i(r.value())}),setTimeout(function(){n.html(r.markdown(r.value())),r.codemirror.refresh()}.bind(r),0),this.editors[e]=r},remove:function(e){console.log("No HTML editor registered.")},removeMarkdown:function(e){var t=this.editors[e];if(null!=t){this.editors.indexOf(t);t.toTextArea(),this.editors.splice[1]}},refreshMarkdown:function(){for(var e in this.editors)this.editors.hasOwnProperty(e)&&this.editors[e].codemirror.refresh()}},$(document).on("focusin",function(e){$(e.target).closest(".tox-tinymce-inline").length&&e.stopImmediatePropagation()}),Vue.component("page-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li :data-id="item.id" class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }">\n    <div class="sitemap-item expanded">\n        <div class="link">\n            <span class="actions">\n                <a v-if="item.items.length > 0 && item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-minus"></i></a>\n                <a v-if="item.items.length > 0 && !item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-plus"></i></a>\n            </span>\n            <a href="#" v-on:click.prevent="piranha.pagepicker.select(item)">\n                {{ item.title }}\n            </a>\n        </div>\n        <div class="type d-none d-md-block">\n            {{ item.typeName }}\n        </div>\n    </div>\n    <ol class="dd-list" v-if="item.items.length > 0">\n        <page-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child">\n        </page-item>\n    </ol>\n</li>\n'});
    \ No newline at end of file
    +Vue.prototype.eventBus=new Vue,piranha.accessibility=new function(){"use strict";var e=this;e.focus=null,e.removeBlock=function(e){var t=$(":focus").parents(".block");if(1===t.length){if(tinymce){var i=tinymce.activeEditor;i&&i.inline&&i.destroy()}t.find(".block-remove").click()}},$(document).on("show.bs.modal",".modal",function(){e.focus=$(":focus")}),$(document).on("hidden.bs.modal",".modal",function(){e.focus&&(e.focus.focus(),e.focus=null)}),$(window).on("keydown",function(t){$(".main-nav");t.altKey&&(67===t.keyCode?$("#contentSelector").click():8===t.keyCode&&e.removeBlock(t))})},piranha.alert=new Vue({el:"#alert",data:{title:null,body:null,confirmCss:null,confirmIcon:null,confirmText:null,cancelCss:null,cancelIcon:null,cancelText:null,onConfirm:null,onCancel:null,verifyPhrase:null,verifyPlaceholder:null,verifyText:null,verifyInput:null},methods:{open:function(e){e&&(this.title=e.title,this.body=e.body,this.confirmCss=e.confirmCss?e.confirmCss:"btn-success",this.confirmIcon=e.confirmIcon,this.confirmText=e.confirmText?e.confirmText:piranha.resources.texts.ok,this.cancelCss=e.cancelCss?e.cancelCss:"btn-secondary",this.cancelIcon=e.cancelIcon,this.cancelText=e.cancelText?e.cancelText:piranha.resources.texts.cancel,this.onConfirm=e.onConfirm,this.onCancel=e.onCancel,this.verifyPhrase=e.verifyPhrase,this.verifyPlaceholder=e.verifyPlaceholder,this.verifyText=e.verifyText,$("#alert").modal("show"))},confirm:function(){this.onConfirm&&(this.onConfirm(),this.clear()),$("#alert").modal("hide")},cancel:function(){this.onCancel&&(this.onCancel(),this.clear()),$("#alert").modal("hide")},canConfirm:function(){return!this.verifyPhrase||this.verifyPhrase===this.verifyInput},clear:function(){this.onCancel=null,this.onConfirm=null,this.verifyInput=null}}}),Dropzone.autoDiscover=!1,piranha.dropzone=new function(){this.init=function(e,t){t||(t={});var i={paramName:"Uploads",url:piranha.baseUrl+"manager/api/media/upload",headers:piranha.utils.antiForgeryHeaders(!1),thumbnailWidth:70,thumbnailHeight:70,previewsContainer:e+" .media-list",previewTemplate:document.querySelector("#media-upload-template").innerHTML,uploadMultiple:!0,init:function(){t.addedfile||(t.addedfile=function(e){}),t.removedfile||(t.removedfile=function(e){}),t.error||(t.error=function(e){}),t.complete||(t.complete=function(e){if("success"!==e.status&&""!==e.xhr.responseText){var t=JSON.parse(e.xhr.responseText);e.previewElement.querySelector("[data-dz-errormessage]").innerText=t.body}}),t.queuecomplete||(t.queuecomplete=function(){}),this.on("addedfile",t.addedfile),this.on("removedfile",t.removedfile),this.on("complete",t.complete),this.on("queuecomplete",t.queuecomplete)}},n=Object.assign(i,t);return new Dropzone(e+" form",n)}},piranha.permissions={loaded:!1,aliases:{edit:!1,delete:!1},comments:{approve:!1,delete:!1},media:{add:!1,addFolder:!1,delete:!1,deleteFolder:!1,edit:!1},pages:{add:!1,delete:!1,edit:!1,preview:!1,publish:!1,save:!1},posts:{add:!1,delete:!1,edit:!1,preview:!1,publish:!1,save:!1},sites:{add:!1,delete:!1,edit:!1,save:!1},load:function(e){var t=this;this.loaded?e&&e():fetch(piranha.baseUrl+"manager/api/permissions").then(function(e){return e.json()}).then(function(i){t.aliases=i.aliases,t.comments=i.comments,t.media=i.media,t.pages=i.pages,t.posts=i.posts,t.sites=i.sites,t.loaded=!0,e&&e()}).catch(function(t){console.log("error:",t),e&&e()})}},piranha.utils={formatUrl:function(e){return e.replace("~/",piranha.baseUrl)},isEmptyHtml:function(e){return null==e||""==e.replace(/(<([^>]+)>)/gi,"").replace(/\s/g,"")&&-1===e.indexOf("<img")},isEmptyText:function(e){return null==e||""==e.replace(/\s/g,"")||"<br>"==e.replace(/\s/g,"")},strLength:function(e){return null!=e?e.length:0},antiForgery:function(){const e=document.cookie.split(";");for(let t=0;t<e.length;t++){let i=e[t].trim().split("=");if(i[0]===piranha.antiForgery.cookieName)return i[1]}return""},antiForgeryHeaders:function(e){var t={};return void 0!==e&&!0!==e||(t["Content-Type"]="application/json"),t[piranha.antiForgery.headerName]=piranha.utils.antiForgery(),t}},Date.prototype.addDays=function(e){var t=new Date(this.valueOf());return t.setDate(t.getDate()+e),t},Date.prototype.toDateString=function(e){var t=new Date(this.valueOf());return t.getFullYear()+"-"+String(t.getMonth()+1).padStart(2,"0")+"-"+String(t.getDate()).padStart(2,"0")},$(document).ready(function(){$(".block-header .danger").hover(function(){$(this).closest(".block").addClass("danger")},function(){$(this).closest(".block").removeClass("danger")}),$("form.validate").submit(function(e){!1===$(this).get(0).checkValidity()&&(e.preventDefault(),e.stopPropagation(),$(this).addClass("was-validated"))})}),$(document).on("mouseenter",".block-header .danger",function(){$(this).closest(".block").addClass("danger")}),$(document).on("mouseleave",".block-header .danger",function(){$(this).closest(".block").removeClass("danger")}),$(document).on("shown.bs.collapse",".collapse",function(){$(this).parent().addClass("active")}),$(document).on("hide.bs.collapse",".collapse",function(){$(this).parent().removeClass("active")}),$(document).on("hidden.bs.modal",".modal",function(){$(".modal:visible").length&&$(document.body).addClass("modal-open")}),$(window).scroll(function(){var e=$(this).scrollTop();$(".app .component-toolbar").each(function(){var t=$(this).parent(),i=t.offset().top;if(e>i){var n=t.outerHeight();e>i+n?$(this).css({top:n+"px"}):$(this).css({top:e-i+"px"})}else $(this).removeAttr("style")})}),piranha.blockpicker=new Vue({el:"#blockpicker",data:{filter:"",categories:[],index:0,callback:null},computed:{filteredCategories:function(){var e=this;return this.categories.filter(function(t){return e.filterBlockTypes(t).length>0})}},methods:{open:function(e,t,i){var n=this,r=piranha.baseUrl+"manager/api/content/blocktypes";piranha.pageedit?r+="/page/"+piranha.pageedit.typeId:piranha.postedit&&(r+="/post/"+piranha.postedit.typeId),fetch(r+(null!=i?"/"+i:"")).then(function(e){return e.json()}).then(function(i){i.typeCount>1?(n.filter="",n.index=t,n.callback=e,n.categories=i.categories,$("#blockpicker").modal("show")):e(i.categories[0].items[0].type,t)}).catch(function(e){console.log("error:",e)})},select:function(e){this.callback(e.type,this.index),this.index=0,this.callback=null,$("#blockpicker").modal("hide")},selectSingleItem:function(){var e=this.filteredCategories;if(1===e.length){var t=this.filterBlockTypes(e[0]);1===t.length&&this.select(t[0])}},filterBlockTypes:function(e){var t=this;return e.items.filter(function(e){return e.name.toLowerCase().indexOf(t.filter.toLowerCase())>-1})}},created:function(){}}),$(document).ready(function(){$("#blockpicker").on("shown.bs.modal",function(){$("#blockpickerSearch").trigger("focus")})}),piranha.notifications=new Vue({el:"#notification-hub",data:{items:[]},methods:{unauthorized:function(){this.push({type:"danger",body:"Request sender could not be verified by the server.",hide:!0})},push:function(e){e.style={visible:!1,"notification-info":"info"===e.type,"notification-danger":"danger"===e.type,"notification-success":"success"===e.type,"notification-warning":"warning"===e.type},piranha.notifications.items.push(e),setTimeout(function(){e.style.visible=!0,e.hide&&setTimeout(function(){e.style.visible=!1,setTimeout(function(){piranha.notifications.items.shift()},200)},5e3)},200)}}}),piranha.contentpicker=new Vue({el:"#contentpicker",data:{search:"",groups:[],items:[],currentGroupId:null,currentGroupTitle:null,currentGroupIcon:null,filter:null,callback:null},computed:{filteredItems:function(){var e=this;return this.items.filter(function(t){return!(e.search.length>0)||t.title.toLowerCase().indexOf(e.search.toLowerCase())>-1})}},methods:{bind:function(e,t){this.currentGroupId=e.group.id,this.currentGroupTitle=e.group.title,this.currentGroupIcon=e.group.icon,this.types=e.types,this.items=e.items.map(function(t){var i=e.types.find(function(e){return e.id===t.typeId});return t.type=i.title||t.typeId,t}),t||(this.groups=e.groups)},load:function(e,t){var i=piranha.baseUrl+"manager/api/content/"+(e?e+"/":"")+"list",n=this;fetch(i).then(function(e){return e.json()}).then(function(e){n.bind(e,t)}).catch(function(e){console.log("error:",e)})},loadGroups:function(){},refresh:function(){this.load(piranha.contentpicker.currentGroupId,!0)},open:function(e,t){this.search="",this.callback=t,this.load(e),$("#contentpicker").modal("show")},onEnter:function(){1==this.filteredItems.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#contentpicker").modal("hide")}}}),$(document).ready(function(){$("#contentpicker").on("shown.bs.modal",function(){$("#contentpickerSearch").trigger("focus")})}),piranha.mediapicker=new Vue({el:"#mediapicker",data:{search:"",folderName:"",listView:!0,currentFolderId:null,parentFolderId:null,currentDocumentFolderId:null,parentDocumentFolderId:null,currentImageFolderId:null,parentImageFolderId:null,currentVideoFolderId:null,parentVideoFolderId:null,currentFolderBreadcrumb:null,folders:[],items:[],folder:{name:null},filter:null,callback:null,dropzone:null},computed:{filteredFolders:function(){return this.folders.filter(function(e){return!(piranha.mediapicker.search.length>0)||e.name.toLowerCase().indexOf(piranha.mediapicker.search.toLowerCase())>-1})},filteredItems:function(){return this.items.filter(function(e){return!(piranha.mediapicker.search.length>0)||e.filename.toLowerCase().indexOf(piranha.mediapicker.search.toLowerCase())>-1})}},methods:{toggle:function(){this.listView=!this.listView},load:function(e){var t=this,i=piranha.baseUrl+"manager/api/media/list"+(e?"/"+e:"")+"/?width=210&height=160";t.filter&&(i+="&filter="+t.filter),fetch(i).then(function(e){return e.json()}).then(function(e){if(t.currentFolderId=e.currentFolderId,t.parentFolderId=e.parentFolderId,t.folders=e.folders,t.items=e.media,t.listView="list"===e.viewMode,t.search="",t.currentFolderBreadcrumb=e.currentFolderBreadcrumb,t.filter)switch(t.filter.toLowerCase()){case"document":t.currentDocumentFolderId=e.currentFolderId,t.parentDocumentFolderId=e.parentFolderId;break;case"image":t.currentImageFolderId=e.currentFolderId,t.parentImageFolderId=e.parentFolderId;break;case"video":t.currentVideoFolderId=e.currentFolderId,t.parentVideoFolderId=e.parentFolderId}}).catch(function(e){console.log("error:",e)})},getThumbnailUrl:function(e){return null!==e.altVersionUrl?e.altVersionUrl:piranha.baseUrl+"manager/api/media/url/"+e.id+"/210/160"},refresh:function(){piranha.mediapicker.load(piranha.mediapicker.currentFolderId)},open:function(e,t,i){this.search="",this.callback=e,this.filter=t,this.load(i),$("#mediapicker").modal("show")},openCurrentFolder:function(e,t){this.callback=e,this.filter=t;var i=this.currentFolderId;if(t)switch(t.toLowerCase()){case"document":i=this.currentDocumentFolderId?this.currentDocumentFolderId:i;break;case"image":i=this.currentImageFolderId?this.currentImageFolderId:i;break;case"video":i=this.currentVideoFolderId?this.currentVideoFolderId:i}this.load(i),$("#mediapicker").modal("show")},onEnter:function(){0===this.filteredItems.length&&1===this.filteredFolders.length&&(this.load(this.filteredFolders[0].id),this.search=""),1===this.filteredItems.length&&0===this.filteredFolders.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#mediapicker").modal("hide")},savefolder:function(){var e=this;""!==e.folderName&&fetch(piranha.baseUrl+"manager/api/media/folder/save"+(e.filter?"?filter="+e.filter:""),{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({parentId:e.currentFolderId,name:e.folderName})}).then(function(e){return e.json()}).then(function(t){"success"===t.status.type&&(e.folderName=null,e.folders=t.folders,e.items=t.media),400!==t.status?piranha.notifications.push(t.status):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}},mounted:function(){var e=this;piranha.permissions.load(function(){piranha.permissions.media.add&&(e.dropzone=piranha.dropzone.init("#mediapicker-upload-container"),e.dropzone.on("complete",function(t){"success"===t.status&&setTimeout(function(){e.dropzone.removeFile(t)},3e3)}),e.dropzone.on("queuecomplete",function(){piranha.mediapicker.refresh()}))})}}),$(document).ready(function(){$("#mediapicker").on("shown.bs.modal",function(){$("#mediapickerSearch").trigger("focus")})}),piranha.pagepicker=new Vue({el:"#pagepicker",data:{search:"",sites:[],items:[],currentSiteId:null,currentSiteTitle:null,filter:null,callback:null},computed:{filteredItems:function(){var e=this;return this.items.filter(function(t){return!(e.search.length>0)||t.title.toLowerCase().indexOf(e.search.toLowerCase())>-1})}},methods:{load:function(e){var t=piranha.baseUrl+"manager/api/page/sitemap"+(e?"/"+e:""),i=this;fetch(t).then(function(e){return e.json()}).then(function(e){i.currentSiteId=e.siteId,i.currentSiteTitle=e.siteTitle,i.sites=e.sites,i.items=e.items}).catch(function(e){console.log("error:",e)})},refresh:function(){this.load(piranha.pagepicker.currentSiteId)},open:function(e,t){this.search="",this.callback=e,this.load(t),$("#pagepicker").modal("show")},onEnter:function(){1==this.filteredItems.length&&this.select(this.filteredItems[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#pagepicker").modal("hide")}}}),$(document).ready(function(){$("#pagepicker").on("shown.bs.modal",function(){$("#pagepickerSearch").trigger("focus")})}),piranha.postpicker=new Vue({el:"#postpicker",data:{search:"",sites:[],archives:[],posts:[],currentSiteId:null,currentArchiveId:null,currentSiteTitle:null,currentArchiveTitle:null,filter:null,callback:null},computed:{filteredPosts:function(){return this.posts.filter(function(e){return!(piranha.postpicker.search.length>0)||e.title.toLowerCase().indexOf(piranha.postpicker.search.toLowerCase())>-1})}},methods:{load:function(e,t){var i=piranha.baseUrl+"manager/api/post/modal";e&&(i+="?siteId="+e,t&&(i+="&archiveId="+t)),fetch(i).then(function(e){return e.json()}).then(function(e){piranha.postpicker.sites=e.sites,piranha.postpicker.archives=e.archives,piranha.postpicker.posts=e.posts,piranha.postpicker.currentSiteId=e.siteId,piranha.postpicker.currentArchiveId=e.archiveId,piranha.postpicker.currentSiteTitle=e.siteTitle,piranha.postpicker.currentArchiveTitle=e.archiveTitle}).catch(function(e){console.log("error:",e)})},refresh:function(){this.load(this.currentSiteId,this.currentArchiveId)},open:function(e,t,i,n){this.search="",this.callback=e,this.load(t,i),$("#postpicker").modal("show")},onEnter:function(){1==this.filteredPosts.length&&this.select(this.filteredPosts[0])},select:function(e){this.callback(JSON.parse(JSON.stringify(e))),this.callback=null,this.search="",$("#postpicker").modal("hide")}}}),$(document).ready(function(){$("#postpicker").on("shown.bs.modal",function(){$("#postpickerSearch").trigger("focus")})}),piranha.preview=new Vue({el:"#previewModal",data:{empty:{filename:null,type:null,contentType:null,publicUrl:null,size:null,width:null,height:null,title:null,altText:null,description:null,lastModified:null},media:null,dropzone:null},methods:{openItem:function(e){piranha.preview.media=e,piranha.preview.show()},open:function(e){piranha.preview.load(e),piranha.preview.show()},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/media/"+e).then(function(e){return e.json()}).then(function(e){t.media=e}).catch(function(e){console.log("error:",e)})},saveMeta:function(e){var t=this,i={id:e.id,title:e.title,altText:e.altText,description:e.description,properties:e.properties};fetch(piranha.baseUrl+"manager/api/media/meta/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),"success"===e.type&&t.close()}).catch(function(e){console.log("error:",e)})},show:function(){$("#previewModal").modal("show")},close:function(){$("#previewModal").modal("hide"),setTimeout(function(){piranha.preview.clear()},300)},clear:function(){this.media=this.empty}},created:function(){this.clear()},mounted:function(){this.dropzone=piranha.dropzone.init("#media-update-container",{uploadMultiple:!1}),this.dropzone.on("complete",function(e){setTimeout(function(){piranha.preview.dropzone.removeFile(e)},3e3)}),this.dropzone.on("queuecomplete",function(){piranha.preview.load(piranha.preview.media.id),piranha.media.refresh()})}}),piranha.languageedit=new Vue({el:"#languageedit",data:{loading:!0,items:[],originalDefault:null,currentDefault:null,showDefaultInfo:!1,currentDelete:null,showDeleteInfo:!1},methods:{bind:function(e){for(var t=0;t<e.items.length;t++)e.items[t].errorTitle=!1,e.items[t].isDefault&&(this.originalDefault=this.currentDefault=e.items[t]);this.items=e.items,this.showDefaultInfo=!1,this.showDeleteInfo=!1,this.currentDelete=null,this.loading=!1},load:function(){var e=this;e.loading=!0,fetch(piranha.baseUrl+"manager/api/language").then(function(e){return e.json()}).then(function(t){e.bind(t)}).catch(function(t){console.log("error:",t),e.loading=!1})},save:function(){if(this.validate()){var e=this;e.loading=!0,fetch(piranha.baseUrl+"manager/api/language",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({items:JSON.parse(JSON.stringify(e.items))})}).then(function(e){return e.json()}).then(function(t){"success"===t.status.type&&e.bind(t),400!==t.status?piranha.notifications.push(t.status):(piranha.notifications.unauthorized(),e.loading=!1)}).catch(function(e){console.log("error:",e)})}},remove:function(e){var t=this;t.loading=!0,fetch(piranha.baseUrl+"manager/api/language/"+e.id,{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){"success"===e.status.type&&t.bind(e),400!==e.status?piranha.notifications.push(e.status):(piranha.notifications.unauthorized(),t.loading=!1)}).catch(function(e){console.log("error:",e)})},open:function(){this.load(),$("#languageedit").modal("show")},close:function(){$("#languageedit").modal("hide")},addItem:function(){this.items.push({id:"00000000-0000-0000-0000-000000000000",title:"",culture:"",isDefault:!1})},setDefault:function(e){if(!e.isDefault){for(var t=0;t<this.items.length;t++)this.items[t].id!=e.id&&(this.items[t].isDefault=!1);e.isDefault=!0,this.currentDefault=e,this.originalDefault!=e&&(this.showDefaultInfo=!0)}},setDefaultConfirm:function(e){this.showDefaultInfo=!1},setDefaultCancel:function(e){this.setDefault(this.originalDefault),this.currentDefault=this.originalDefault,this.showDefaultInfo=!1},removeItem:function(e){this.currentDelete=e,this.showDeleteInfo=!0},removeConfirm:function(){this.remove(this.currentDelete)},removeCancel:function(){this.currentDelete=null,this.showDeleteInfo=!1},validate:function(e){isValid=!0;for(var t=0;t<this.items.length;t++)null===this.items[t].title||""===this.items[t].title?(this.items[t].errorTitle=!0,isValid=!1):this.items[t].errorTitle=!1,Vue.set(this.items,t,this.items[t]);return isValid}}}),piranha.resources=new function(){"use strict";var e=this;this.texts={},this.init=function(t){e.texts=t}},piranha.editor={editors:[],addInline:function(e,t){console.log("No HTML editor registered.")},addInlineMarkdown:function(e,t,i){var n=$("#"+e).parent().find(".markdown-preview"),r=new SimpleMDE({element:document.getElementById(e),status:!1,spellChecker:!0,hideIcons:["preview","guide"],initialValue:t,toolbar:["bold","italic","heading","|","quote","unordered-list","ordered-list","|","link",{name:"image",action:function(e){piranha.mediapicker.openCurrentFolder(function(t){var i=e.codemirror,n=r.getState(i).image,o=i.getCursor("start"),a=i.getCursor("end");n?(text=i.getLine(o.line),i.replaceRange("!["+t.filename+"]("+t.publicUrl+")",{line:o.line,ch:0})):i.replaceSelection("!["+t.filename+"]("+t.publicUrl+")"),i.setSelection(o,a),i.focus()},"Image")},className:"fa fa-picture-o",title:"Image"},"side-by-side","fullscreen"],renderingConfig:{singleLineBreaks:!1}});r.codemirror.on("change",function(){n.html(r.markdown(r.value())),i(r.value())}),setTimeout(function(){n.html(r.markdown(r.value())),r.codemirror.refresh()}.bind(r),0),this.editors[e]=r},remove:function(e){console.log("No HTML editor registered.")},removeMarkdown:function(e){var t=this.editors[e];if(null!=t){this.editors.indexOf(t);t.toTextArea(),this.editors.splice[1]}},refreshMarkdown:function(){for(var e in this.editors)this.editors.hasOwnProperty(e)&&this.editors[e].codemirror.refresh()}},$(document).on("focusin",function(e){$(e.target).closest(".tox-tinymce-inline").length&&e.stopImmediatePropagation()}),Vue.component("page-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li :data-id="item.id" class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }">\n    <div class="sitemap-item expanded">\n        <div class="link">\n            <span class="actions">\n                <a v-if="item.items.length > 0 && item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-minus"></i></a>\n                <a v-if="item.items.length > 0 && !item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-plus"></i></a>\n            </span>\n            <a href="#" v-on:click.prevent="piranha.pagepicker.select(item)">\n                {{ item.title }}\n            </a>\n        </div>\n        <div class="type d-none d-md-block">\n            {{ item.typeName }}\n        </div>\n    </div>\n    <ol class="dd-list" v-if="item.items.length > 0">\n        <page-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child">\n        </page-item>\n    </ol>\n</li>\n'});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.pageedit.js+30 19 modified
    @@ -280,9 +280,7 @@ piranha.pageedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -314,29 +312,38 @@ piranha.pageedit = new Vue({
             revert: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/page/revert/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/page/revert", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
             },
             detach: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/page/detach/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/page/detach", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
    -
             },
             remove: function () {
                 var self = this;
    @@ -348,7 +355,11 @@ piranha.pageedit = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/page/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/page/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/dist/js/piranha.pageedit.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.pageedit=new Vue({el:"#pageedit",data:{loading:!0,id:null,siteId:null,parentId:null,originalId:null,sortOrder:0,typeId:null,title:null,navigationTitle:null,slug:null,metaTitle:null,metaKeywords:null,metaDescription:null,metaIndex:null,metaFollow:null,metaPriority:null,ogTitle:null,ogDescription:null,ogImage:{id:null,media:null},excerpt:null,isHidden:!1,published:null,publishedTime:null,redirectUrl:null,redirectType:null,enableComments:null,closeCommentsAfterDays:null,commentCount:null,pendingCommentCount:0,state:"new",blocks:[],regions:[],editors:[],useBlocks:!0,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,permissions:[],primaryImage:{id:null,media:null},selectedPermissions:[],isCopy:!1,isScheduled:!1,saving:!1,savingDraft:!1,selectedRegion:{uid:"uid-blocks",name:null,icon:null},selectedSetting:"uid-settings",selectedRoute:null,routes:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl("~/manager/api/media/url/"+this.primaryImage.id+"/448/200"):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},metaPriorityDescription:function(){var e=piranha.resources.texts.important;return this.metaPriority<=.3?e=piranha.resources.texts.low:this.metaPriority<=.6?e=piranha.resources.texts.medium:this.metaPriority<=.9&&(e=piranha.resources.texts.high),e+" ("+this.metaPriority+")"}},mounted(){document.addEventListener("keydown",this.doHotKeys)},beforeDestroy(){document.removeEventListener("keydown",this.doHotKeys)},methods:{bind:function(e){this.id=e.id,this.siteId=e.siteId,this.parentId=e.parentId,this.originalId=e.originalId,this.sortOrder=e.sortOrder,this.typeId=e.typeId,this.title=e.title,this.navigationTitle=e.navigationTitle,this.slug=e.slug,this.metaTitle=e.metaTitle,this.metaKeywords=e.metaKeywords,this.metaDescription=e.metaDescription,this.metaIndex=e.metaIndex,this.metaFollow=e.metaFollow,this.metaPriority=e.metaPriority,this.ogTitle=e.ogTitle,this.ogDescription=e.ogDescription,this.ogImage=e.ogImage,this.excerpt=e.excerpt,this.isHidden=e.isHidden,this.published=e.published,this.publishedTime=e.publishedTime,this.redirectUrl=e.redirectUrl,this.redirectType=e.redirectType,this.enableComments=e.enableComments,this.closeCommentsAfterDays=e.closeCommentsAfterDays,this.commentCount=e.commentCount,this.pendingCommentCount=e.pendingCommentCount,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.useBlocks=e.useBlocks,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.isCopy=e.isCopy,this.isScheduled=e.isScheduled,this.selectedRoute=e.selectedRoute,this.routes=e.routes,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.selectedPermissions=e.selectedPermissions,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/page/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/page/create/"+e+"/"+t).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},createrelative:function(e,t,i){var n=this;fetch(piranha.baseUrl+"manager/api/page/createrelative/"+e+"/"+t+"/"+i).then(function(e){return e.json()}).then(function(e){n.bind(e)}).catch(function(e){console.log("error:",e)})},copyrelative:function(e,t,i){var n=this;fetch(piranha.baseUrl+"manager/api/page/copyrelative/"+e+"/"+t+"/"+i).then(function(e){return e.json()}).then(function(e){n.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.saveDraft())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save")},saveDraft:function(){this.savingDraft=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save/draft")},unpublish:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save/unpublish")},saveInternal:function(e){var t=this,i={id:t.id,siteId:t.siteId,parentId:t.parentId,originalId:t.originalId,sortOrder:t.sortOrder,typeId:t.typeId,title:t.title,navigationTitle:t.navigationTitle,slug:t.slug,metaTitle:t.metaTitle,metaKeywords:t.metaKeywords,metaDescription:t.metaDescription,metaIndex:t.metaIndex,metaFollow:t.metaFollow,metaPriority:t.metaPriority,ogTitle:t.ogTitle,ogDescription:t.ogDescription,ogImage:{id:t.ogImage.id},excerpt:t.excerpt,isHidden:t.isHidden,published:t.published,publishedTime:t.publishedTime,redirectUrl:t.redirectUrl,redirectType:t.redirectType,enableComments:t.enableComments,closeCommentsAfterDays:t.closeCommentsAfterDays,isCopy:t.isCopy,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions,primaryImage:{id:t.primaryImage.id}};fetch(e,{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.slug=e.slug,t.published=e.published,t.publishedTime=e.publishedTime,t.state=e.state,t.isCopy=e.isCopy,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit page",piranha.baseUrl+"manager/page/edit/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.savingDraft=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},revert:function(){var e=this;fetch(piranha.baseUrl+"manager/api/page/revert/"+e.id).then(function(e){return e.json()}).then(function(t){e.bind(t),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},detach:function(){var e=this;fetch(piranha.baseUrl+"manager/api/page/detach/"+e.id).then(function(e){return e.json()}).then(function(t){e.bind(t),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/page/delete/"+e.id).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),window.location=piranha.baseUrl+"manager/pages"}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){piranha.pageedit.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectSetting:function(e){this.selectedSetting=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},isCommentsOpen:function(){var e=new Date(this.published+" "+this.publishedTime);return(e=e.addDays(this.closeCommentsAfterDays))>new Date},commentsClosedDate:function(){var e=new Date(this.published+" "+this.publishedTime);return(e=e.addDays(this.closeCommentsAfterDays)).toDateString()},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){this.loading?(sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(e){piranha.pageedit.moveBlock(e.detail.origin.index,e.detail.destination.index)}),piranha.editor.addInline("excerpt-body","excerpt-toolbar")):(sortable("#content-blocks","disable"),sortable("#content-blocks","enable")),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    +piranha.pageedit=new Vue({el:"#pageedit",data:{loading:!0,id:null,siteId:null,parentId:null,originalId:null,sortOrder:0,typeId:null,title:null,navigationTitle:null,slug:null,metaTitle:null,metaKeywords:null,metaDescription:null,metaIndex:null,metaFollow:null,metaPriority:null,ogTitle:null,ogDescription:null,ogImage:{id:null,media:null},excerpt:null,isHidden:!1,published:null,publishedTime:null,redirectUrl:null,redirectType:null,enableComments:null,closeCommentsAfterDays:null,commentCount:null,pendingCommentCount:0,state:"new",blocks:[],regions:[],editors:[],useBlocks:!0,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,permissions:[],primaryImage:{id:null,media:null},selectedPermissions:[],isCopy:!1,isScheduled:!1,saving:!1,savingDraft:!1,selectedRegion:{uid:"uid-blocks",name:null,icon:null},selectedSetting:"uid-settings",selectedRoute:null,routes:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl("~/manager/api/media/url/"+this.primaryImage.id+"/448/200"):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},metaPriorityDescription:function(){var e=piranha.resources.texts.important;return this.metaPriority<=.3?e=piranha.resources.texts.low:this.metaPriority<=.6?e=piranha.resources.texts.medium:this.metaPriority<=.9&&(e=piranha.resources.texts.high),e+" ("+this.metaPriority+")"}},mounted(){document.addEventListener("keydown",this.doHotKeys)},beforeDestroy(){document.removeEventListener("keydown",this.doHotKeys)},methods:{bind:function(e){this.id=e.id,this.siteId=e.siteId,this.parentId=e.parentId,this.originalId=e.originalId,this.sortOrder=e.sortOrder,this.typeId=e.typeId,this.title=e.title,this.navigationTitle=e.navigationTitle,this.slug=e.slug,this.metaTitle=e.metaTitle,this.metaKeywords=e.metaKeywords,this.metaDescription=e.metaDescription,this.metaIndex=e.metaIndex,this.metaFollow=e.metaFollow,this.metaPriority=e.metaPriority,this.ogTitle=e.ogTitle,this.ogDescription=e.ogDescription,this.ogImage=e.ogImage,this.excerpt=e.excerpt,this.isHidden=e.isHidden,this.published=e.published,this.publishedTime=e.publishedTime,this.redirectUrl=e.redirectUrl,this.redirectType=e.redirectType,this.enableComments=e.enableComments,this.closeCommentsAfterDays=e.closeCommentsAfterDays,this.commentCount=e.commentCount,this.pendingCommentCount=e.pendingCommentCount,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.useBlocks=e.useBlocks,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.isCopy=e.isCopy,this.isScheduled=e.isScheduled,this.selectedRoute=e.selectedRoute,this.routes=e.routes,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.selectedPermissions=e.selectedPermissions,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/page/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/page/create/"+e+"/"+t).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},createrelative:function(e,t,i){var n=this;fetch(piranha.baseUrl+"manager/api/page/createrelative/"+e+"/"+t+"/"+i).then(function(e){return e.json()}).then(function(e){n.bind(e)}).catch(function(e){console.log("error:",e)})},copyrelative:function(e,t,i){var n=this;fetch(piranha.baseUrl+"manager/api/page/copyrelative/"+e+"/"+t+"/"+i).then(function(e){return e.json()}).then(function(e){n.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.saveDraft())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save")},saveDraft:function(){this.savingDraft=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save/draft")},unpublish:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/page/save/unpublish")},saveInternal:function(e){var t=this,i={id:t.id,siteId:t.siteId,parentId:t.parentId,originalId:t.originalId,sortOrder:t.sortOrder,typeId:t.typeId,title:t.title,navigationTitle:t.navigationTitle,slug:t.slug,metaTitle:t.metaTitle,metaKeywords:t.metaKeywords,metaDescription:t.metaDescription,metaIndex:t.metaIndex,metaFollow:t.metaFollow,metaPriority:t.metaPriority,ogTitle:t.ogTitle,ogDescription:t.ogDescription,ogImage:{id:t.ogImage.id},excerpt:t.excerpt,isHidden:t.isHidden,published:t.published,publishedTime:t.publishedTime,redirectUrl:t.redirectUrl,redirectType:t.redirectType,enableComments:t.enableComments,closeCommentsAfterDays:t.closeCommentsAfterDays,isCopy:t.isCopy,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions,primaryImage:{id:t.primaryImage.id}};fetch(e,{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.slug=e.slug,t.published=e.published,t.publishedTime=e.publishedTime,t.state=e.state,t.isCopy=e.isCopy,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit page",piranha.baseUrl+"manager/page/edit/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.savingDraft=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},revert:function(){var e=this;fetch(piranha.baseUrl+"manager/api/page/revert",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){e.bind(t),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},detach:function(){var e=this;fetch(piranha.baseUrl+"manager/api/page/detach",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){e.bind(t),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/page/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),window.location=piranha.baseUrl+"manager/pages"}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){piranha.pageedit.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},selectSetting:function(e){this.selectedSetting=e,Vue.nextTick(function(){piranha.editor.refreshMarkdown()})},isCommentsOpen:function(){var e=new Date(this.published+" "+this.publishedTime);return(e=e.addDays(this.closeCommentsAfterDays))>new Date},commentsClosedDate:function(){var e=new Date(this.published+" "+this.publishedTime);return(e=e.addDays(this.closeCommentsAfterDays)).toDateString()},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){this.loading?(sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(e){piranha.pageedit.moveBlock(e.detail.origin.index,e.detail.destination.index)}),piranha.editor.addInline("excerpt-body","excerpt-toolbar")):(sortable("#content-blocks","disable"),sortable("#content-blocks","enable")),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.pagelist.js+6 4 modified
    @@ -57,7 +57,11 @@ piranha.pagelist = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/page/delete/" + id)
    +                    fetch(piranha.baseUrl + "manager/api/page/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    @@ -78,9 +82,7 @@ piranha.pagelist = new Vue({
                         callback: function (l, e) {
                             fetch(piranha.baseUrl + "manager/api/page/move", {
                                 method: "post",
    -                            headers: {
    -                                "Content-Type": "application/json"
    -                            },
    +                            headers: piranha.utils.antiForgeryHeaders(),
                                 body: JSON.stringify({
                                     id: $(e).attr("data-id"),
                                     items: $(l).nestable("serialize")
    
  • core/Piranha.Manager/assets/dist/js/piranha.pagelist.min.js+1 1 modified
    @@ -1 +1 @@
    -Vue.component("pagecopy-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }">\n    <div class="sitemap-item expanded">\n        <div class="link" :class="{ readonly: item.isCopy }">\n            <a v-if="!item.isCopy" :href="piranha.baseUrl + \'manager/page/copyrelative/\' + item.id + \'/\' + piranha.pagelist.addPageId + \'/\' + piranha.pagelist.addAfter">\n                {{ item.title }}\n            </a>\n            <a href="#" v-else>\n                {{ item.title }}\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </a>\n            <div class="content-blocker"></div>\n        </div>\n        <div class="type d-none d-md-block">\n            {{ item.typeName }}\n        </div>\n    </div>\n    <ol class="dd-list" v-if="item.items.length > 0">\n        <pagecopy-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child"></pagecopy-item>\n    </ol>\n</li>\n'}),Vue.component("sitemap-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }" :data-id="item.id">\n    <div class="sitemap-item" :class="{ dimmed: item.isUnpublished || item.isScheduled }">\n        <div class="handle dd-handle"><i class="fas fa-ellipsis-v"></i></div>\n        <div class="link">\n            <span class="actions">\n                <a v-if="item.items.length > 0 && item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-minus"></i></a>\n                <a v-if="item.items.length > 0 && !item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-plus"></i></a>\n            </span>\n            <a v-if="piranha.permissions.pages.edit" :href="piranha.baseUrl + item.editUrl + item.id">\n                <span>{{ item.title }}</span>\n                <span v-if="item.isRestricted" class="icon-restricted text-secondary small"><i class="fas fa-lock"></i></span>\n                <span v-if="item.status" class="badge badge-info">{{ item.status }}</span>\n                <span v-if="item.isScheduled" class="badge badge-info">{{ piranha.resources.texts.scheduled }}</span>\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </a>\n            <span v-else class="title">\n                <span>{{ item.title }}</span>\n                <span v-if="item.isRestricted" class="icon-restricted text-secondary small"><i class="fas fa-lock"></i></span>\n                <span v-if="item.status" class="badge badge-info">{{ item.status }}</span>\n                <span v-if="item.isScheduled" class="badge badge-info">{{ piranha.resources.texts.scheduled }}</span>\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </span>\n        </div>\n        <div class="type d-none d-md-block">{{ item.typeName }}</div>\n        <div class="date d-none d-lg-block">{{ item.published }}</div>\n        <div class="actions">\n            <a v-if="piranha.permissions.pages.add" href="#" v-on:click.prevent="piranha.pagelist.add(item.siteId, item.id, true)"><i class="fas fa-angle-down"></i></a>\n            <a v-if="piranha.permissions.pages.add" href="#" v-on:click.prevent="piranha.pagelist.add(item.siteId, item.id, false)"><i class="fas fa-angle-right"></i></a>\n            <a v-if="piranha.permissions.pages.delete && item.items.length === 0" v-on:click.prevent="piranha.pagelist.remove(item.id)" class="danger" href="#"><i class="fas fa-trash"></i></a>\n        </div>\n    </div>\n    <ol v-if="item.items.length > 0" class="dd-list">\n        <sitemap-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child">\n        </sitemap-item>\n    </ol>\n</li>\n'}),piranha.pagelist=new Vue({el:"#pagelist",data:{loading:!0,updateBindings:!1,items:[],sites:[],pageTypes:[],addSiteId:null,addSiteTitle:null,addPageId:null,addAfter:!0},methods:{load:function(){var e=this;piranha.permissions.load(function(){fetch(piranha.baseUrl+"manager/api/page/list").then(function(e){return e.json()}).then(function(i){e.sites=i.sites,e.pageTypes=i.pageTypes,e.updateBindings=!0}).catch(function(e){console.log("error:",e)})})},remove:function(e){var i=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/page/delete/"+e).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),i.load()}).catch(function(e){console.log("error:",e)})}})},bind:function(){var e=this;$(".sitemap-container").each(function(i,s){$(s).nestable({maxDepth:100,group:i,callback:function(i,s){fetch(piranha.baseUrl+"manager/api/page/move",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify({id:$(s).attr("data-id"),items:$(i).nestable("serialize")})}).then(function(e){return e.json()}).then(function(i){piranha.notifications.push(i.status),"success"===i.status.type&&($(".sitemap-container").nestable("destroy"),e.sites=[],Vue.nextTick(function(){e.sites=i.sites,Vue.nextTick(function(){e.bind()})}))}).catch(function(e){console.log("error:",e)})}})})},add:function(e,i,s){var a=this;a.addSiteId=e,a.addPageId=i,a.addAfter=s,a.sites.forEach(function(i){i.id===e&&(a.addSiteTitle=i.title)}),$("#pageAddModal").modal("show")},selectSite:function(e){var i=this;i.addSiteId=e,i.sites.forEach(function(s){s.id===e&&(i.addSiteTitle=s.title)})},collapse:function(){for(var e=0;e<this.sites.length;e++)for(var i=0;i<this.sites[e].pages.length;i++)this.changeVisibility(this.sites[e].pages[i],!1)},expand:function(){for(var e=0;e<this.sites.length;e++)for(var i=0;i<this.sites[e].pages.length;i++)this.changeVisibility(this.sites[e].pages[i],!0)},changeVisibility:function(e,i){e.isExpanded=i;for(var s=0;s<e.items.length;s++)this.changeVisibility(e.items[s],i)}},created:function(){},updated:function(){this.updateBindings&&(this.bind(),this.updateBindings=!1),this.loading=!1}});
    \ No newline at end of file
    +Vue.component("pagecopy-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }">\n    <div class="sitemap-item expanded">\n        <div class="link" :class="{ readonly: item.isCopy }">\n            <a v-if="!item.isCopy" :href="piranha.baseUrl + \'manager/page/copyrelative/\' + item.id + \'/\' + piranha.pagelist.addPageId + \'/\' + piranha.pagelist.addAfter">\n                {{ item.title }}\n            </a>\n            <a href="#" v-else>\n                {{ item.title }}\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </a>\n            <div class="content-blocker"></div>\n        </div>\n        <div class="type d-none d-md-block">\n            {{ item.typeName }}\n        </div>\n    </div>\n    <ol class="dd-list" v-if="item.items.length > 0">\n        <pagecopy-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child"></pagecopy-item>\n    </ol>\n</li>\n'}),Vue.component("sitemap-item",{props:["item"],methods:{toggleItem:function(e){e.isExpanded=!e.isExpanded}},template:'\n<li class="dd-item" :class="{ expanded: item.isExpanded || item.items.length === 0 }" :data-id="item.id">\n    <div class="sitemap-item" :class="{ dimmed: item.isUnpublished || item.isScheduled }">\n        <div class="handle dd-handle"><i class="fas fa-ellipsis-v"></i></div>\n        <div class="link">\n            <span class="actions">\n                <a v-if="item.items.length > 0 && item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-minus"></i></a>\n                <a v-if="item.items.length > 0 && !item.isExpanded" v-on:click.prevent="toggleItem(item)" class="expand" href="#"><i class="fas fa-plus"></i></a>\n            </span>\n            <a v-if="piranha.permissions.pages.edit" :href="piranha.baseUrl + item.editUrl + item.id">\n                <span>{{ item.title }}</span>\n                <span v-if="item.isRestricted" class="icon-restricted text-secondary small"><i class="fas fa-lock"></i></span>\n                <span v-if="item.status" class="badge badge-info">{{ item.status }}</span>\n                <span v-if="item.isScheduled" class="badge badge-info">{{ piranha.resources.texts.scheduled }}</span>\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </a>\n            <span v-else class="title">\n                <span>{{ item.title }}</span>\n                <span v-if="item.isRestricted" class="icon-restricted text-secondary small"><i class="fas fa-lock"></i></span>\n                <span v-if="item.status" class="badge badge-info">{{ item.status }}</span>\n                <span v-if="item.isScheduled" class="badge badge-info">{{ piranha.resources.texts.scheduled }}</span>\n                <span v-if="item.isCopy" class="badge badge-warning">{{ piranha.resources.texts.copy }}</span>\n            </span>\n        </div>\n        <div class="type d-none d-md-block">{{ item.typeName }}</div>\n        <div class="date d-none d-lg-block">{{ item.published }}</div>\n        <div class="actions">\n            <a v-if="piranha.permissions.pages.add" href="#" v-on:click.prevent="piranha.pagelist.add(item.siteId, item.id, true)"><i class="fas fa-angle-down"></i></a>\n            <a v-if="piranha.permissions.pages.add" href="#" v-on:click.prevent="piranha.pagelist.add(item.siteId, item.id, false)"><i class="fas fa-angle-right"></i></a>\n            <a v-if="piranha.permissions.pages.delete && item.items.length === 0" v-on:click.prevent="piranha.pagelist.remove(item.id)" class="danger" href="#"><i class="fas fa-trash"></i></a>\n        </div>\n    </div>\n    <ol v-if="item.items.length > 0" class="dd-list">\n        <sitemap-item v-for="child in item.items" v-bind:key="child.id" v-bind:item="child">\n        </sitemap-item>\n    </ol>\n</li>\n'}),piranha.pagelist=new Vue({el:"#pagelist",data:{loading:!0,updateBindings:!1,items:[],sites:[],pageTypes:[],addSiteId:null,addSiteTitle:null,addPageId:null,addAfter:!0},methods:{load:function(){var e=this;piranha.permissions.load(function(){fetch(piranha.baseUrl+"manager/api/page/list").then(function(e){return e.json()}).then(function(i){e.sites=i.sites,e.pageTypes=i.pageTypes,e.updateBindings=!0}).catch(function(e){console.log("error:",e)})})},remove:function(e){var i=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePageConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/page/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e)}).then(function(e){return e.json()}).then(function(e){piranha.notifications.push(e),i.load()}).catch(function(e){console.log("error:",e)})}})},bind:function(){var e=this;$(".sitemap-container").each(function(i,s){$(s).nestable({maxDepth:100,group:i,callback:function(i,s){fetch(piranha.baseUrl+"manager/api/page/move",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify({id:$(s).attr("data-id"),items:$(i).nestable("serialize")})}).then(function(e){return e.json()}).then(function(i){piranha.notifications.push(i.status),"success"===i.status.type&&($(".sitemap-container").nestable("destroy"),e.sites=[],Vue.nextTick(function(){e.sites=i.sites,Vue.nextTick(function(){e.bind()})}))}).catch(function(e){console.log("error:",e)})}})})},add:function(e,i,s){var a=this;a.addSiteId=e,a.addPageId=i,a.addAfter=s,a.sites.forEach(function(i){i.id===e&&(a.addSiteTitle=i.title)}),$("#pageAddModal").modal("show")},selectSite:function(e){var i=this;i.addSiteId=e,i.sites.forEach(function(s){s.id===e&&(i.addSiteTitle=s.title)})},collapse:function(){for(var e=0;e<this.sites.length;e++)for(var i=0;i<this.sites[e].pages.length;i++)this.changeVisibility(this.sites[e].pages[i],!1)},expand:function(){for(var e=0;e<this.sites.length;e++)for(var i=0;i<this.sites[e].pages.length;i++)this.changeVisibility(this.sites[e].pages[i],!0)},changeVisibility:function(e,i){e.isExpanded=i;for(var s=0;s<e.items.length;s++)this.changeVisibility(e.items[s],i)}},created:function(){},updated:function(){this.updateBindings&&(this.bind(),this.updateBindings=!1),this.loading=!1}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.postedit.js+29 22 modified
    @@ -246,9 +246,7 @@ piranha.postedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -278,27 +276,32 @@ piranha.postedit = new Vue({
             revert: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/post/revert/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/post/revert", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    Vue.nextTick(function () {
    -                        $("#selectedCategory").select2({
    -                            tags: true,
    -                            selectOnClose: true,
    -                            placeholder: piranha.resources.texts.addCategory
    -                        });
    -                        $("#selectedTags").select2({
    -                            tags: true,
    -                            selectOnClose: false,
    -                            placeholder: piranha.resources.texts.addTags
    -                        });
    +                Vue.nextTick(function () {
    +                    $("#selectedCategory").select2({
    +                        tags: true,
    +                        selectOnClose: true,
    +                        placeholder: piranha.resources.texts.addCategory
    +                    });
    +                    $("#selectedTags").select2({
    +                        tags: true,
    +                        selectOnClose: false,
    +                        placeholder: piranha.resources.texts.addTags
                         });
    +                });
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
             },
             remove: function () {
    @@ -311,7 +314,11 @@ piranha.postedit = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/post/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/post/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/dist/js/piranha.postedit.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.postedit=new Vue({el:"#postedit",data:{loading:!0,id:null,blogId:null,typeId:null,title:null,slug:null,metaTitle:null,metaKeywords:null,metaDescription:null,metaIndex:null,metaFollow:null,metaPriority:null,ogTitle:null,ogDescription:null,ogImage:{id:null,media:null},excerpt:null,published:null,redirectUrl:null,redirectType:null,enableComments:null,closeCommentsAfterDays:null,commentCount:null,pendingCommentCount:0,state:"new",categories:[],tags:[],blocks:[],regions:[],editors:[],useBlocks:!0,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,permissions:[],primaryImage:{id:null,media:null},isScheduled:!1,selectedPermissions:[],saving:!1,savingDraft:!1,selectedRegion:{uid:"uid-blocks",name:null,icon:null},selectedSetting:"uid-settings",selectedCategory:null,selectedTags:[],selectedRoute:null,routes:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl(this.primaryImage.media.publicUrl):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},metaPriorityDescription:function(){var e=piranha.resources.texts.important;return this.metaPriority<=.3?e=piranha.resources.texts.low:this.metaPriority<=.6?e=piranha.resources.texts.medium:this.metaPriority<=.9&&(e=piranha.resources.texts.high),e+" ("+this.metaPriority+")"}},mounted(){document.addEventListener("keydown",this.doHotKeys)},beforeDestroy(){document.removeEventListener("keydown",this.doHotKeys)},methods:{bind:function(e){this.id=e.id,this.blogId=e.blogId,this.typeId=e.typeId,this.title=e.title,this.slug=e.slug,this.metaTitle=e.metaTitle,this.metaKeywords=e.metaKeywords,this.metaDescription=e.metaDescription,this.metaIndex=e.metaIndex,this.metaFollow=e.metaFollow,this.metaPriority=e.metaPriority,this.ogTitle=e.ogTitle,this.ogDescription=e.ogDescription,this.ogImage=e.ogImage,this.excerpt=e.excerpt,this.published=e.published,this.redirectUrl=e.redirectUrl,this.redirectType=e.redirectType,this.enableComments=e.enableComments,this.closeCommentsAfterDays=e.closeCommentsAfterDays,this.commentCount=e.commentCount,this.pendingCommentCount=e.pendingCommentCount,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.categories=e.categories,this.tags=e.tags,this.useBlocks=e.useBlocks,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.selectedCategory=e.selectedCategory,this.selectedTags=e.selectedTags,this.selectedRoute=e.selectedRoute,this.routes=e.routes,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.isScheduled=e.isScheduled,this.selectedPermissions=e.selectedPermissions,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/post/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/post/create/"+e+"/"+t).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.saveDraft())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save")},saveDraft:function(){this.savingDraft=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save/draft")},unpublish:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save/unpublish")},saveInternal:function(e){var t=this,i={id:t.id,blogId:t.blogId,typeId:t.typeId,primaryImage:{id:t.primaryImage.id},title:t.title,slug:t.slug,metaTitle:t.metaTitle,metaKeywords:t.metaKeywords,metaDescription:t.metaDescription,metaIndex:t.metaIndex,metaFollow:t.metaFollow,metaPriority:t.metaPriority,ogTitle:t.ogTitle,ogDescription:t.ogDescription,ogImage:{id:t.ogImage.id},excerpt:t.excerpt,published:t.published,redirectUrl:t.redirectUrl,redirectType:t.redirectType,enableComments:t.enableComments,closeCommentsAfterDays:t.closeCommentsAfterDays,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedCategory:t.selectedCategory,selectedTags:JSON.parse(JSON.stringify(t.selectedTags)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions};fetch(e,{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.slug=e.slug,t.published=e.published,t.state=e.state,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit post",piranha.baseUrl+"manager/post/edit/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.savingDraft=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},revert:function(){var e=this;fetch(piranha.baseUrl+"manager/api/post/revert/"+e.id).then(function(e){return e.json()}).then(function(t){e.bind(t),Vue.nextTick(function(){$("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags})}),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePostConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/post/delete/"+e.id).then(function(e){return e.json()}).then(function(t){piranha.notifications.push(t),window.location=piranha.baseUrl+"manager/page/edit/"+e.blogId}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){i.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e},selectSetting:function(e){this.selectedSetting=e},isCommentsOpen:function(){var e=new Date(this.published);return(e=e.addDays(this.closeCommentsAfterDays))>new Date},commentsClosedDate:function(){var e=new Date(this.published);return(e=e.addDays(this.closeCommentsAfterDays)).toDateString()},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){var e=this;this.loading?(sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(t){e.moveBlock(t.detail.origin.index,t.detail.destination.index)}),$("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedCategory").on("change",function(){var t=$(this).find("option:selected").text();e.selectedCategory=t}),$("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags}),$("#selectedTags").on("change",function(){var t=$(this).find("option:selected");e.selectedTags=[];for(var i=0;i<t.length;i++)e.selectedTags.push(t[i].text)}),piranha.editor.addInline("excerpt-body","excerpt-toolbar")):(sortable("#content-blocks","disable"),sortable("#content-blocks","enable")),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    +piranha.postedit=new Vue({el:"#postedit",data:{loading:!0,id:null,blogId:null,typeId:null,title:null,slug:null,metaTitle:null,metaKeywords:null,metaDescription:null,metaIndex:null,metaFollow:null,metaPriority:null,ogTitle:null,ogDescription:null,ogImage:{id:null,media:null},excerpt:null,published:null,redirectUrl:null,redirectType:null,enableComments:null,closeCommentsAfterDays:null,commentCount:null,pendingCommentCount:0,state:"new",categories:[],tags:[],blocks:[],regions:[],editors:[],useBlocks:!0,usePrimaryImage:!0,useExcerpt:!0,useHtmlExcerpt:!0,permissions:[],primaryImage:{id:null,media:null},isScheduled:!1,selectedPermissions:[],saving:!1,savingDraft:!1,selectedRegion:{uid:"uid-blocks",name:null,icon:null},selectedSetting:"uid-settings",selectedCategory:null,selectedTags:[],selectedRoute:null,routes:[]},computed:{contentRegions:function(){return this.regions.filter(function(e){return"setting"!=e.meta.display&&"hidden"!=e.meta.display})},settingRegions:function(){return this.regions.filter(function(e){return"setting"===e.meta.display})},primaryImageUrl:function(){return null!=this.primaryImage.media?piranha.utils.formatUrl(this.primaryImage.media.publicUrl):piranha.utils.formatUrl("~/manager/assets/img/empty-image.png")},isExcerptEmpty:function(){return piranha.utils.isEmptyText(this.excerpt)},metaPriorityDescription:function(){var e=piranha.resources.texts.important;return this.metaPriority<=.3?e=piranha.resources.texts.low:this.metaPriority<=.6?e=piranha.resources.texts.medium:this.metaPriority<=.9&&(e=piranha.resources.texts.high),e+" ("+this.metaPriority+")"}},mounted(){document.addEventListener("keydown",this.doHotKeys)},beforeDestroy(){document.removeEventListener("keydown",this.doHotKeys)},methods:{bind:function(e){this.id=e.id,this.blogId=e.blogId,this.typeId=e.typeId,this.title=e.title,this.slug=e.slug,this.metaTitle=e.metaTitle,this.metaKeywords=e.metaKeywords,this.metaDescription=e.metaDescription,this.metaIndex=e.metaIndex,this.metaFollow=e.metaFollow,this.metaPriority=e.metaPriority,this.ogTitle=e.ogTitle,this.ogDescription=e.ogDescription,this.ogImage=e.ogImage,this.excerpt=e.excerpt,this.published=e.published,this.redirectUrl=e.redirectUrl,this.redirectType=e.redirectType,this.enableComments=e.enableComments,this.closeCommentsAfterDays=e.closeCommentsAfterDays,this.commentCount=e.commentCount,this.pendingCommentCount=e.pendingCommentCount,this.state=e.state,this.blocks=e.blocks,this.regions=e.regions,this.editors=e.editors,this.categories=e.categories,this.tags=e.tags,this.useBlocks=e.useBlocks,this.usePrimaryImage=e.usePrimaryImage,this.useExcerpt=e.useExcerpt,this.useHtmlExcerpt=e.useHtmlExcerpt,this.selectedCategory=e.selectedCategory,this.selectedTags=e.selectedTags,this.selectedRoute=e.selectedRoute,this.routes=e.routes,this.permissions=e.permissions,this.primaryImage=e.primaryImage,this.isScheduled=e.isScheduled,this.selectedPermissions=e.selectedPermissions,this.useBlocks?this.selectedRegion={uid:"uid-blocks",name:null,icon:null}:this.editors.length>0?this.selectedRegion=this.editors[0]:this.contentRegions.length>0&&(this.selectedRegion=this.contentRegions[0].meta)},load:function(e){var t=this;fetch(piranha.baseUrl+"manager/api/post/"+e).then(function(e){return e.json()}).then(function(e){t.bind(e)}).catch(function(e){console.log("error:",e)})},create:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/post/create/"+e+"/"+t).then(function(e){return e.json()}).then(function(e){i.bind(e)}).catch(function(e){console.log("error:",e)})},doHotKeys(e){83===e.keyCode&&e.ctrlKey&&(e.preventDefault(),this.saveDraft())},save:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save")},saveDraft:function(){this.savingDraft=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save/draft")},unpublish:function(){this.saving=!0,this.saveInternal(piranha.baseUrl+"manager/api/post/save/unpublish")},saveInternal:function(e){var t=this,i={id:t.id,blogId:t.blogId,typeId:t.typeId,primaryImage:{id:t.primaryImage.id},title:t.title,slug:t.slug,metaTitle:t.metaTitle,metaKeywords:t.metaKeywords,metaDescription:t.metaDescription,metaIndex:t.metaIndex,metaFollow:t.metaFollow,metaPriority:t.metaPriority,ogTitle:t.ogTitle,ogDescription:t.ogDescription,ogImage:{id:t.ogImage.id},excerpt:t.excerpt,published:t.published,redirectUrl:t.redirectUrl,redirectType:t.redirectType,enableComments:t.enableComments,closeCommentsAfterDays:t.closeCommentsAfterDays,blocks:JSON.parse(JSON.stringify(t.blocks)),regions:JSON.parse(JSON.stringify(t.regions)),selectedCategory:t.selectedCategory,selectedTags:JSON.parse(JSON.stringify(t.selectedTags)),selectedRoute:t.selectedRoute,selectedPermissions:t.selectedPermissions};fetch(e,{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){var i=t.state;t.id=e.id,t.slug=e.slug,t.published=e.published,t.state=e.state,t.selectedRoute=e.selectedRoute,"new"===i&&"new"!==e.state&&window.history.replaceState({state:"created"},"Edit post",piranha.baseUrl+"manager/post/edit/"+e.id),piranha.notifications.push(e.status),t.saving=!1,t.savingDraft=!1,t.eventBus.$emit("onSaved",t.state)}).catch(function(e){console.log("error:",e)})},revert:function(){var e=this;fetch(piranha.baseUrl+"manager/api/post/revert",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){e.bind(t),Vue.nextTick(function(){$("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags})}),piranha.notifications.push(t.status)}).catch(function(e){console.log("error:",e)})},remove:function(){var e=this;piranha.alert.open({title:piranha.resources.texts.delete,body:piranha.resources.texts.deletePostConfirm,confirmCss:"btn-danger",confirmIcon:"fas fa-trash",confirmText:piranha.resources.texts.delete,onConfirm:function(){fetch(piranha.baseUrl+"manager/api/post/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){piranha.notifications.push(t),window.location=piranha.baseUrl+"manager/page/edit/"+e.blogId}).catch(function(e){console.log("error:",e)})}})},addBlock:function(e,t){var i=this;fetch(piranha.baseUrl+"manager/api/content/block/"+e).then(function(e){return e.json()}).then(function(e){i.blocks.splice(t,0,e.body)}).catch(function(e){console.log("error:",e)})},moveBlock:function(e,t){this.blocks.splice(t,0,this.blocks.splice(e,1)[0])},collapseBlock:function(e){e.meta.isCollapsed=!e.meta.isCollapsed},removeBlock:function(e){var t=this.blocks.indexOf(e);-1!==t&&this.blocks.splice(t,1)},updateBlockTitle:function(e){for(var t=0;t<this.blocks.length;t++)if(this.blocks[t].meta.uid===e.uid){this.blocks[t].meta.title=e.title;break}},selectRegion:function(e){this.selectedRegion=e},selectSetting:function(e){this.selectedSetting=e},isCommentsOpen:function(){var e=new Date(this.published);return(e=e.addDays(this.closeCommentsAfterDays))>new Date},commentsClosedDate:function(){var e=new Date(this.published);return(e=e.addDays(this.closeCommentsAfterDays)).toDateString()},selectPrimaryImage:function(){null!==this.primaryImage.media?piranha.mediapicker.open(this.updatePrimaryImage,"Image",this.primaryImage.media.folderId):piranha.mediapicker.openCurrentFolder(this.updatePrimaryImage,"Image")},removePrimaryImage:function(){this.primaryImage.id=null,this.primaryImage.media=null},updatePrimaryImage:function(e){"Image"===e.type?(this.primaryImage.id=e.id,this.primaryImage.media=e):console.log("No image was selected")},onExcerptBlur:function(e){this.useHtmlExcerpt?this.excerpt=tinyMCE.activeEditor.getContent():this.excerpt=e.target.innerHTML}},created:function(){},updated:function(){var e=this;this.loading?(sortable("#content-blocks",{handle:".handle",items:":not(.unsortable)"})[0].addEventListener("sortupdate",function(t){e.moveBlock(t.detail.origin.index,t.detail.destination.index)}),$("#selectedCategory").select2({tags:!0,selectOnClose:!0,placeholder:piranha.resources.texts.addCategory}),$("#selectedCategory").on("change",function(){var t=$(this).find("option:selected").text();e.selectedCategory=t}),$("#selectedTags").select2({tags:!0,selectOnClose:!1,placeholder:piranha.resources.texts.addTags}),$("#selectedTags").on("change",function(){var t=$(this).find("option:selected");e.selectedTags=[];for(var i=0;i<t.length;i++)e.selectedTags.push(t[i].text)}),piranha.editor.addInline("excerpt-body","excerpt-toolbar")):(sortable("#content-blocks","disable"),sortable("#content-blocks","enable")),this.loading=!1},components:{datepicker:vuejsDatepicker}});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/dist/js/piranha.siteedit.js+10 10 modified
    @@ -85,9 +85,7 @@ piranha.siteedit = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/site/save", {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -104,9 +102,7 @@ piranha.siteedit = new Vue({
     
                             fetch(piranha.baseUrl + "manager/api/site/savecontent", {
                                 method: "post",
    -                            headers: {
    -                                "Content-Type": "application/json",
    -                            },
    +                            headers: piranha.utils.antiForgeryHeaders(),
                                 body: JSON.stringify(content)
                             })
                             .then(function (contentResponse) { return contentResponse.json(); })
    @@ -121,7 +117,13 @@ piranha.siteedit = new Vue({
                                         self.callback = null;
                                     }
                                 } else {
    -                                piranha.notifications.push(contentResult);
    +                                if (result.status !== 400) {
    +                                    // Push status to notification hub
    +                                    piranha.notifications.push(contentResult);
    +                                } else {
    +                                    // Unauthorized request
    +                                    piranha.notifications.unauthorized();
    +                                }
                                 }
                             })
                             .catch(function (error) {
    @@ -216,9 +218,7 @@ piranha.siteedit = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/site/delete", {
                     method: "delete",
    -                headers: {
    -                    "Content-Type": "application/json"
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(self.id)
                 })
                 .then(function (response) { return response.json(); })
    
  • core/Piranha.Manager/assets/dist/js/piranha.siteedit.min.js+1 1 modified
    @@ -1 +1 @@
    -piranha.siteedit=new Vue({el:"#siteedit",data:{loading:!0,id:null,typeId:null,languageId:null,title:null,internalId:null,culture:null,description:null,logo:{id:null,media:null},hostnames:null,isDefault:!1,siteTypes:[],languages:[],regions:[],isNew:!1,isConfirm:!1,confirmTitle:null,selectedRegion:{uid:"uid-settings",name:null,icon:null},callback:null},methods:{load:function(e){self=this,fetch(piranha.baseUrl+"manager/api/site/"+e).then(function(e){return e.json()}).then(function(e){self.id=e.id,self.typeId=e.typeId,self.languageId=e.languageId,self.title=e.title,self.internalId=e.internalId,self.culture=e.culture,self.description=e.description,self.logo=e.logo,self.hostnames=e.hostnames,self.isDefault=e.isDefault,self.siteTypes=e.siteTypes,self.languages=e.languages}).catch(function(e){console.log("error:",e)}),fetch(piranha.baseUrl+"manager/api/site/content/"+e).then(function(e){return e.json()}).then(function(e){self.regions=e.regions}).catch(function(e){console.log("error:",e)})},save:function(){var e=document.getElementById("siteForm");if(!1!==e.checkValidity()){var t=this,i={id:this.id,typeId:this.typeId,languageId:this.languageId,title:this.title,internalId:this.internalId,culture:this.culture,description:this.description,logo:this.logo,hostnames:this.hostnames,isDefault:this.isDefault};fetch(piranha.baseUrl+"manager/api/site/save",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){if("success"===e.type)if(null!=t.id&&null!=t.typeId){var i={id:t.id,typeId:t.typeId,title:t.title,regions:JSON.parse(JSON.stringify(t.regions))};fetch(piranha.baseUrl+"manager/api/site/savecontent",{method:"post",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(i){"success"===i.type?(piranha.notifications.push(e),$("#siteedit").modal("hide"),t.callback&&(t.callback(),t.callback=null)):piranha.notifications.push(i)}).catch(function(e){console.log("error:",e)})}else $("#siteedit").modal("hide"),t.callback&&(t.callback(),t.callback=null)}).catch(function(e){console.log("error:",e)})}else e.classList.add("was-validated")},open:function(e,t){document.getElementById("siteForm").classList.remove("was-validated"),this.callback=t,this.isNew=!1,this.selectedRegion={uid:"uid-settings",name:null,icon:null},this.isConfirm=!1,this.confirmTitle=null,this.load(e),$("#siteedit").modal("show"),$("#sitetitle").focus()},create:function(e){var t=this;document.getElementById("siteForm").classList.remove("was-validated"),fetch(piranha.baseUrl+"manager/api/site/create").then(function(e){return e.json()}).then(function(i){t.id=i.id,t.typeId=i.typeId,t.title=i.title,t.internalId=i.internalId,t.culture=i.culture,t.description=i.description,t.hostnames=i.hostnames,t.isDefault=i.isDefault,t.siteTypes=i.siteTypes,t.languages=i.languages,t.isNew=!0,t.callback=e,t.selectedRegion={uid:"uid-settings",name:null,icon:null}}).catch(function(e){console.log("error:",e)}),$("#siteedit").modal("show"),$("#sitetitle").focus()},confirm:function(){this.isConfirm=!0},cancel:function(){this.isConfirm=!1,this.confirmTitle=null},remove:function(){this.isConfirm=!1,this.confirmTitle=null;var e=this;fetch(piranha.baseUrl+"manager/api/site/delete",{method:"delete",headers:{"Content-Type":"application/json"},body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){piranha.notifications.push(t),"success"===t.type&&($("#siteedit").modal("hide"),e.callback&&(e.callback(),e.callback=null))}).catch(function(e){console.log("error:",e)})},selectRegion:function(e){this.selectedRegion=e}},updated:function(){this.loading=!1}}),$("#siteedit").on("shown.bs.modal",function(){$("#sitetitle").trigger("focus")});
    \ No newline at end of file
    +piranha.siteedit=new Vue({el:"#siteedit",data:{loading:!0,id:null,typeId:null,languageId:null,title:null,internalId:null,culture:null,description:null,logo:{id:null,media:null},hostnames:null,isDefault:!1,siteTypes:[],languages:[],regions:[],isNew:!1,isConfirm:!1,confirmTitle:null,selectedRegion:{uid:"uid-settings",name:null,icon:null},callback:null},methods:{load:function(e){self=this,fetch(piranha.baseUrl+"manager/api/site/"+e).then(function(e){return e.json()}).then(function(e){self.id=e.id,self.typeId=e.typeId,self.languageId=e.languageId,self.title=e.title,self.internalId=e.internalId,self.culture=e.culture,self.description=e.description,self.logo=e.logo,self.hostnames=e.hostnames,self.isDefault=e.isDefault,self.siteTypes=e.siteTypes,self.languages=e.languages}).catch(function(e){console.log("error:",e)}),fetch(piranha.baseUrl+"manager/api/site/content/"+e).then(function(e){return e.json()}).then(function(e){self.regions=e.regions}).catch(function(e){console.log("error:",e)})},save:function(){var e=document.getElementById("siteForm");if(!1!==e.checkValidity()){var t=this,i={id:this.id,typeId:this.typeId,languageId:this.languageId,title:this.title,internalId:this.internalId,culture:this.culture,description:this.description,logo:this.logo,hostnames:this.hostnames,isDefault:this.isDefault};fetch(piranha.baseUrl+"manager/api/site/save",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(e){if("success"===e.type)if(null!=t.id&&null!=t.typeId){var i={id:t.id,typeId:t.typeId,title:t.title,regions:JSON.parse(JSON.stringify(t.regions))};fetch(piranha.baseUrl+"manager/api/site/savecontent",{method:"post",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(i)}).then(function(e){return e.json()}).then(function(i){"success"===i.type?(piranha.notifications.push(e),$("#siteedit").modal("hide"),t.callback&&(t.callback(),t.callback=null)):400!==e.status?piranha.notifications.push(i):piranha.notifications.unauthorized()}).catch(function(e){console.log("error:",e)})}else $("#siteedit").modal("hide"),t.callback&&(t.callback(),t.callback=null)}).catch(function(e){console.log("error:",e)})}else e.classList.add("was-validated")},open:function(e,t){document.getElementById("siteForm").classList.remove("was-validated"),this.callback=t,this.isNew=!1,this.selectedRegion={uid:"uid-settings",name:null,icon:null},this.isConfirm=!1,this.confirmTitle=null,this.load(e),$("#siteedit").modal("show"),$("#sitetitle").focus()},create:function(e){var t=this;document.getElementById("siteForm").classList.remove("was-validated"),fetch(piranha.baseUrl+"manager/api/site/create").then(function(e){return e.json()}).then(function(i){t.id=i.id,t.typeId=i.typeId,t.title=i.title,t.internalId=i.internalId,t.culture=i.culture,t.description=i.description,t.hostnames=i.hostnames,t.isDefault=i.isDefault,t.siteTypes=i.siteTypes,t.languages=i.languages,t.isNew=!0,t.callback=e,t.selectedRegion={uid:"uid-settings",name:null,icon:null}}).catch(function(e){console.log("error:",e)}),$("#siteedit").modal("show"),$("#sitetitle").focus()},confirm:function(){this.isConfirm=!0},cancel:function(){this.isConfirm=!1,this.confirmTitle=null},remove:function(){this.isConfirm=!1,this.confirmTitle=null;var e=this;fetch(piranha.baseUrl+"manager/api/site/delete",{method:"delete",headers:piranha.utils.antiForgeryHeaders(),body:JSON.stringify(e.id)}).then(function(e){return e.json()}).then(function(t){piranha.notifications.push(t),"success"===t.type&&($("#siteedit").modal("hide"),e.callback&&(e.callback(),e.callback=null))}).catch(function(e){console.log("error:",e)})},selectRegion:function(e){this.selectedRegion=e}},updated:function(){this.loading=!1}}),$("#siteedit").on("shown.bs.modal",function(){$("#sitetitle").trigger("focus")});
    \ No newline at end of file
    
  • core/Piranha.Manager/assets/src/js/components/post-archive.vue+5 1 modified
    @@ -113,7 +113,11 @@ export default {
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/post/delete/" + postId)
    +                    fetch(piranha.baseUrl + "manager/api/post/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(postId)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/src/js/piranha.alias.js+43 36 modified
    @@ -44,43 +44,45 @@
                 }
     
                 fetch(piranha.baseUrl + "manager/api/alias/save", {
    -                    method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    -                    body: JSON.stringify({
    -                        id: piranha.alias.model.id,
    -                        siteId: piranha.alias.siteId,
    -                        aliasUrl: piranha.alias.model.aliasUrl,
    -                        redirectUrl: piranha.alias.model.redirectUrl,
    -                        isPermanent: piranha.alias.model.isPermanent != null ? piranha.alias.model.isPermanent : false
    -                    })
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: piranha.alias.model.id,
    +                    siteId: piranha.alias.siteId,
    +                    aliasUrl: piranha.alias.model.aliasUrl,
    +                    redirectUrl: piranha.alias.model.redirectUrl,
    +                    isPermanent: piranha.alias.model.isPermanent != null ? piranha.alias.model.isPermanent : false
                     })
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status.type === "success")
    -                    {
    -                        // Remove validation class
    -                        form.classList.remove("was-validated");
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status.type === "success") {
    +                    // Remove validation class
    +                    form.classList.remove("was-validated");
     
    -                        // Close modal
    -                        $("#aliasModal").modal("hide");
    +                    // Close modal
    +                    $("#aliasModal").modal("hide");
     
    -                        // Clear modal
    -                        piranha.alias.model.id = null;
    -                        piranha.alias.model.aliasUrl = null;
    -                        piranha.alias.model.redirectUrl = null;
    -                        piranha.alias.model.isPermanent = true;
    +                    // Clear modal
    +                    piranha.alias.model.id = null;
    +                    piranha.alias.model.aliasUrl = null;
    +                    piranha.alias.model.redirectUrl = null;
    +                    piranha.alias.model.isPermanent = true;
     
    -                        piranha.alias.items = result.items;
    -                    }
    +                    piranha.alias.items = result.items;
    +                }
     
    +                if (result.status !== 400) {
                         // Push status to notification hub
                         piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) {
    -                    console.log("error:", error);
    -                });
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
    +            })
    +            .catch(function (error) {
    +                console.log("error:", error);
    +            });
             },
             remove: function (id) {
                 var self = this;
    @@ -94,17 +96,22 @@
                     onConfirm: function () {
                         fetch(piranha.baseUrl + "manager/api/alias/delete", {
                             method: "delete",
    -                        headers: {
    -                            "Content-Type": "application/json"
    -                        },
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(id)
                         })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
    -                        self.items = result.items;
    +                        if (result.status.type === "success") {
    +                            self.items = result.items;
    +                        }
     
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    +                        if (result.status !== 400) {
    +                            // Push status to notification hub
    +                            piranha.notifications.push(result.status);
    +                        } else {
    +                            // Unauthorized request
    +                            piranha.notifications.unauthorized();
    +                        }
                         })
                         .catch(function (error) { console.log("error:", error ); });
                     }
    
  • core/Piranha.Manager/assets/src/js/piranha.comment.js+53 29 modified
    @@ -44,32 +44,50 @@ piranha.comment = new Vue({
             approve: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/approve/" + id + (self.contentId != null ? "/" + self.contentId : ""))
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status) {
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    -                    }
    -                    self.contentId = result.contentId;
    -                    self.items = result.comments;
    +            fetch(piranha.baseUrl + "manager/api/comment/approve", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: id,
    +                    parentId: self.contentId 
                     })
    -                .catch(function (error) { console.log("error:", error ); });
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                }
    +                self.contentId = result.contentId;
    +                self.items = result.comments;
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             unapprove: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/unapprove/" + id + (self.contentId != null ? "/" + self.contentId : ""))
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    if (result.status) {
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result.status);
    -                    }
    -                    self.contentId = result.contentId;
    -                    self.items = result.comments;
    +            fetch(piranha.baseUrl + "manager/api/comment/unapprove", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify({
    +                    id: id,
    +                    parentId: self.contentId 
                     })
    -                .catch(function (error) { console.log("error:", error ); });
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                if (result.status) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                }
    +                self.contentId = result.contentId;
    +                self.items = result.comments;
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             toggleApproved: function (item) {
                 item.isApproved = !item.isApproved;
    @@ -83,16 +101,22 @@ piranha.comment = new Vue({
             remove: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/comment/delete/" + id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result);
    +            fetch(piranha.baseUrl + "manager/api/comment/delete", {
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                // Push status to notification hub
    +                piranha.notifications.push(result);
     
    -                    // Refresh the list
    -                    self.load(self.contentId);
    -                })
    -                .catch(function (error) { console.log("error:", error ); });
    +                // Refresh the list
    +                self.load(self.contentId);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error ); 
    +            });
             },
             setStatus: function (status) {
                 this.state = status;
    
  • core/Piranha.Manager/assets/src/js/piranha.config.js+8 5 modified
    @@ -56,9 +56,7 @@ piranha.config = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/config/save", {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             hierarchicalPageSlugs: self.model.hierarchicalPageSlugs,
                             expandedSitemapLevels: self.model.expandedSitemapLevels,
    @@ -80,8 +78,13 @@ piranha.config = new Vue({
                     })
                     .then(function (response) { return response.json(); })
                     .then(function (result) {
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result.status);
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    
  • core/Piranha.Manager/assets/src/js/piranha.contentedit.js+6 4 modified
    @@ -195,9 +195,7 @@ piranha.contentedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -233,7 +231,11 @@ piranha.contentedit = new Vue({
                     onConfirm: function () {
                         var groupId = self.groupId;
     
    -                    fetch(piranha.baseUrl + "manager/api/content/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/content/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/src/js/piranha.contentlist.js+5 1 modified
    @@ -49,7 +49,11 @@ piranha.contentlist = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/content/delete/" + id)
    +                    fetch(piranha.baseUrl + "manager/api/content/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/src/js/piranha.dropzone.js+1 0 modified
    @@ -14,6 +14,7 @@ piranha.dropzone = new function () {
             var defaultOptions = {
                 paramName: 'Uploads',
                 url: piranha.baseUrl + "manager/api/media/upload",
    +            headers: piranha.utils.antiForgeryHeaders(false),
                 thumbnailWidth: 70,
                 thumbnailHeight: 70,
                 previewsContainer: selector + " .media-list",
    
  • core/Piranha.Manager/assets/src/js/piranha.languageedit.js+23 17 modified
    @@ -52,22 +52,25 @@ piranha.languageedit = new Vue({
                     self.loading = true;
                     fetch(piranha.baseUrl + "manager/api/language", {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             items: JSON.parse(JSON.stringify(self.items))
                         })
                     })
                     .then(function (response) { return response.json(); })
                     .then(function (result) {
    -                    //if (result.status.type === "success")
    -                    //{
    +                    if (result.status.type === "success") {
                             self.bind(result);
    -                    //}
    -
    -                    // Push status to notification hub
    -                    // piranha.notifications.push(result.status);
    +                    }
    +                    
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                        self.loading = false;
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    @@ -80,20 +83,23 @@ piranha.languageedit = new Vue({
                 self.loading = true;
                 fetch(piranha.baseUrl + "manager/api/language/" + item.id, {
                     method: "delete",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(item)
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
    -                //if (result.status.type === "success")
    -                //{
    +                if (result.status.type === "success") {
                         self.bind(result);
    -                //}
    +                }
     
    -                // Push status to notification hub
    -                // piranha.notifications.push(result.status);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                    self.loading = false;
    +                }
                 })
                 .catch(function (error) {
                     console.log("error:", error);
    
  • core/Piranha.Manager/assets/src/js/piranha.media.js+52 31 modified
    @@ -115,18 +115,22 @@ piranha.media = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/media/move/" + (folderId || ""), {
                     method: "POST",
    -                headers: {
    -                    'Content-Type': 'application/json'
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(selections)
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
                     if (result.type === "success") {
                         piranha.media.refresh();
                     }
    -                // Push status to notification hub
    -                piranha.notifications.push(result);
    +
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) { console.log("error:", error); });
             },
    @@ -166,7 +170,6 @@ piranha.media = new Vue({
                 piranha.media.load(piranha.media.currentFolderId);
             },
             addFolder: function () {
    -            //this.saveFolder("#mediaFolderModal", "mediaFolderForm", {
                 this.saveFolder(null, null, {
                     parentId: this.currentFolderId,
                     name: this.folder.name
    @@ -193,9 +196,7 @@ piranha.media = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/media/folder/save", {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(folder)
                 })
                 .then(function (response) { return response.json(); })
    @@ -215,8 +216,13 @@ piranha.media = new Vue({
                         self.refresh();
                     }
     
    -                // Push status to notification hub
    -                piranha.notifications.push(result.status);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) {
                     console.log("error:", error);
    @@ -226,19 +232,22 @@ piranha.media = new Vue({
                 var self = this;
     
                 fetch(piranha.baseUrl + "manager/api/media/delete", {
    -                method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify([id])
                 })
                 .then(function (response) { return response.json(); })
                 .then(function (result) {
                     // Refresh
                     self.refresh();
     
    -                // Push status to notification hub
    -                piranha.notifications.push(result);
    +                if (result.status !== 400) {
    +                    // Push status to notification hub
    +                    piranha.notifications.push(result.status);
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
                 })
                 .catch(function (error) { console.log("error:", error ); });
             },
    @@ -254,19 +263,22 @@ piranha.media = new Vue({
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
                         fetch(piranha.baseUrl + "manager/api/media/delete", {
    -                        method: "post",
    -                        headers: {
    -                            "Content-Type": "application/json",
    -                        },
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(selections)
                         })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             // Refresh
                             self.refresh();
     
    -                        // Push status to notification hub
    -                        piranha.notifications.push(result);
    +                        if (result.status !== 400) {
    +                            // Push status to notification hub
    +                            piranha.notifications.push(result.status);
    +                        } else {
    +                            // Unauthorized request
    +                            piranha.notifications.unauthorized();
    +                        }
                         })
                         .catch(function (error) { console.log("error:", error); });
                     }
    @@ -275,18 +287,27 @@ piranha.media = new Vue({
             removeFolder: function (id) {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/media/folder/delete/" + id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/media/folder/delete", {
    +                method: "delete",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    history.pushState({ folderId: id }, "", piranha.baseUrl + "manager/media" + (id ? "/" + id : ""));
    -                    document.title = result.currentFolderName ? result.currentFolderName : "Media";
    +                history.pushState({ folderId: id }, "", piranha.baseUrl + "manager/media" + (id ? "/" + id : ""));
    +                document.title = result.currentFolderName ? result.currentFolderName : "Media";
     
    +                if (result.status !== 400) {
                         // Push status to notification hub
                         piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error ); });
    +                } else {
    +                    // Unauthorized request
    +                    piranha.notifications.unauthorized();
    +                }
    +            })
    +            .catch(function (error) { console.log("error:", error ); });
             }
         },
         computed: {
    
  • core/Piranha.Manager/assets/src/js/piranha.mediapicker.js+8 5 modified
    @@ -148,9 +148,7 @@ piranha.mediapicker = new Vue({
                 if (self.folderName !== "") {
                     fetch(piranha.baseUrl + "manager/api/media/folder/save" + (self.filter ? "?filter=" + self.filter : ""), {
                         method: "post",
    -                    headers: {
    -                        "Content-Type": "application/json",
    -                    },
    +                    headers: piranha.utils.antiForgeryHeaders(),
                         body: JSON.stringify({
                             parentId: self.currentFolderId,
                             name: self.folderName
    @@ -167,8 +165,13 @@ piranha.mediapicker = new Vue({
                             self.items = result.media;
                         }
     
    -                    // Push status to notification hub
    -                    piranha.notifications.push(result.status);
    +                    if (result.status !== 400) {
    +                        // Push status to notification hub
    +                        piranha.notifications.push(result.status);
    +                    } else {
    +                        // Unauthorized request
    +                        piranha.notifications.unauthorized();
    +                    }
                     })
                     .catch(function (error) {
                         console.log("error:", error);
    
  • core/Piranha.Manager/assets/src/js/piranha.notifications.js+7 0 modified
    @@ -8,6 +8,13 @@ piranha.notifications = new Vue({
             items: [],
         },
         methods: {
    +        unauthorized: function() {
    +            this.push({
    +                type: "danger",
    +                body: "Request sender could not be verified by the server.",
    +                hide: true
    +            });
    +        },
             push: function (notification) {
     
                 notification.style = {
    
  • core/Piranha.Manager/assets/src/js/piranha.pageedit.js+30 19 modified
    @@ -280,9 +280,7 @@ piranha.pageedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -314,29 +312,38 @@ piranha.pageedit = new Vue({
             revert: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/page/revert/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/page/revert", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
             },
             detach: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/page/detach/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/page/detach", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
    -
             },
             remove: function () {
                 var self = this;
    @@ -348,7 +355,11 @@ piranha.pageedit = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/page/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/page/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/src/js/piranha.pagelist.js+6 4 modified
    @@ -39,7 +39,11 @@ piranha.pagelist = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/page/delete/" + id)
    +                    fetch(piranha.baseUrl + "manager/api/page/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    @@ -60,9 +64,7 @@ piranha.pagelist = new Vue({
                         callback: function (l, e) {
                             fetch(piranha.baseUrl + "manager/api/page/move", {
                                 method: "post",
    -                            headers: {
    -                                "Content-Type": "application/json"
    -                            },
    +                            headers: piranha.utils.antiForgeryHeaders(),
                                 body: JSON.stringify({
                                     id: $(e).attr("data-id"),
                                     items: $(l).nestable("serialize")
    
  • core/Piranha.Manager/assets/src/js/piranha.postedit.js+29 22 modified
    @@ -246,9 +246,7 @@ piranha.postedit = new Vue({
     
                 fetch(route, {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -278,27 +276,32 @@ piranha.postedit = new Vue({
             revert: function () {
                 var self = this;
     
    -            fetch(piranha.baseUrl + "manager/api/post/revert/" + self.id)
    -                .then(function (response) { return response.json(); })
    -                .then(function (result) {
    -                    self.bind(result);
    +            fetch(piranha.baseUrl + "manager/api/post/revert", {
    +                method: "post",
    +                headers: piranha.utils.antiForgeryHeaders(),
    +                body: JSON.stringify(self.id)
    +            })
    +            .then(function (response) { return response.json(); })
    +            .then(function (result) {
    +                self.bind(result);
     
    -                    Vue.nextTick(function () {
    -                        $("#selectedCategory").select2({
    -                            tags: true,
    -                            selectOnClose: true,
    -                            placeholder: piranha.resources.texts.addCategory
    -                        });
    -                        $("#selectedTags").select2({
    -                            tags: true,
    -                            selectOnClose: false,
    -                            placeholder: piranha.resources.texts.addTags
    -                        });
    +                Vue.nextTick(function () {
    +                    $("#selectedCategory").select2({
    +                        tags: true,
    +                        selectOnClose: true,
    +                        placeholder: piranha.resources.texts.addCategory
    +                    });
    +                    $("#selectedTags").select2({
    +                        tags: true,
    +                        selectOnClose: false,
    +                        placeholder: piranha.resources.texts.addTags
                         });
    +                });
     
    -                    piranha.notifications.push(result.status);
    -                })
    -                .catch(function (error) { console.log("error:", error );
    +                piranha.notifications.push(result.status);
    +            })
    +            .catch(function (error) { 
    +                console.log("error:", error );
                 });
             },
             remove: function () {
    @@ -311,7 +314,11 @@ piranha.postedit = new Vue({
                     confirmIcon: "fas fa-trash",
                     confirmText: piranha.resources.texts.delete,
                     onConfirm: function () {
    -                    fetch(piranha.baseUrl + "manager/api/post/delete/" + self.id)
    +                    fetch(piranha.baseUrl + "manager/api/post/delete", {
    +                        method: "delete",
    +                        headers: piranha.utils.antiForgeryHeaders(),
    +                        body: JSON.stringify(self.id)
    +                    })
                         .then(function (response) { return response.json(); })
                         .then(function (result) {
                             piranha.notifications.push(result);
    
  • core/Piranha.Manager/assets/src/js/piranha.siteedit.js+10 10 modified
    @@ -85,9 +85,7 @@ piranha.siteedit = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/site/save", {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json",
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(model)
                 })
                 .then(function (response) { return response.json(); })
    @@ -104,9 +102,7 @@ piranha.siteedit = new Vue({
     
                             fetch(piranha.baseUrl + "manager/api/site/savecontent", {
                                 method: "post",
    -                            headers: {
    -                                "Content-Type": "application/json",
    -                            },
    +                            headers: piranha.utils.antiForgeryHeaders(),
                                 body: JSON.stringify(content)
                             })
                             .then(function (contentResponse) { return contentResponse.json(); })
    @@ -121,7 +117,13 @@ piranha.siteedit = new Vue({
                                         self.callback = null;
                                     }
                                 } else {
    -                                piranha.notifications.push(contentResult);
    +                                if (result.status !== 400) {
    +                                    // Push status to notification hub
    +                                    piranha.notifications.push(contentResult);
    +                                } else {
    +                                    // Unauthorized request
    +                                    piranha.notifications.unauthorized();
    +                                }
                                 }
                             })
                             .catch(function (error) {
    @@ -216,9 +218,7 @@ piranha.siteedit = new Vue({
     
                 fetch(piranha.baseUrl + "manager/api/site/delete", {
                     method: "delete",
    -                headers: {
    -                    "Content-Type": "application/json"
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(self.id)
                 })
                 .then(function (response) { return response.json(); })
    
  • core/Piranha.Manager/assets/src/js/piranha.utils.js+22 1 modified
    @@ -14,7 +14,28 @@ piranha.utils = {
         },
         strLength: function (str) {
             return str != null ? str.length : 0;
    -    }
    +    },
    +    antiForgery: function () {
    +        const cookies = document.cookie.split(";");
    +        for (let i = 0; i < cookies.length; i++) {
    +            let c = cookies[i].trim().split("=");
    +            if (c[0] === piranha.antiForgery.cookieName) {
    +                return c[1];
    +            }
    +        }
    +        return "";
    +    },
    +    antiForgeryHeaders: function (isJson) {
    +        var headers = {};
    +
    +        if (isJson === undefined || isJson === true)
    +        {
    +            headers["Content-Type"] = "application/json";
    +        }
    +        headers[piranha.antiForgery.headerName] = piranha.utils.antiForgery();
    +
    +        return headers;
    +    }    
     };
     
     Date.prototype.addDays = function(days) {
    
  • core/Piranha.Manager/assets/src/scss/inc/_actions.scss+3 1 modified
    @@ -3,7 +3,9 @@
     .actions {
         white-space: nowrap;
     
    -    a {
    +    a, button {
    +        background-color: transparent;
    +        border: none;
             display: inline-block;
             width: 1.5rem;
             height: 1.5rem;
    
  • core/Piranha.Manager/assets/src/scss/inc/_notifications.scss+1 1 modified
    @@ -4,7 +4,7 @@
         position: fixed;
         left: 4rem;
         bottom: 0;
    -    z-index: 2000; // TODO: verify z-index later
    +    z-index: 20000; // TODO: verify z-index later
     
         .notification {
             width: 300px;
    
  • core/Piranha.Manager/Controllers/AliasApiController.cs+1 0 modified
    @@ -25,6 +25,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/alias")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class AliasApiController : Controller
         {
             private readonly IApi _api;
    
  • core/Piranha.Manager/Controllers/AuthController.cs+55 0 added
    @@ -0,0 +1,55 @@
    +/*
    + * Copyright (c) .NET Foundation and Contributors
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + *
    + * https://github.com/piranhacms/piranha.core
    + *
    + */
    +
    +using Microsoft.AspNetCore.Antiforgery;
    +using Microsoft.AspNetCore.Authorization;
    +using Microsoft.AspNetCore.Http;
    +using Microsoft.AspNetCore.Mvc;
    +using Microsoft.Extensions.Options;
    +
    +namespace Piranha.Manager.Controllers
    +{
    +    [Area("Manager")]
    +    [Route("manager/login/auth")]
    +    [Authorize(Policy = Permission.Admin)]
    +    [ApiController]
    +    public sealed class AuthController : Controller
    +    {
    +        private readonly IAntiforgery _antiForgery;
    +        private readonly ManagerOptions _options;
    +
    +        /// <summary>
    +        /// Default constructor.
    +        /// </summary>
    +        /// <param name="antiforgery">The antiforgery service</param>
    +        /// <param name="options">The manager options</param>
    +        public AuthController(IAntiforgery antiforgery, IOptions<ManagerOptions> options)
    +        {
    +            _antiForgery = antiforgery;
    +            _options = options.Value;
    +        }
    +
    +        [Route("{returnUrl?}")]
    +        [HttpGet]
    +        public IActionResult SetAuthCookie(string returnUrl = null)
    +        {
    +            var tokens = _antiForgery.GetAndStoreTokens(HttpContext);
    +            Response.Cookies.Append(_options.XsrfCookieName, tokens.RequestToken, new CookieOptions
    +            {
    +                HttpOnly = false
    +            });    
    +            if (!string.IsNullOrEmpty(returnUrl))
    +            {
    +                return LocalRedirect(returnUrl);
    +            }        
    +            return LocalRedirect("~/manager");
    +        }
    +    }
    +}
    \ No newline at end of file
    
  • core/Piranha.Manager/Controllers/CommentApiController.cs+21 14 modified
    @@ -24,8 +24,15 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/comment")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class CommentApiController : Controller
         {
    +        public class ApprovalModel
    +        {
    +            public Guid Id { get; set; }
    +            public Guid? ParentId { get; set; }
    +        }
    +
             private readonly CommentService _service;
             private readonly ManagerLocalizer _localizer;
     
    @@ -50,14 +57,14 @@ public Task<CommentListModel> List(Guid? id = null)
                 return _service.Get(id);
             }
     
    -        [Route("approve/{id}/{parentId?}")]
    -        [HttpGet]
    +        [Route("approve")]
    +        [HttpPost]
             [Authorize(Policy = Permission.CommentsApprove)]
    -        public async Task<CommentListModel> Approve(Guid id, Guid? parentId = null)
    +        public async Task<CommentListModel> Approve(ApprovalModel model)
             {
    -            await _service.ApproveAsync(id);
    +            await _service.ApproveAsync(model.Id).ConfigureAwait(false);
     
    -            var result = await List(parentId);
    +            var result = await List(model.ParentId).ConfigureAwait(false);
     
                 result.Status = new StatusMessage
                 {
    @@ -67,14 +74,14 @@ public async Task<CommentListModel> Approve(Guid id, Guid? parentId = null)
                 return result;
             }
     
    -        [Route("unapprove/{id}/{parentId?}")]
    -        [HttpGet]
    +        [Route("unapprove")]
    +        [HttpPost]
             [Authorize(Policy = Permission.CommentsApprove)]
    -        public async Task<CommentListModel> UnApprove(Guid id, Guid? parentId = null)
    +        public async Task<CommentListModel> UnApprove(ApprovalModel model)
             {
    -            await _service.UnApproveAsync(id);
    +            await _service.UnApproveAsync(model.Id).ConfigureAwait(false);
     
    -            var result = await List(parentId);
    +            var result = await List(model.ParentId).ConfigureAwait(false);
     
                 result.Status = new StatusMessage
                 {
    @@ -84,12 +91,12 @@ public async Task<CommentListModel> UnApprove(Guid id, Guid? parentId = null)
                 return result;
             }
     
    -        [Route("delete/{id}")]
    -        [HttpGet]
    +        [Route("delete")]
    +        [HttpDelete]
             [Authorize(Policy = Permission.CommentsDelete)]
    -        public async Task<StatusMessage> Delete(Guid id)
    +        public async Task<StatusMessage> Delete([FromBody]Guid id)
             {
    -            await _service.DeleteAsync(id);
    +            await _service.DeleteAsync(id).ConfigureAwait(false);
     
                 var result = new StatusMessage
                 {
    
  • core/Piranha.Manager/Controllers/ConfigApiController.cs+1 0 modified
    @@ -22,6 +22,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/config")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class ConfigApiController : Controller
         {
             private readonly ConfigService _service;
    
  • core/Piranha.Manager/Controllers/ContentApiController.cs+4 3 modified
    @@ -25,6 +25,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/content")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class ContentApiController : Controller
         {
             private readonly IApi _api;
    @@ -228,10 +229,10 @@ public async Task<ContentEditModel> Save(ContentEditModel model)
             /// </summary>
             /// <param name="id">The unique id</param>
             /// <returns>The result of the operation</returns>
    -        [Route("delete/{id}")]
    -        [HttpGet]
    +        [Route("delete")]
    +        [HttpDelete]
             [Authorize(Policy = Permission.ContentDelete)]
    -        public async Task<StatusMessage> Delete(Guid id)
    +        public async Task<StatusMessage> Delete([FromBody]Guid id)
             {
                 try
                 {
    
  • core/Piranha.Manager/Controllers/LanguageApiController.cs+51 5 modified
    @@ -14,7 +14,6 @@
     using Microsoft.AspNetCore.Mvc;
     using Piranha.Manager.Models;
     using Piranha.Manager.Services;
    -using Piranha.Models;
     
     namespace Piranha.Manager.Controllers
     {
    @@ -25,6 +24,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/language")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class LanguageApiController : Controller
         {
             private readonly LanguageService _service;
    @@ -56,16 +56,62 @@ public async Task<LanguageEditModel> Get()
             /// <param name="model">The model</param>
             [Route("")]
             [HttpPost]
    -        public async Task<LanguageEditModel> Save(LanguageEditModel model)
    +        public async Task<IActionResult> Save(LanguageEditModel model)
             {
    -            return await _service.Save(model);
    +            try 
    +            {
    +                var result = await _service.Save(model);
    +
    +                result.Status = new StatusMessage
    +                {
    +                    Type = StatusMessage.Success,
    +                    Body = _localizer.Language["The language was successfully saved"]
    +                };
    +
    +                return Ok(result);                
    +            }
    +            catch (Exception e)
    +            {
    +                var result = new LanguageEditModel();
    +                result.Status = new StatusMessage
    +                {
    +                    Type = StatusMessage.Error,
    +                    Body = e.Message
    +                };   
    +                return BadRequest(result);             
    +            }
             }
     
    +        /// <summary>
    +        /// Deletes the language with the given id.
    +        /// </summary>
    +        /// <param name="id">The unique id</param>
             [Route("{id}")]
             [HttpDelete]
    -        public async Task<LanguageEditModel> Delete(Guid id)
    +        public async Task<IActionResult> Delete(Guid id)
             {
    -            return await _service.Delete(id);
    +            try 
    +            {
    +                var result = await _service.Delete(id);
    +
    +                result.Status = new StatusMessage
    +                {
    +                    Type = StatusMessage.Success,
    +                    Body = _localizer.Language["The language was successfully deleted"]
    +                };
    +
    +                return Ok(result);              
    +            }
    +            catch (Exception e)
    +            {
    +                var result = new LanguageEditModel();
    +                result.Status = new StatusMessage
    +                {
    +                    Type = StatusMessage.Error,
    +                    Body = e.Message
    +                };   
    +                return BadRequest(result);             
    +            }
             }
         }
     }
    \ No newline at end of file
    
  • core/Piranha.Manager/Controllers/MediaApiController.cs+5 4 modified
    @@ -28,6 +28,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/media")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class MediaApiController : Controller
         {
             private readonly MediaService _service;
    @@ -153,10 +154,10 @@ public async Task<IActionResult> SaveFolder(MediaFolderModel model, MediaType? f
                 }
             }
     
    -        [Route("folder/delete/{id:Guid}")]
    -        [HttpGet]
    +        [Route("folder/delete")]
    +        [HttpDelete]
             [Authorize(Policy = Permission.MediaDeleteFolder)]
    -        public async Task<IActionResult> DeleteFolder(Guid id)
    +        public async Task<IActionResult> DeleteFolder([FromBody]Guid id)
             {
                 try
                 {
    @@ -299,7 +300,7 @@ public async Task<IActionResult> Move([FromBody] IEnumerable<Guid> items, Guid?
             }
     
             [Route("delete")]
    -        [HttpPost]
    +        [HttpDelete]
             [Consumes("application/json")]
             [Authorize(Policy = Permission.MediaDelete)]
             public async Task<IActionResult> Delete([FromBody] IEnumerable<Guid> items)
    
  • core/Piranha.Manager/Controllers/ModuleApiController.cs+1 0 modified
    @@ -23,6 +23,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/module")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class ModuleApiController : Controller
         {
             private readonly ModuleService _service;
    
  • core/Piranha.Manager/Controllers/PageApiController.cs+10 9 modified
    @@ -26,6 +26,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/page")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class PageApiController : Controller
         {
             private readonly PageService _service;
    @@ -151,10 +152,10 @@ public async Task<PageEditModel> CopyRelative(Guid sourceId, Guid pageId, bool a
             /// </summary>
             /// <param name="pageId">The page id</param>
             /// <returns>The page edit model</returns>
    -        [Route("detach/{pageId}")]
    -        [HttpGet]
    +        [Route("detach")]
    +        [HttpPost]
             [Authorize(Policy = Permission.PagesEdit)]
    -        public async Task<PageEditModel> Detach(Guid pageId)
    +        public async Task<PageEditModel> Detach([FromBody]Guid pageId)
             {
                 var model = await _service.Detach(pageId);
     
    @@ -227,10 +228,10 @@ public async Task<PageEditModel> SaveUnpublish(PageEditModel model)
                 return ret;
             }
     
    -        [Route("revert/{id}")]
    -        [HttpGet]
    +        [Route("revert")]
    +        [HttpPost]
             [Authorize(Policy = Permission.PagesSave)]
    -        public async Task<PageEditModel> Revert(Guid id)
    +        public async Task<PageEditModel> Revert([FromBody]Guid id)
             {
                 var page = await _service.GetById(id, false);
     
    @@ -257,10 +258,10 @@ public async Task<PageEditModel> Revert(Guid id)
             /// </summary>
             /// <param name="id">The unique id</param>
             /// <returns>The result of the operation</returns>
    -        [Route("delete/{id}")]
    -        [HttpGet]
    +        [Route("delete")]
    +        [HttpDelete]
             [Authorize(Policy = Permission.PagesDelete)]
    -        public async Task<StatusMessage> Delete(Guid id)
    +        public async Task<StatusMessage> Delete([FromBody]Guid id)
             {
                 try
                 {
    
  • core/Piranha.Manager/Controllers/PermissionApiController.cs+1 0 modified
    @@ -22,6 +22,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/permissions")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class PermissionApiController : Controller
         {
             private readonly IAuthorizationService _auth;
    
  • core/Piranha.Manager/Controllers/PostApiController.cs+7 6 modified
    @@ -26,6 +26,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/post")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class PostApiController : Controller
         {
             private readonly PostService _service;
    @@ -162,10 +163,10 @@ public async Task<PostEditModel> SaveUnpublish(PostEditModel model)
                 return ret;
             }
     
    -        [Route("revert/{id}")]
    -        [HttpGet]
    +        [Route("revert")]
    +        [HttpPost]
             [Authorize(Policy = Permission.PostsSave)]
    -        public async Task<PostEditModel> Revert(Guid id)
    +        public async Task<PostEditModel> Revert([FromBody]Guid id)
             {
                 var post = await _service.GetById(id, false);
     
    @@ -193,10 +194,10 @@ public async Task<PostEditModel> Revert(Guid id)
             /// </summary>
             /// <param name="id">The unique id</param>
             /// <returns>The result of the operation</returns>
    -        [Route("delete/{id}")]
    -        [HttpGet]
    +        [Route("delete")]
    +        [HttpDelete]
             [Authorize(Policy = Permission.PostsDelete)]
    -        public async Task<StatusMessage> Delete(Guid id)
    +        public async Task<StatusMessage> Delete([FromBody]Guid id)
             {
                 try
                 {
    
  • core/Piranha.Manager/Controllers/SiteApiController.cs+1 0 modified
    @@ -25,6 +25,7 @@ namespace Piranha.Manager.Controllers
         [Route("manager/api/site")]
         [Authorize(Policy = Permission.Admin)]
         [ApiController]
    +    [AutoValidateAntiforgeryToken]
         public class SiteApiController : Controller
         {
             private readonly SiteService _service;
    
  • core/Piranha.Manager/Extensions/ManagerExtensions.cs+166 166 renamed
    @@ -1,166 +1,166 @@
    -/*
    
    - * Copyright (c) .NET Foundation and Contributors
    
    - *
    
    - * This software may be modified and distributed under the terms
    
    - * of the MIT license.  See the LICENSE file for details.
    
    - *
    
    - * https://github.com/piranhacms/piranha.core
    
    - *
    
    - */
    
    -
    
    -using System;
    
    -using Microsoft.AspNetCore.Authorization;
    
    -using Microsoft.AspNetCore.Builder;
    
    -using Microsoft.AspNetCore.Mvc;
    
    -using Microsoft.AspNetCore.Routing;
    
    -using Microsoft.Extensions.DependencyInjection;
    
    -using Microsoft.Extensions.FileProviders;
    
    -using Newtonsoft.Json;
    
    -using Piranha.Manager;
    
    -using Piranha.Manager.Hubs;
    
    -using Piranha.Manager.Services;
    
    -
    
    -public static class ManagerModuleExtensions
    
    -{
    
    -    /// <summary>
    
    -    /// Adds the Piranha manager module.
    
    -    /// </summary>
    
    -    /// <param name="services">The current service collection</param>
    
    -    /// <returns>The services</returns>
    
    -    public static IServiceCollection AddPiranhaManager(this IServiceCollection services)
    
    -    {
    
    -        return services.AddPiranhaManager(null);
    
    -    }
    
    -
    
    -    /// <summary>
    
    -    /// Adds the Piranha manager module.
    
    -    /// </summary>
    
    -    /// <param name="services">The current service collection</param>
    
    -    /// <param name="configurePolicy">The delegate that will be used to build the Piranha Manager named policies.</param>
    
    -    /// <returns>The services</returns>
    
    -    public static IServiceCollection AddPiranhaManager(this IServiceCollection services, Action<string, AuthorizationPolicyBuilder> configurePolicy)
    
    -    {
    
    -        // Add the manager module
    
    -        Piranha.App.Modules.Register<Piranha.Manager.Module>();
    
    -
    
    -        // Add the manager services
    
    -        services.AddScoped<AliasService>();
    
    -        services.AddScoped<CommentService>();
    
    -        services.AddScoped<ConfigService>();
    
    -        services.AddScoped<ContentService>();
    
    -        services.AddScoped<ContentTypeService>();
    
    -        services.AddScoped<LanguageService>();
    
    -        services.AddScoped<MediaService>();
    
    -        services.AddScoped<ModuleService>();
    
    -        services.AddScoped<PageService>();
    
    -        services.AddScoped<PostService>();
    
    -        services.AddScoped<SiteService>();
    
    -
    
    -        // Add localization service
    
    -        services.AddScoped<ManagerLocalizer>();
    
    -
    
    -        // Add session support
    
    -        services.AddSession();
    
    -
    
    -        // Add SignalR
    
    -        services.AddSignalR();
    
    -
    
    -        // Setup authorization policies
    
    -        services.AddAuthorization(o =>
    
    -        {
    
    -            if (configurePolicy is not null)
    
    -            {
    
    -                //If custom AuthorizationOptions delegate is provided, invoke
    
    -                foreach (var permission in Permission.All())
    
    -                {
    
    -                    o.AddPolicy(permission, configure => configurePolicy.Invoke(permission, configure));
    
    -                }
    
    -            }
    
    -            else
    
    -            {
    
    -                //Else configure default Claims based Policies, using Piranha nested permissions structure
    
    -                //Add root (Admin) policy and Claims
    
    -                o.AddPolicy(Permission.PermissionsStructure.PermissionName, configure => { configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName); });
    
    -
    
    -                foreach (var level1Permission in Permission.PermissionsStructure.ChildPermissions)
    
    -                {
    
    -                    //Add 1 level deep nested level policy and Claims
    
    -                    o.AddPolicy(level1Permission.PermissionName, configure =>
    
    -                    {
    
    -                        configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName);
    
    -                        configure.RequireClaim(level1Permission.PermissionName, level1Permission.PermissionName);
    
    -                    });
    
    -
    
    -                    //Add 2 levels deep policy and Claims
    
    -                    foreach (var level2Permission in level1Permission.ChildPermissions)
    
    -                    {
    
    -                        o.AddPolicy(level2Permission.PermissionName, configure =>
    
    -                        {
    
    -                            configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName);
    
    -                            configure.RequireClaim(level1Permission.PermissionName, level1Permission.PermissionName);
    
    -                            configure.RequireClaim(level2Permission.PermissionName, level2Permission.PermissionName);
    
    -                        });
    
    -                    }
    
    -                }
    
    -            }
    
    -        });
    
    -
    
    -        // Return the service collection
    
    -        return services;
    
    -    }
    
    -
    
    -    /// <summary>
    
    -    /// Uses the Piranha Manager.
    
    -    /// </summary>
    
    -    /// <param name="builder">The application builder</param>
    
    -    /// <returns>The builder</returns>
    
    -    public static IApplicationBuilder UsePiranhaManager(this IApplicationBuilder builder) {
    
    -        return builder.UseStaticFiles(new StaticFileOptions
    
    -        {
    
    -            FileProvider = new EmbeddedFileProvider(typeof(ManagerModuleExtensions).Assembly, "Piranha.Manager.assets.dist"),
    
    -            RequestPath = "/manager/assets"
    
    -        });
    
    -    }
    
    -
    
    -    /// <summary>
    
    -    /// Adds the mappings needed for the Piranha Manager to
    
    -    /// the endpoint routes.
    
    -    /// </summary>
    
    -    /// <param name="builder">The route builder</param>
    
    -    public static void MapPiranhaManager(this IEndpointRouteBuilder builder)
    
    -    {
    
    -        builder.MapHub<PreviewHub>("/manager/preview");
    
    -        builder.MapRazorPages();
    
    -    }
    
    -
    
    -    public static IMvcBuilder AddPiranhaManagerOptions(this IMvcBuilder builder,
    
    -        Action<MvcNewtonsoftJsonOptions> jsonOptions = null)
    
    -    {
    
    -        return builder
    
    -            .AddRazorPagesOptions(options =>
    
    -            {
    
    -                options.Conventions.AuthorizeAreaFolder("Manager", "/");
    
    -            })
    
    -            .AddViewLocalization()
    
    -            .AddDataAnnotationsLocalization()
    
    -            .AddNewtonsoftJson(options =>
    
    -            {
    
    -                // Invoke custom json options
    
    -                jsonOptions?.Invoke(options);
    
    -
    
    -                // Set required options for Piranha
    
    -                options.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
    
    -            });
    
    -    }
    
    -
    
    -    /// <summary>
    
    -    /// Static accessor to Manager module if it is registered in the Piranha
    
    -    /// application.
    
    -    /// </summary>
    
    -    /// <param name="modules">The available modules</param>
    
    -    /// <returns>The manager module</returns>
    
    -    public static Piranha.Manager.Module Manager(this Piranha.Runtime.AppModuleList modules)
    
    -    {
    
    -        return modules.Get<Piranha.Manager.Module>();
    
    -    }
    
    -}
    
    +/*
    + * Copyright (c) .NET Foundation and Contributors
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + *
    + * https://github.com/piranhacms/piranha.core
    + *
    + */
    +
    +using System;
    +using Microsoft.AspNetCore.Authorization;
    +using Microsoft.AspNetCore.Builder;
    +using Microsoft.AspNetCore.Mvc;
    +using Microsoft.AspNetCore.Routing;
    +using Microsoft.Extensions.DependencyInjection;
    +using Microsoft.Extensions.FileProviders;
    +using Newtonsoft.Json;
    +using Piranha.Manager;
    +using Piranha.Manager.Hubs;
    +using Piranha.Manager.Services;
    +
    +public static class ManagerModuleExtensions
    +{
    +    /// <summary>
    +    /// Adds the Piranha manager module.
    +    /// </summary>
    +    /// <param name="services">The current service collection</param>
    +    /// <returns>The services</returns>
    +    public static IServiceCollection AddPiranhaManager(this IServiceCollection services)
    +    {
    +        return services.AddPiranhaManager(null);
    +    }
    +
    +    /// <summary>
    +    /// Adds the Piranha manager module.
    +    /// </summary>
    +    /// <param name="services">The current service collection</param>
    +    /// <param name="configurePolicy">The delegate that will be used to build the Piranha Manager named policies.</param>
    +    /// <returns>The services</returns>
    +    public static IServiceCollection AddPiranhaManager(this IServiceCollection services, Action<string, AuthorizationPolicyBuilder> configurePolicy)
    +    {
    +        // Add the manager module
    +        Piranha.App.Modules.Register<Piranha.Manager.Module>();
    +
    +        // Add the manager services
    +        services.AddScoped<AliasService>();
    +        services.AddScoped<CommentService>();
    +        services.AddScoped<ConfigService>();
    +        services.AddScoped<ContentService>();
    +        services.AddScoped<ContentTypeService>();
    +        services.AddScoped<LanguageService>();
    +        services.AddScoped<MediaService>();
    +        services.AddScoped<ModuleService>();
    +        services.AddScoped<PageService>();
    +        services.AddScoped<PostService>();
    +        services.AddScoped<SiteService>();
    +
    +        // Add localization service
    +        services.AddScoped<ManagerLocalizer>();
    +
    +        // Add session support
    +        services.AddSession();
    +
    +        // Add SignalR
    +        services.AddSignalR();
    +
    +        // Setup authorization policies
    +        services.AddAuthorization(o =>
    +        {
    +            if (configurePolicy is not null)
    +            {
    +                //If custom AuthorizationOptions delegate is provided, invoke
    +                foreach (var permission in Permission.All())
    +                {
    +                    o.AddPolicy(permission, configure => configurePolicy.Invoke(permission, configure));
    +                }
    +            }
    +            else
    +            {
    +                //Else configure default Claims based Policies, using Piranha nested permissions structure
    +                //Add root (Admin) policy and Claims
    +                o.AddPolicy(Permission.PermissionsStructure.PermissionName, configure => { configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName); });
    +
    +                foreach (var level1Permission in Permission.PermissionsStructure.ChildPermissions)
    +                {
    +                    //Add 1 level deep nested level policy and Claims
    +                    o.AddPolicy(level1Permission.PermissionName, configure =>
    +                    {
    +                        configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName);
    +                        configure.RequireClaim(level1Permission.PermissionName, level1Permission.PermissionName);
    +                    });
    +
    +                    //Add 2 levels deep policy and Claims
    +                    foreach (var level2Permission in level1Permission.ChildPermissions)
    +                    {
    +                        o.AddPolicy(level2Permission.PermissionName, configure =>
    +                        {
    +                            configure.RequireClaim(Permission.PermissionsStructure.PermissionName, Permission.PermissionsStructure.PermissionName);
    +                            configure.RequireClaim(level1Permission.PermissionName, level1Permission.PermissionName);
    +                            configure.RequireClaim(level2Permission.PermissionName, level2Permission.PermissionName);
    +                        });
    +                    }
    +                }
    +            }
    +        });
    +
    +        // Return the service collection
    +        return services;
    +    }
    +
    +    /// <summary>
    +    /// Uses the Piranha Manager.
    +    /// </summary>
    +    /// <param name="builder">The application builder</param>
    +    /// <returns>The builder</returns>
    +    public static IApplicationBuilder UsePiranhaManager(this IApplicationBuilder builder) {
    +        return builder.UseStaticFiles(new StaticFileOptions
    +        {
    +            FileProvider = new EmbeddedFileProvider(typeof(ManagerModuleExtensions).Assembly, "Piranha.Manager.assets.dist"),
    +            RequestPath = "/manager/assets"
    +        });
    +    }
    +
    +    /// <summary>
    +    /// Adds the mappings needed for the Piranha Manager to
    +    /// the endpoint routes.
    +    /// </summary>
    +    /// <param name="builder">The route builder</param>
    +    public static void MapPiranhaManager(this IEndpointRouteBuilder builder)
    +    {
    +        builder.MapHub<PreviewHub>("/manager/preview");
    +        builder.MapRazorPages();
    +    }
    +
    +    public static IMvcBuilder AddPiranhaManagerOptions(this IMvcBuilder builder,
    +        Action<MvcNewtonsoftJsonOptions> jsonOptions = null)
    +    {
    +        return builder
    +            .AddRazorPagesOptions(options =>
    +            {
    +                options.Conventions.AuthorizeAreaFolder("Manager", "/");
    +            })
    +            .AddViewLocalization()
    +            .AddDataAnnotationsLocalization()
    +            .AddNewtonsoftJson(options =>
    +            {
    +                // Invoke custom json options
    +                jsonOptions?.Invoke(options);
    +
    +                // Set required options for Piranha
    +                options.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
    +            });
    +    }
    +
    +    /// <summary>
    +    /// Static accessor to Manager module if it is registered in the Piranha
    +    /// application.
    +    /// </summary>
    +    /// <param name="modules">The available modules</param>
    +    /// <returns>The manager module</returns>
    +    public static Piranha.Manager.Module Manager(this Piranha.Runtime.AppModuleList modules)
    +    {
    +        return modules.Get<Piranha.Manager.Module>();
    +    }
    +}
    
  • core/Piranha.Manager/Extensions/ManagerStartupExtensions.cs+23 6 renamed
    @@ -22,22 +22,39 @@ public static class ManagerStartupExtensions
         /// Uses the Piranha Manager services if simple startup is used.
         /// </summary>
         /// <param name="serviceBuilder">The service builder</param>
    +    /// <param name="options">The optional options</param>
         /// <param name="jsonOptions">Optional JSON options</param>
         /// <returns>The updated builder</returns>
         public static PiranhaServiceBuilder UseManager(this PiranhaServiceBuilder serviceBuilder,
    +        Action<ManagerOptions> options = null,
             Action<MvcNewtonsoftJsonOptions> jsonOptions = null)
         {
    -        // Add dependent services
    -        serviceBuilder.Services.AddLocalization(options =>
    -            options.ResourcesPath = "Resources"
    +        // Perform optional configuration
    +        var managerOptions = new ManagerOptions();
    +        options?.Invoke(managerOptions);
    +
    +        // Add manager services
    +        serviceBuilder.Services.AddPiranhaManager();
    +
    +        // Add dependent ASP.NET services
    +        serviceBuilder.Services.AddLocalization(o =>
    +            o.ResourcesPath = "Resources"
             );
             serviceBuilder.Services.AddControllersWithViews();
             serviceBuilder.Services.AddRazorPages()
                 .AddPiranhaManagerOptions(jsonOptions);
    +        serviceBuilder.Services.AddAntiforgery(o =>
    +        {
    +            o.HeaderName = managerOptions.XsrfHeaderName; 
    +        });
     
    -        // Add manager services
    -        serviceBuilder.Services.AddPiranhaManager();
    -
    +        // Add options
    +        serviceBuilder.Services.Configure<ManagerOptions>(o => 
    +        {
    +            o.JsonOptions = managerOptions.JsonOptions;
    +            o.XsrfCookieName = managerOptions.XsrfCookieName;
    +            o.XsrfHeaderName = managerOptions.XsrfHeaderName;
    +        }); 
             return serviceBuilder;
         }
     
    
  • core/Piranha.Manager.LocalAuth/Areas/Manager/Pages/Login.cshtml.cs+2 3 modified
    @@ -104,10 +104,9 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)
     
                 if (!string.IsNullOrEmpty(returnUrl))
                 {
    -                return LocalRedirect(returnUrl);
    +                return LocalRedirect($"~/manager/login/auth?returnUrl={ returnUrl }");
                 }
    -
    -            return new RedirectToPageResult("Index");
    +            return LocalRedirect("~/manager/login/auth");
             }
         }
     }
    \ No newline at end of file
    
  • core/Piranha.Manager/ManagerOptions.cs+36 0 added
    @@ -0,0 +1,36 @@
    +/*
    + * Copyright (c) .NET Foundation and Contributors
    + *
    + * This software may be modified and distributed under the terms
    + * of the MIT license.  See the LICENSE file for details.
    + *
    + * https://github.com/piranhacms/piranha.core
    + *
    + */
    +
    +using System;
    +using Microsoft.AspNetCore.Mvc;
    +
    +namespace Piranha.Manager
    +{
    +    public sealed class ManagerOptions
    +    {
    +        /// <summary>
    +        /// Gets/sets the XSRF cookie name that will be set by the manager after
    +        /// successful authentication. The default is "XSRF-REQUEST-TOKEN".
    +        /// </summary>
    +        public string XsrfCookieName { get; set; } = "XSRF-REQUEST-TOKEN";
    +
    +        /// <summary>
    +        /// Gets/sets the name of the header the manager will use to send back the
    +        /// anti forgery information to the API. The default is "X-XSRF-TOKEN". 
    +        /// </summary>
    +        public string XsrfHeaderName { get; set; } = "X-XSRF-TOKEN";
    +
    +        /// <summary>
    +        /// Gets/sets the optional JSON options for the Newtonsoft serializer.
    +        /// </summary>
    +        /// <value></value>
    +        public Action<MvcNewtonsoftJsonOptions> JsonOptions { get; set; }
    +    }
    +}
    \ No newline at end of file
    
  • core/Piranha.Manager/Models/LanguageEditModel.cs+5 0 modified
    @@ -22,5 +22,10 @@ public class LanguageEditModel
             /// Gets/sets the available languages
             /// </summary>
             public IEnumerable<Language> Items { get; set; } = new List<Language>();
    +
    +        /// <summary>
    +        /// Gets/sets the optional status message from the last operation.
    +        /// </summary>
    +        public StatusMessage Status { get; set; }        
         }
     }
    \ No newline at end of file
    
  • identity/Piranha.AspNetCore.Identity/Areas/Manager/Views/Role/List.cshtml+6 3 modified
    @@ -53,9 +53,12 @@
                     <td class="actions one">
                         @if ((await Auth.AuthorizeAsync(User, Piranha.AspNetCore.Identity.Permissions.RolesDelete)).Succeeded)
                         {
    -                        <a class="danger" href="@Url.Action("Delete", new {id = role.Id})">
    -                            <span class="fas fa-trash"></span>
    -                        </a>
    +                        <form method="post" action="~/manager/role/delete">
    +                            <input type="hidden" name="id" value="@role.Id">
    +                            <button class="danger">
    +                                <span class="fas fa-trash"></span>
    +                            </button>
    +                        </form>
                         }
                     </td>
                 </tr>
    
  • identity/Piranha.AspNetCore.Identity/assets/piranha.useredit.js+2 6 modified
    @@ -48,9 +48,7 @@ piranha.useredit= new Vue({
                 console.log(JSON.stringify(self.userModel));
                 fetch(piranha.baseUrl + "manager/user/save", {
                     method: "post",
    -                headers: {
    -                    "Content-Type": "application/json"
    -                },
    +                headers: piranha.utils.antiForgeryHeaders(),
                     body: JSON.stringify(self.userModel)
                 })
                 .then(function (response) {
    @@ -102,9 +100,7 @@ piranha.useredit= new Vue({
                         var ok = false;
                         fetch(piranha.baseUrl + "manager/user/delete", {
                             method: "delete",
    -                        headers: {
    -                            "Content-Type": "application/json"
    -                        },
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(userId)
                         })
                         .then(function (response) { 
    
  • identity/Piranha.AspNetCore.Identity/assets/piranha.userlist.js+1 3 modified
    @@ -50,9 +50,7 @@ piranha.userlist = new Vue({
                     onConfirm: function () {
                         fetch(piranha.baseUrl + "manager/user/delete", {
                             method: "delete",
    -                        headers: {
    -                            "Content-Type": "application/json"
    -                        },
    +                        headers: piranha.utils.antiForgeryHeaders(),
                             body: JSON.stringify(user.id)
                         })
                         .then(function (response) { return response.json(); })
    
  • identity/Piranha.AspNetCore.Identity/Controllers/RoleController.cs+2 1 modified
    @@ -18,6 +18,7 @@
     namespace Piranha.AspNetCore.Identity.Controllers
     {
         [Area("Manager")]
    +    [AutoValidateAntiforgeryToken]
         public class RoleController : ManagerController
         {
             private readonly IDb _db;
    @@ -66,7 +67,7 @@ public IActionResult Save(RoleEditModel model)
                 return View("Edit", model);
             }
     
    -        [HttpGet]
    +        [HttpPost]
             [Route("/manager/role/delete")]
             [Authorize(Policy = Permissions.RolesDelete)]
             public IActionResult Delete(Guid id)
    
  • identity/Piranha.AspNetCore.Identity/Controllers/UserController.cs+1 0 modified
    @@ -28,6 +28,7 @@ namespace Piranha.AspNetCore.Identity.Controllers
         /// Manager controller for managing users accounts.
         /// </summary>
         [Area("Manager")]
    +    [AutoValidateAntiforgeryToken]
         public class UserController : ManagerController
         {
             private readonly IDb _db;
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.