shell bypass 403
UnknownSec Shell
:
/
home
/
forge
/
lolas.brannanatkinson.com
/
vendor
/
statamic
/
cms
/
resources
/
js
/
components
/
nav
/ [
drwxrwxr-x
]
upload
mass deface
mass delete
console
info server
name :
Builder.vue
<template> <div> <header class="mb-6"> <breadcrumb v-if="indexUrl" :url="indexUrl" :title="__('CP Nav Preferences')" /> <div class="flex items-center"> <h1 class="flex-1">{{ __(title) }}</h1> <dropdown-list class="rtl:ml-2 ltr:mr-2"> <dropdown-item :text="__('Reset Nav Customizations')" class="warning" @click="confirmingReset = true"></dropdown-item> </dropdown-list> <a @click="discardChanges" class="text-2xs text-blue rtl:ml-4 ltr:mr-4 underline" v-if="isDirty" v-text="__('Discard changes')" /> <dropdown-list> <template #trigger> <button class="btn flex items-center rtl:pl-4 ltr:pr-4"> {{ __('Add Item') }} <svg-icon name="micro/chevron-down-xs" class="w-2 rtl:mr-4 ltr:ml-4" /> </button> </template> <dropdown-item :text="__('Add Nav Item')" @click="addItem(topLevelTreeData)" /> <dropdown-item :text="__('Add Section')" @click="addSection" /> </dropdown-list> <div class="rtl:mr-4 ltr:ml-4 rtl:text-right ltr:text-left" :class="{ 'btn-group': hasSaveAsOptions }"> <button class="btn-primary rtl:pr-4 ltr:pl-4" :class="{ 'disabled': !changed }" :disabled="!changed" @click="save" v-text="__('Save Changes')" /> <dropdown-list v-if="hasSaveAsOptions" class="rtl:mr-0 ltr:ml-0"> <template #trigger> <button class="btn-primary rtl:rounded-r-none ltr:rounded-l-none flex items-center"> <svg-icon name="micro/chevron-down-xs" class="w-2" /> </button> </template> <h6 class="p-2">{{ __('Save to') }}...</h6> <dropdown-item v-for="option in saveAsOptions" :key="option.url" @click="saveAs(option.url)" class="group"> <div class="flex items-start rtl:pl-4 ltr:pr-4"> <svg-icon :name="option.icon" class="text-gray shrink-0 rtl:ml-2 ltr:mr-2 w-4 group-hover:text-white" /> <span class="whitespace-normal">{{ __(option.label) }}</span> </div> </dropdown-item> </dropdown-list> </div> </div> </header> <div class="mb-2 flex justify-end"> <a class="text-2xs text-blue rtl:ml-4 ltr:mr-4 underline" v-text="__('Expand All')" @click="expandAll" /> <a class="text-2xs text-blue rtl:ml-2 ltr:mr-2 underline" v-text="__('Collapse All')" @click="collapseAll" /> </div> <div v-if="!loading" class="page-tree page-tree-with-sections w-full"> <draggable-tree draggable cross-tree class="mb-8" :class="{ 'section-placeholder-inner': showTopLevelSectionPlaceholder }" ref="topLevelTree" :data="topLevelTreeData" :space="1" :indent="24" :dir="direction" @change="changed = true" @drag="treeDrag" @drop="treeDrop" > <tree-branch slot-scope="{ data: item, store, vm }" :item="item" :depth="vm.level" :vm="vm" :is-open="item.open" :is-child="isChildItemNode(item)" :has-children="item.children.length > 0" :disable-sections="true" :top-level="true" @edit="editItem(item, true)" @toggle-open="store.toggleOpen(item)" > <template #branch-options="{ item }"> <dropdown-item v-if="vm.level < 2" :text="__('Add Item')" @click="addItem(item.children, item)" /> <dropdown-item :text="__('Edit')" @click="editingItem = item" /> <dropdown-item :text="__('Duplicate')" @click="aliasItem(item)" /> <li class="divider" /> <dropdown-item v-if="itemIsVisible(item)" :text="isHideable(item) ? __('Hide') : __('Remove')" class="warning" @click="isHideable(item) ? hideItem(item) : removeItem(item, vm)" /> <dropdown-item v-else :text="__('Show')" @click="showItem(item)" /> </template> </tree-branch> </draggable-tree> <draggable-tree draggable cross-tree class="page-tree-with-sections" ref="mainTree" :data="mainTreeData" :space="1" :indent="24" :dir="direction" @change="changed = true" @drag="treeDrag" @drop="treeDrop" > <tree-branch slot-scope="{ data: item, store, vm }" :item="item" :parent-section="getParentSectionNode(item)" :depth="vm.level" :vm="vm" :is-open="item.open" :is-child="isChildItemNode(item)" :has-children="item.children.length > 0" @edit="editItem(item)" @toggle-open="store.toggleOpen(item)" > <template #branch-options="{ item }"> <dropdown-item v-if="vm.level < 3" :text="__('Add Item')" @click="addItem(item.children, item)" /> <dropdown-item :text="__('Edit')" @click="editItem(item)" /> <dropdown-item v-if="! isSectionNode(item)" :text="__('Pin to Top Level')" @click="pinItem(item)" /> <dropdown-item v-if="! isSectionNode(item)" :text="__('Duplicate')" @click="aliasItem(item)" /> <li class="divider" /> <dropdown-item v-if="itemIsVisible(item)" :text="isHideable(item) ? __('Hide') : __('Remove')" class="warning" @click="isHideable(item) ? hideItem(item) : removeItem(item)" /> <dropdown-item v-else :text="__('Show')" @click="showItem(item)" /> </template> </tree-branch> </draggable-tree> </div> <item-editor v-if="creatingItem" :creating="true" :is-child="creatingItemIsChild" @closed="resetItemEditor" @updated="itemAdded" /> <item-editor v-if="editingItem" :item="editingItem" :is-child="isChildItemNode(editingItem)" @closed="resetItemEditor" @updated="itemUpdated" /> <section-editor v-if="creatingSection" :creating="true" @closed="resetSectionEditor" @updated="sectionAdded" /> <section-editor v-if="editingSection" :section-item="editingSection" @closed="resetSectionEditor" @updated="sectionUpdated" /> <confirmation-modal v-if="confirmingReset" :title="__('Reset')" :bodyText="__('Are you sure you want to reset nav customizations?')" :buttonText="__('Reset')" :danger="true" @confirm="reset" @cancel="confirmingReset = false" /> <confirmation-modal v-if="confirmingRemoval" :title="__('Remove')" :bodyText="__('Are you sure you want to remove this section and all of its children?')" :buttonText="__('Remove')" :danger="true" @confirm="removeItem(confirmingRemoval, true)" @cancel="confirmingReset = false" /> </div> </template> <script> import {DraggableTree} from 'vue-draggable-nested-tree/dist/vue-draggable-nested-tree'; import TreeBranch from './Branch.vue'; import ItemEditor from './ItemEditor.vue'; import SectionEditor from './SectionEditor.vue'; import { data_get } from '../../bootstrap/globals.js' export default { components: { DraggableTree, TreeBranch, ItemEditor, SectionEditor, }, props: { title: { type: String, require: true, }, nav: { type: Array, required: true, }, indexUrl: { type: String, }, updateUrl: { type: String, require: true, }, destroyUrl: { type: String, require: true, }, saveAsOptions: { type: Array, default: () => [], }, }, data() { return { initialNav: clone(this.nav), loading: false, topLevelTreeData: [], mainTreeData: [], originalSectionItems: {}, changed: false, targetDataArray: null, creatingItem: false, creatingItemIsChild: false, editingItem: false, creatingSection: false, editingSection: false, confirmingReset: false, confirmingRemoval: false, draggingNode: false, draggingNodeParent: false, } }, created() { this.$keys.bindGlobal(['mod+s'], e => { e.preventDefault(); this.save(); }); }, mounted() { this.setInitialNav(this.nav); }, computed: { isDirty() { return this.changed; }, hasSaveAsOptions() { return this.saveAsOptions.length; }, showTopLevelSectionPlaceholder() { if (! this.topLevelTreeData.length) { return true; } return this.draggingNode && this.topLevelTreeData.length === 1 && this.topLevelTreeData[0]._id === this.draggingNode._id; }, direction() { return this.$config.get('direction', 'ltr'); }, }, methods: { setInitialNav(nav) { let navConfig = clone(nav); this.setOriginalSectionItems(navConfig); let topLevelConfig = navConfig.shift(); this.topLevelTreeData = _.chain(topLevelConfig.items) .map(section => this.normalizeNavConfig(section, false)) .values() .value(); this.mainTreeData = _.chain(navConfig) .mapObject(section => this.normalizeNavConfig(section)) .values() .value(); }, setOriginalSectionItems(nav) { nav.forEach(section => this.originalSectionItems[section.display_original] = section.items_original || []); }, discardChanges() { this.setInitialNav(this.initialNav); this.changed = false; }, normalizeNavConfig(config, isSectionNode = true) { let item = { text: config.display, config: config, original: config.original, manipulations: isSectionNode ? config : config.manipulations || {}, isSection: isSectionNode, open: isSectionNode, }; let children = config.items || config.children; if (children) { item.children = children.map(childItem => { return { text: childItem.display, children: childItem.children.map(childChildItem => this.normalizeNavConfig(childChildItem, false)), open: false, config: childItem, original: childItem.original, manipulations: childItem.manipulations || {}, isSection: false, }; }); } return item; }, treeDrag(node) { this.draggingNode = node; this.draggingNodeParent = node.parent; let nodeDepth = 1; this.traverseTree(node, (_, { depth }) => { nodeDepth = Math.max(nodeDepth, depth); }); // Ensure you can only drop nav item nodes into top level tree root this.$set(this.$refs.topLevelTree.rootData, 'droppable', ! this.isSectionNode(node)); // Ensure you can only drop section nodes to main tree root this.$set(this.$refs.mainTree.rootData, 'droppable', this.isSectionNode(node)); // Hardcode max depths const topLevelTreeMaxDepth = 2 - nodeDepth; // 2 for nav items, and one level of nav item children const mainTreeMaxDepth = 3 - nodeDepth; // 3 for sections, nav items, and one level of nav item children // Ensure max depth for top level tree this.traverseTree(this.topLevelTreeData, (childNode, { depth }) => { if (childNode !== node) { this.$set(childNode, 'droppable', depth <= topLevelTreeMaxDepth && ! this.isSectionNode(node)); } }); // Ensure max depth for main tree this.traverseTree(this.mainTreeData, (childNode, { depth }) => { if (childNode !== node) { this.$set(childNode, 'droppable', depth <= mainTreeMaxDepth && ! this.isSectionNode(node)); } }); }, treeDrop(node) { this.updateItemAction(node); if (data_get(this.draggingNodeParent, 'isRoot') !== true) { this.updateItemAction(this.draggingNodeParent); } this.$nextTick(() => { this.draggingNode = false; this.draggingNodeParent = false; }); }, isSectionNode(node) { return data_get(node, 'isSection', false); }, isParentItemNode(node) { return ! this.isSectionNode(node) && ! this.isChildItemNode(node); }, isChildItemNode(node) { if (data_get(node, 'parent.isRoot')) { return false; } return ! this.isSectionNode(node.parent); }, isCustomSectionNode(node) { return this.isSectionNode(node) && data_get(node, 'manipulations.action') === '@create'; }, getParentSectionNode(node) { if (! this.isSectionNode(node) && node !== undefined) { return this.getParentSectionNode(node.parent); } return node; }, traverseTree(nodes, callback, parentPath = []) { const nodesArray = Array.isArray(nodes) ? nodes : [nodes]; nodesArray.every((node, index) => { const nodePath = [...parentPath, index]; const path = nodePath.join('.'); const depth = nodePath.length; if (false === callback(node, { path, depth, index })) { return false; } if (node.children.length) { this.traverseTree(node.children, callback, nodePath); } return true; }); }, addItem(targetDataArray, parentItem) { this.targetDataArray = targetDataArray; this.creatingItem = true; this.creatingItemIsChild = parentItem && this.isParentItemNode(parentItem); }, addSection() { this.creatingSection = true; }, itemAdded(createdConfig) { let item = this.normalizeNavConfig(createdConfig, false); item.manipulations = { action: '@create', display: createdConfig.display, url: createdConfig.url, icon: createdConfig.icon, }; this.targetDataArray.push(item); this.resetItemEditor(); this.changed = true; }, sectionAdded(sectionDisplay) { let item = this.normalizeNavConfig({ action: '@create', display: sectionDisplay, display_original: false, }); this.mainTreeData.push(item); this.resetSectionEditor(); this.changed = true; }, editItem(item, topLevel) { if (this.isSectionNode(item) && ! topLevel) { this.editingSection = item; } else { this.editingItem = item; } }, itemUpdated(updatedConfig, item) { item.text = updatedConfig.display; item.config.icon = updatedConfig.icon; this.updateItemManipulation(item, 'display', updatedConfig.display); this.updateItemManipulation(item, 'url', updatedConfig.url); this.updateItemManipulation(item, 'icon', updatedConfig.icon); this.updateItemAction(item); this.resetItemEditor(); this.changed = true; }, sectionUpdated(sectionDisplay, sectionItem) { sectionItem.text = sectionDisplay; this.resetSectionEditor(); this.changed = true; }, updateItemManipulation(item, key, value) { let currentAction = data_get(item.manipulations, 'action'); if (currentAction === '@create' || value !== data_get(item.original, key)) { item.manipulations[key] = value; } else { Vue.delete(item.manipulations, key); } }, updateItemAction(item) { if (this.isSectionNode(item)) { return; } let detectedAction = this.detectItemAction(item); if (detectedAction) { item.manipulations.action = detectedAction; } else { Vue.delete(item.manipulations, 'action'); } if (this.isChildItemNode(item)) { this.updateItemAction(item.parent); } }, detectItemAction(item) { let currentAction = data_get(item.manipulations, 'action'); switch (true) { case currentAction === '@create': return '@create'; case currentAction === '@alias': return '@alias'; case currentAction === '@hide': return '@hide'; case this.itemHasMoved(item): return '@move'; case this.itemHasBeenModified(item): return '@modify'; } return null; }, itemHasMoved(item) { if (this.itemIsWithinOriginalParentItem(item)) { return false; } return this.itemHasMovedWithinSection(item) || this.itemHasMovedToAnotherSection(item); }, itemIsWithinOriginalParentItem(item) { let parentsOriginalChildIds = data_get(item.parent, 'original', { children: [] }) .children .map(child => child.id); return this.isChildItemNode(item) && parentsOriginalChildIds.includes(item.config.id); }, itemHasMovedWithinSection(item) { let parentsOriginalChildIds = data_get(item.parent, 'original', { children: [] }) .children .map(child => child.id); if (this.isChildItemNode(item) && ! parentsOriginalChildIds.includes(item.config.id)) { return true; } let currentSection = data_get(this.getParentSectionNode(item), 'config.display_original', 'Top Level'); let sectionsOriginalIds = this.originalSectionItems[currentSection]; if (sectionsOriginalIds === undefined) { return false; } if (! this.isChildItemNode(item) && ! sectionsOriginalIds.includes(item.config.id)) { return true; } return false; }, itemHasMovedToAnotherSection(item) { let currentSection = data_get(this.getParentSectionNode(item), 'config.display_original', 'Top Level'); let originalSection = data_get(item.original, 'section') || data_get(item.parent, 'original.section'); return currentSection !== originalSection; }, itemHasBeenModified(item) { return this.itemHasModifiedProperties(item) || this.itemHasModifiedChildren(item); }, itemHasModifiedProperties(item) { return _.chain(item.manipulations).omit(['action', 'reorder', 'children']).keys().value().length > 0; }, itemHasModifiedChildren(item) { return item.children.filter(childItem => { return _.chain(childItem.manipulations).keys().value().length > 0; }).length > 0; }, expandAll() { this.traverseTree(this.topLevelTreeData, (node) => { if (! this.isSectionNode(node)) { this.$set(node, 'open', true); } }); this.traverseTree(this.mainTreeData, (node) => { if (! this.isSectionNode(node)) { this.$set(node, 'open', true); } }); }, collapseAll() { this.traverseTree(this.topLevelTreeData, (node) => { if (! this.isSectionNode(node)) { this.$set(node, 'open', false); } }); this.traverseTree(this.mainTreeData, (node) => { if (! this.isSectionNode(node)) { this.$set(node, 'open', false); } }); }, resetItemEditor() { this.editingItem = false; this.creatingItem = false; this.creatingItemIsChild = false; this.targetDataArray = false; }, resetSectionEditor() { this.editingSection = false; this.creatingSection = false; }, pinItem(item) { this.aliasItem(item, this.topLevelTreeData); }, aliasItem(item, treeData) { let currentAction = data_get(item.manipulations, 'action'); let newItem = this.normalizeNavConfig(clone(item.config), false); if (currentAction === '@create') { newItem.manipulations = clone(item.manipulations); } else { newItem.manipulations = { action: '@alias' }; } newItem.children = []; if (newItem.original) { newItem.original.children = []; } let tree = treeData || item.parent.children; tree.push(newItem); this.changed = true; }, itemIsVisible(item) { return data_get(item.manipulations, 'action') !== '@hide'; }, isHideable(item) { let action = data_get(item.manipulations, 'action'); if (this.isSectionNode(item) && action === '@create') { return false; } return ! ['@alias', '@create'].includes(action); }, removeItem(item, bypassConfirmation = false) { if (this.isCustomSectionNode(item) && item.children.length && ! bypassConfirmation) { return this.confirmingRemoval = item; } item._vm.store.deleteNode(item); this.changed = true; this.confirmingRemoval = false; }, hideItem(item) { Vue.set(item.manipulations, 'action', '@hide'); this.updateItemAction(item); this.changed = true; }, showItem(item) { Vue.delete(item.manipulations, 'action'); this.updateItemAction(item); this.changed = true; }, reset() { this.$axios .delete(this.destroyUrl) .then(() => window.location.reload()) .catch(() => this.$toast.error(__('Something went wrong'))); }, save() { if (! this.changed) { return; } this.saveAs(this.updateUrl); }, saveAs(url) { let tree = this.preparePreferencesSubmission(); this.$axios .patch(url, {tree}) .then(() => location.reload()) .catch(() => this.$toast.error(__('Something went wrong'))); }, preparePreferencesSubmission() { let tree = []; tree.push({ 'display': 'Top Level', 'display_original': 'Top Level', 'action': false, 'items': this.prepareItemsForSubmission(this.topLevelTreeData), }); this.mainTreeData.forEach(section => { tree.push({ 'display': section.text, 'display_original': section.config.display_original || section.text, 'action': section.manipulations.action || false, 'items': this.prepareItemsForSubmission(section.children), }); }); return tree; }, prepareItemsForSubmission(treeItems) { let items = []; treeItems.forEach(item => { items.push({ 'id': this.prepareItemIdForSubmission(item), 'manipulations': item.manipulations, 'children': item.children ? this.prepareItemsForSubmission(item.children) : [], }); }); return items; }, prepareItemIdForSubmission(item) { return data_get(item, 'original.id', item.text.toLowerCase().replaceAll(' ', '_')); }, }, } </script>
© 2026 UnknownSec