From 3cafa4dcc9c13b7806c324b5ba8f7c1f23291d78 Mon Sep 17 00:00:00 2001 From: "Ronald A. Richardson" Date: Fri, 26 Dec 2025 13:12:44 +0800 Subject: [PATCH 1/4] Fixed icon branding, removed quick actions --- addon/extension.js | 4 +- addon/templates/home.hbs | 29 ---------- composer.json | 2 +- extension.json | 2 +- package.json | 4 +- pnpm-lock.yaml | 122 ++++++++++++++++++++++++++------------- 6 files changed, 89 insertions(+), 74 deletions(-) diff --git a/addon/extension.js b/addon/extension.js index 5b63ecb..c5b6a90 100644 --- a/addon/extension.js +++ b/addon/extension.js @@ -5,8 +5,8 @@ export default { const menuService = universe.getService('menu'); // Register menu item in header - // const iconOptions = { iconComponent: new ExtensionComponent('@fleetbase/solid-engine', 'solid-brand-icon'), iconComponentOptions: { width: 19, height: 19 } }; - menuService.registerHeaderMenuItem('Solid', 'console.solid-protocol', { priority: 5 }); + const iconOptions = { iconComponent: new ExtensionComponent('@fleetbase/solid-engine', 'solid-brand-icon'), iconComponentOptions: { width: 19, height: 19 } }; + menuService.registerHeaderMenuItem('Solid', 'console.solid-protocol', { ...iconOptions, priority: 5 }); // Register admin settings -- create a solid server menu panel with it's own setting options universe.registerAdminMenuPanel( diff --git a/addon/templates/home.hbs b/addon/templates/home.hbs index e129e91..1b84ecb 100644 --- a/addon/templates/home.hbs +++ b/addon/templates/home.hbs @@ -58,35 +58,6 @@ - -
-
- -

Browse Data

-

Explore and manage your Fleetops data in Solid

-
-
-
-
- -

Account Settings

-

Manage your account and profile settings

-
-
-
-
- -

Sync Data

-

Sync your Fleetbase data to Solid storage

-
-
-
-
-
-
diff --git a/composer.json b/composer.json index 378897b..d046761 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "fleetbase/solid-api", - "version": "0.0.5", + "version": "0.0.6", "description": "Solid Protocol Extension to Store and Share Data with Fleetbase", "keywords": [ "fleetbase-extension", diff --git a/extension.json b/extension.json index 702d6d1..94ba457 100644 --- a/extension.json +++ b/extension.json @@ -1,6 +1,6 @@ { "name": "Solid", - "version": "0.0.5", + "version": "0.0.6", "description": "Solid Protocol Extension to Store and Share Data with Fleetbase", "repository": "https://github.com/fleetbase/solid", "license": "AGPL-3.0-or-later", diff --git a/package.json b/package.json index d229147..4125c7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fleetbase/solid-engine", - "version": "0.0.5", + "version": "0.0.6", "description": "Solid Protocol Extension to Store and Share Data with Fleetbase", "fleetbase": { "route": "solid-protocol" @@ -46,7 +46,7 @@ "dependencies": { "@babel/core": "^7.23.2", "@fleetbase/ember-core": "^0.3.9", - "@fleetbase/ember-ui": "^0.3.15", + "@fleetbase/ember-ui": "^0.3.16", "@fleetbase/fleetops-data": "^0.1.24", "@fortawesome/ember-fontawesome": "^2.0.0", "@fortawesome/fontawesome-svg-core": "6.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76e932b..89b8f68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^0.3.9 version: 0.3.9(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(eslint@8.57.1)(webpack@5.104.1) '@fleetbase/ember-ui': - specifier: ^0.3.15 - version: 0.3.15(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.104.1) + specifier: ^0.3.16 + version: 0.3.16(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.104.1) '@fleetbase/fleetops-data': specifier: ^0.1.24 version: 0.1.24(@ember/string@3.1.1)(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(eslint@8.57.1)(webpack@5.104.1) @@ -1225,6 +1225,15 @@ packages: '@glint/template': optional: true + '@embroider/macros@1.19.6': + resolution: {integrity: sha512-yPf8lD/gRZmcxms66CCKuZuvWMJ0g/hdCE6P8FZsyewR3So6pxgdgOFp0zk2w5d34jS1ejBtxLNREZbBTELpzw==} + engines: {node: 12.* || 14.* || >= 16} + peerDependencies: + '@glint/template': ^1.0.0 + peerDependenciesMeta: + '@glint/template': + optional: true + '@embroider/reverse-exports@0.2.0': resolution: {integrity: sha512-WFsw8nQpHZiWGEDYpa/A79KEFfTisqteXbY+jg9eZiww1r1G+LZvsmdszDp86TkotUSCqrMbK/ewn0jR1CJmqg==} engines: {node: 12.* || 14.* || >= 16} @@ -1241,6 +1250,10 @@ packages: resolution: {integrity: sha512-d7RQwDwqqHo7YvjE9t1rtIrCCYtbSoO0uRq2ikVhRh4hGS5OojZNu2ZtS0Wqrg+V72CRtMFr/hibTvHNsRM2Lg==} engines: {node: 12.* || 14.* || >= 16} + '@embroider/shared-internals@3.0.2': + resolution: {integrity: sha512-/SusdG+zgosc3t+9sPFVKSFOYyiSgLfXOT6lYNWoG1YtnhWDxlK4S8leZ0jhcVjemdaHln5rTyxCnq8oFLxqpQ==} + engines: {node: 12.* || 14.* || >= 16} + '@embroider/test-setup@3.0.3': resolution: {integrity: sha512-3K5KSyTdnxAkZQill6+TdC/XTRr6226LNwZMsrhRbBM0FFZXw2D8qmJSHPvZLheQx3A1jnF9t1lyrAzrKlg6Yw==} engines: {node: 12.* || 14.* || >= 16} @@ -1297,8 +1310,8 @@ packages: resolution: {integrity: sha512-CxMEyNGhSk0u8SkI6GZiKY2W/246PyBpY6clZahoxtyokL76kWx2KjEk4iXqKdhKKU5a5OKlS8Kw9wb0peZZzw==} engines: {node: '>= 18'} - '@fleetbase/ember-ui@0.3.15': - resolution: {integrity: sha512-eKzaUyTUa6Fp8seRS0dB/6+tf///YVMx8MvuJPQoKBupxmQ7i+D6iehu6KBrbDRfoHVWRL6/44WmoTZoDzDQRA==} + '@fleetbase/ember-ui@0.3.16': + resolution: {integrity: sha512-3JewcvB86pUEcMN+aOmPR6hA6OaFRbxDuHjRMguP16zQOd31++Qr0rOviTaNyih2SIwoJCjCTc3FS1Umanko3w==} engines: {node: '>= 18'} '@fleetbase/fleetops-data@0.1.24': @@ -1380,18 +1393,18 @@ packages: resolution: {integrity: sha512-kutPeRGWm8V5dltFP1zGjQOEAzaLZj4StdQhWVZnfGFCvAPVvHh8qk5bRrU4KXnRRRNni5tKQI9PBAdI6MP8nQ==} engines: {node: '>=6'} - '@fullcalendar/core@6.1.19': - resolution: {integrity: sha512-z0aVlO5e4Wah6p6mouM0UEqtRf1MZZPt4mwzEyU6kusaNL+dlWQgAasF2cK23hwT4cmxkEmr4inULXgpyeExdQ==} + '@fullcalendar/core@6.1.20': + resolution: {integrity: sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==} - '@fullcalendar/daygrid@6.1.19': - resolution: {integrity: sha512-IAAfnMICnVWPjpT4zi87i3FEw0xxSza0avqY/HedKEz+l5MTBYvCDPOWDATpzXoLut3aACsjktIyw9thvIcRYQ==} + '@fullcalendar/daygrid@6.1.20': + resolution: {integrity: sha512-AO9vqhkLP77EesmJzuU+IGXgxNulsA8mgQHynclJ8U70vSwAVnbcLG9qftiTAFSlZjiY/NvhE7sflve6cJelyQ==} peerDependencies: - '@fullcalendar/core': ~6.1.19 + '@fullcalendar/core': ~6.1.20 - '@fullcalendar/interaction@6.1.19': - resolution: {integrity: sha512-GOciy79xe8JMVp+1evAU3ytdwN/7tv35t5i1vFkifiuWcQMLC/JnLg/RA2s4sYmQwoYhTw/p4GLcP0gO5B3X5w==} + '@fullcalendar/interaction@6.1.20': + resolution: {integrity: sha512-p6txmc5txL0bMiPaJxe2ip6o0T384TyoD2KGdsU6UjZ5yoBlaY+dg7kxfnYKpYMzEJLG58n+URrHr2PgNL2fyA==} peerDependencies: - '@fullcalendar/core': ~6.1.19 + '@fullcalendar/core': ~6.1.20 '@glimmer/compiler@0.84.3': resolution: {integrity: sha512-cj9sGlnvExP9httxY6ZMivJRGulyaZ31DddCYB5h6LxupR4Nk2d1nAJCWPLsvuQJ8qR+eYw0y9aiY/VeT0krpQ==} @@ -3295,8 +3308,8 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-tag@3.1.3: - resolution: {integrity: sha512-4Kiv9mEroxuMXfWUNUHcljVJgxThCNk7eEswdHMXdzJnkBBaYDqDwzHkoh3F74JJhfU3taJOsgpR6oEGIDg17g==} + content-tag@4.1.0: + resolution: {integrity: sha512-On6gUuvI1l5MScHO+Xbwjeq1Pk9H6HOipDWkzqGGUGmKpq6K5TRmQuCl1LGSHbdIo2l+lSsgLKrLgCl5kKYA+A==} content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} @@ -4155,8 +4168,8 @@ packages: resolution: {integrity: sha512-OS8TUVG2kQYYwP3netunLVfeijPoOKIs1SvPQRTNOQX4Pu8xGGBEZmrv0U1YTnQn12Eg+p6w/0UdGbUnITjyzw==} engines: {node: 12.* || >= 14} - ember-template-imports@4.3.0: - resolution: {integrity: sha512-jZ5D6KLKU8up/AynZltmKh4lkXBPgTGSPgomprI/55XvIVqn42UNUpEz7ra/mO3QiGODDZOUesbggPe49i38sQ==} + ember-template-imports@4.4.0: + resolution: {integrity: sha512-HNOHabTEMbRluci1uScvh3ljMDo9E46dHHNcJAIf5yjOhIQ/zN4Y0DVDWrRfcbihlHvt4v/iF69G+8tffC1YkA==} engines: {node: 16.* || >= 18} ember-template-lint@5.13.0: @@ -6914,8 +6927,8 @@ packages: prosemirror-state@1.4.4: resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} - prosemirror-tables@1.8.3: - resolution: {integrity: sha512-wbqCR/RlRPRe41a4LFtmhKElzBEfBTdtAYWNIGHM6X2e24NN/MTNUKyXjjphfAfdQce37Kh/5yf765mLPYDe7Q==} + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} prosemirror-trailing-node@3.0.0: resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} @@ -9666,7 +9679,7 @@ snapshots: '@ember/render-modifiers@2.1.0(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))': dependencies: - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 ember-cli-babel: 7.26.11 ember-modifier-manager-polyfill: 1.2.0(@babel/core@7.28.5) ember-source: 5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1) @@ -9735,6 +9748,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@embroider/macros@1.19.6': + dependencies: + '@embroider/shared-internals': 3.0.2 + assert-never: 1.4.0 + babel-import-util: 3.0.1 + ember-cli-babel: 7.26.11 + find-up: 5.0.0 + lodash: 4.17.21 + resolve: 1.22.11 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + '@embroider/reverse-exports@0.2.0': dependencies: mem: 8.1.1 @@ -9786,6 +9812,24 @@ snapshots: transitivePeerDependencies: - supports-color + '@embroider/shared-internals@3.0.2': + dependencies: + babel-import-util: 3.0.1 + debug: 4.4.3 + ember-rfc176-data: 0.3.18 + fs-extra: 9.1.0 + is-subdir: 1.2.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + minimatch: 3.1.2 + pkg-entry-points: 1.1.1 + resolve-package-path: 4.0.3 + resolve.exports: 2.0.3 + semver: 7.7.3 + typescript-memoize: 1.1.1 + transitivePeerDependencies: + - supports-color + '@embroider/test-setup@3.0.3': dependencies: lodash: 4.17.21 @@ -9793,7 +9837,7 @@ snapshots: '@embroider/util@1.13.5(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))': dependencies: - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 broccoli-funnel: 3.0.8 ember-cli-babel: 7.26.11 ember-source: 5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1) @@ -9865,22 +9909,22 @@ snapshots: - utf-8-validate - webpack - '@fleetbase/ember-ui@0.3.15(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.104.1)': + '@fleetbase/ember-ui@0.3.16(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(@glimmer/component@1.1.2(@babel/core@7.28.5))(@glimmer/tracking@1.1.2)(ember-resolver@11.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(postcss@8.5.6)(rollup@2.79.2)(tracked-built-ins@3.4.0(@babel/core@7.28.5))(webpack@5.104.1)': dependencies: '@babel/core': 7.28.5 '@ember/render-modifiers': 2.1.0(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)) '@ember/string': 3.1.1 '@embroider/addon': 0.30.0 - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 '@fleetbase/ember-accounting': 0.0.1(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)) '@floating-ui/dom': 1.7.4 '@fortawesome/ember-fontawesome': 2.0.0(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(rollup@2.79.2)(webpack@5.104.1) '@fortawesome/fontawesome-svg-core': 6.4.0 '@fortawesome/free-brands-svg-icons': 6.4.0 '@fortawesome/free-solid-svg-icons': 6.4.0 - '@fullcalendar/core': 6.1.19 - '@fullcalendar/daygrid': 6.1.19(@fullcalendar/core@6.1.19) - '@fullcalendar/interaction': 6.1.19(@fullcalendar/core@6.1.19) + '@fullcalendar/core': 6.1.20 + '@fullcalendar/daygrid': 6.1.20(@fullcalendar/core@6.1.20) + '@fullcalendar/interaction': 6.1.20(@fullcalendar/core@6.1.20) '@makepanic/ember-power-calendar-date-fns': 0.4.2 '@tailwindcss/forms': 0.5.11(tailwindcss@3.4.19) '@tiptap/core': 2.27.1(@tiptap/pm@2.27.1) @@ -10111,17 +10155,17 @@ snapshots: dependencies: '@fortawesome/fontawesome-common-types': 6.4.0 - '@fullcalendar/core@6.1.19': + '@fullcalendar/core@6.1.20': dependencies: preact: 10.12.1 - '@fullcalendar/daygrid@6.1.19(@fullcalendar/core@6.1.19)': + '@fullcalendar/daygrid@6.1.20(@fullcalendar/core@6.1.20)': dependencies: - '@fullcalendar/core': 6.1.19 + '@fullcalendar/core': 6.1.20 - '@fullcalendar/interaction@6.1.19(@fullcalendar/core@6.1.19)': + '@fullcalendar/interaction@6.1.20(@fullcalendar/core@6.1.20)': dependencies: - '@fullcalendar/core': 6.1.19 + '@fullcalendar/core': 6.1.20 '@glimmer/compiler@0.84.3': dependencies: @@ -10629,7 +10673,7 @@ snapshots: prosemirror-schema-basic: 1.2.4 prosemirror-schema-list: 1.5.1 prosemirror-state: 1.4.4 - prosemirror-tables: 1.8.3 + prosemirror-tables: 1.8.5 prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4) prosemirror-transform: 1.10.5 prosemirror-view: 1.41.4 @@ -12590,7 +12634,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-tag@3.1.3: {} + content-tag@4.1.0: {} content-type@1.0.5: {} @@ -12956,7 +13000,7 @@ snapshots: ember-animated@1.1.4(@ember/test-helpers@3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1))(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)): dependencies: '@embroider/addon-shim': 1.10.2 - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 '@embroider/util': 1.13.5(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)) assert-never: 1.4.0 ember-element-helper: 0.8.8 @@ -13079,7 +13123,7 @@ snapshots: '@babel/core': 7.28.5 '@ember/test-helpers': 3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1) '@embroider/addon-shim': 1.10.2 - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 '@embroider/util': 1.13.5(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1)) '@glimmer/component': 1.1.2(@babel/core@7.28.5) '@glimmer/tracking': 1.1.2 @@ -13768,7 +13812,7 @@ snapshots: ember-cli-htmlbars: 6.3.0 ember-element-helper: 0.8.8 ember-source: 5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1) - ember-template-imports: 4.3.0 + ember-template-imports: 4.4.0 transitivePeerDependencies: - '@glint/template' - supports-color @@ -13820,7 +13864,7 @@ snapshots: '@ember/test-helpers': 3.3.1(@babel/core@7.28.5)(ember-source@5.4.1(@babel/core@7.28.5)(@glimmer/component@1.1.2(@babel/core@7.28.5))(rsvp@4.8.5)(webpack@5.104.1))(webpack@5.104.1) '@ember/test-waiters': 3.1.0 '@embroider/addon-shim': 1.10.2 - '@embroider/macros': 1.19.5 + '@embroider/macros': 1.19.6 '@glimmer/component': 1.1.2(@babel/core@7.28.5) '@glimmer/tracking': 1.1.2 ember-auto-import: 2.12.0(webpack@5.104.1) @@ -14256,10 +14300,10 @@ snapshots: transitivePeerDependencies: - supports-color - ember-template-imports@4.3.0: + ember-template-imports@4.4.0: dependencies: broccoli-stew: 3.0.0 - content-tag: 3.1.3 + content-tag: 4.1.0 ember-cli-version-checker: 5.1.2 transitivePeerDependencies: - supports-color @@ -17478,7 +17522,7 @@ snapshots: prosemirror-transform: 1.10.5 prosemirror-view: 1.41.4 - prosemirror-tables@1.8.3: + prosemirror-tables@1.8.5: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 From 9d01cda5fe0747e5c9fa3370f0e3635586c20979 Mon Sep 17 00:00:00 2001 From: Manus AI Date: Fri, 26 Dec 2025 00:15:36 -0500 Subject: [PATCH 2/4] Add ACL permission checks and auto-creation for folders - Added ensureFolderPermissions() method to AclService - Automatically create ACL files with full owner permissions after folder creation - Check existing permissions before creating ACL to avoid overwriting - Updated PodService.createFolder() to set ACL after folder creation - Updated ResourceSyncService.createContainer() to set ACL after container creation - Fixes 401 Unauthorized errors when writing to newly created folders --- server/src/Services/AclService.php | 145 ++++++++++++++++++++ server/src/Services/PodService.php | 9 ++ server/src/Services/ResourceSyncService.php | 8 ++ 3 files changed, 162 insertions(+) diff --git a/server/src/Services/AclService.php b/server/src/Services/AclService.php index f99ba80..69b1755 100644 --- a/server/src/Services/AclService.php +++ b/server/src/Services/AclService.php @@ -143,4 +143,149 @@ public function ensureWritePermissions(SolidIdentity $identity, string $podUrl, Log::info('[ACL NEEDS UPDATE]', ['pod_url' => $podUrl]); return $this->grantWritePermissions($identity, $podUrl, $webId); } + + /** + * Ensure a folder has proper ACL permissions after creation. + * + * @param SolidIdentity $identity + * @param string $folderUrl The folder URL (must end with /) + * @param string $webId The WebID to grant permissions to + * @return bool + */ + public function ensureFolderPermissions(SolidIdentity $identity, string $folderUrl, string $webId): bool + { + try { + // Ensure folder URL ends with / + $folderUrl = rtrim($folderUrl, '/') . '/'; + $aclUrl = $folderUrl . '.acl'; + + Log::info('[ACL] Ensuring folder permissions', [ + 'folder_url' => $folderUrl, + 'acl_url' => $aclUrl, + 'webid' => $webId, + ]); + + // Check if ACL already exists and has write permissions + if ($this->hasFolderWritePermissions($identity, $folderUrl, $webId)) { + Log::info('[ACL] Folder already has write permissions', ['folder_url' => $folderUrl]); + return true; + } + + // Create ACL with full permissions for the owner + $aclContent = $this->generateFolderAcl($folderUrl, $webId); + + return $this->createFolderAcl($identity, $aclUrl, $aclContent); + } catch (\Throwable $e) { + Log::error('[ACL] Failed to ensure folder permissions', [ + 'folder_url' => $folderUrl, + 'error' => $e->getMessage(), + ]); + return false; + } + } + + /** + * Check if a folder has write permissions. + * + * @param SolidIdentity $identity + * @param string $folderUrl + * @param string $webId + * @return bool + */ + protected function hasFolderWritePermissions(SolidIdentity $identity, string $folderUrl, string $webId): bool + { + try { + $response = $identity->request('head', $folderUrl); + + if (!$response->successful()) { + return false; + } + + // Check WAC-Allow header + $wacAllow = $response->header('WAC-Allow'); + + if ($wacAllow) { + Log::debug('[ACL] WAC-Allow header', [ + 'folder_url' => $folderUrl, + 'wac_allow' => $wacAllow, + ]); + + // Parse WAC-Allow header: user="read write", public="read" + if (preg_match('/user="([^"]*)"/i', $wacAllow, $matches)) { + $userModes = strtolower($matches[1]); + return str_contains($userModes, 'write') || str_contains($userModes, 'append'); + } + } + + return false; + } catch (\Throwable $e) { + Log::debug('[ACL] Error checking folder permissions', [ + 'folder_url' => $folderUrl, + 'error' => $e->getMessage(), + ]); + return false; + } + } + + /** + * Create an ACL file for a folder. + * + * @param SolidIdentity $identity + * @param string $aclUrl + * @param string $aclContent + * @return bool + */ + protected function createFolderAcl(SolidIdentity $identity, string $aclUrl, string $aclContent): bool + { + try { + $response = $identity->request('put', $aclUrl, $aclContent, [ + 'headers' => [ + 'Content-Type' => 'text/turtle', + ], + ]); + + if ($response->successful()) { + Log::info('[ACL] Folder ACL created successfully', [ + 'acl_url' => $aclUrl, + 'status' => $response->status(), + ]); + return true; + } + + Log::error('[ACL] Failed to create folder ACL', [ + 'acl_url' => $aclUrl, + 'status' => $response->status(), + 'body' => $response->body(), + ]); + + return false; + } catch (\Throwable $e) { + Log::error('[ACL] Error creating folder ACL', [ + 'acl_url' => $aclUrl, + 'error' => $e->getMessage(), + ]); + return false; + } + } + + /** + * Generate ACL content for a folder with full owner permissions. + * + * @param string $folderUrl The folder URL + * @param string $webId The WebID to grant permissions to + * @return string + */ + protected function generateFolderAcl(string $folderUrl, string $webId): string + { + return <<. + +<#owner> + a acl:Authorization; + acl:agent <{$webId}>; + acl:accessTo <./>; + acl:default <./>; + acl:mode acl:Read, acl:Write, acl:Control. +TURTLE; + } } diff --git a/server/src/Services/PodService.php b/server/src/Services/PodService.php index b07690d..95193bb 100644 --- a/server/src/Services/PodService.php +++ b/server/src/Services/PodService.php @@ -804,6 +804,15 @@ public function createFolder(SolidIdentity $identity, string $parentUrl, string 'folder_url' => $createdUrl, 'status' => $response->status(), ]); + + // Ensure the folder has proper ACL permissions + $aclService = app(AclService::class); + $webId = $identity->webid; + + if ($webId) { + $aclService->ensureFolderPermissions($identity, $createdUrl, $webId); + } + return true; } diff --git a/server/src/Services/ResourceSyncService.php b/server/src/Services/ResourceSyncService.php index d854d19..d37e771 100644 --- a/server/src/Services/ResourceSyncService.php +++ b/server/src/Services/ResourceSyncService.php @@ -299,6 +299,14 @@ protected function createContainer(SolidIdentity $identity, string $containerUrl 'url' => $containerUrl, 'status' => $response->status(), ]); + + // Ensure the container has proper ACL permissions + $aclService = app(AclService::class); + $webId = $identity->webid; + + if ($webId) { + $aclService->ensureFolderPermissions($identity, $containerUrl, $webId); + } } catch (\Throwable $e) { // Container might already exist, that's okay Log::debug('[CONTAINER CREATION SKIPPED]', [ From 4e74776499d05d87976405137d88db8d103cb634 Mon Sep 17 00:00:00 2001 From: Manus AI Date: Fri, 26 Dec 2025 00:21:33 -0500 Subject: [PATCH 3/4] Add pod root ACL permission check before folder creation and resource import - Check and ensure pod root has write permissions before creating folders - Check and ensure pod root has write permissions before importing resources - Fixes 401 Unauthorized errors when trying to create folders in pod root - Calls ensureWritePermissions() which will update pod root ACL if needed --- server/src/Http/Controllers/DataController.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/src/Http/Controllers/DataController.php b/server/src/Http/Controllers/DataController.php index 789d876..664fa08 100644 --- a/server/src/Http/Controllers/DataController.php +++ b/server/src/Http/Controllers/DataController.php @@ -138,6 +138,15 @@ public function createFolder(Request $request) 'parent_url' => $parentUrl, ]); + // Ensure pod root has write permissions before creating folder + $aclService = app(\Fleetbase\Solid\Services\AclService::class); + if (!$aclService->ensureWritePermissions($identity, $podUrl, $webId)) { + Log::warning('[FOLDER CREATE] Pod root lacks write permissions', [ + 'pod_url' => $podUrl, + 'webid' => $webId, + ]); + } + // Use POST with Slug header (Solid Protocol standard) $result = $this->podService->createFolder($identity, $parentUrl, $folderName); @@ -237,6 +246,15 @@ public function importResources(Request $request) 'resource_types' => $resourceTypes, ]); + // Ensure pod root has write permissions before importing + $aclService = app(\Fleetbase\Solid\Services\AclService::class); + if (!$aclService->ensureWritePermissions($identity, $podUrl, $webId)) { + Log::warning('[IMPORT RESOURCES] Pod root lacks write permissions', [ + 'pod_url' => $podUrl, + 'webid' => $webId, + ]); + } + $result = $this->resourceSyncService->importResources($identity, $podUrl, $resourceTypes); return response()->json([ From 0b12e239f5f7dd9df8d874a2ae26a1e24a137624 Mon Sep 17 00:00:00 2001 From: Manus AI Date: Fri, 26 Dec 2025 04:41:20 -0500 Subject: [PATCH 4/4] Implement smart folder creation with permission checking - Add isWritable() method to check WAC-Allow permissions before operations - Add findWritableLocations() to discover writable storage locations - Check permissions before folder creation and resource import - Return helpful error messages with writable location suggestions - Improve parseContainerContents() to extract more resource metadata - Add extractResourceMetadata() to parse RDF types, titles, sizes, and timestamps Fixes: - Prevents 401 errors by checking permissions first - Works with read-only pod roots (e.g., solidcommunity.net) - Suggests alternative writable locations to users - Shows more complete resource listings in UI --- .../src/Http/Controllers/DataController.php | 53 ++++++-- server/src/Services/AclService.php | 115 ++++++++++++++++++ server/src/Services/PodService.php | 42 ++++++- 3 files changed, 201 insertions(+), 9 deletions(-) diff --git a/server/src/Http/Controllers/DataController.php b/server/src/Http/Controllers/DataController.php index 664fa08..93e323f 100644 --- a/server/src/Http/Controllers/DataController.php +++ b/server/src/Http/Controllers/DataController.php @@ -138,13 +138,33 @@ public function createFolder(Request $request) 'parent_url' => $parentUrl, ]); - // Ensure pod root has write permissions before creating folder + // Check if parent URL is writable before attempting folder creation $aclService = app(\Fleetbase\Solid\Services\AclService::class); - if (!$aclService->ensureWritePermissions($identity, $podUrl, $webId)) { - Log::warning('[FOLDER CREATE] Pod root lacks write permissions', [ - 'pod_url' => $podUrl, + + if (!$aclService->isWritable($identity, $parentUrl)) { + Log::warning('[FOLDER CREATE] Location not writable', [ + 'parent_url' => $parentUrl, 'webid' => $webId, ]); + + // Find writable locations + $writableLocations = $aclService->findWritableLocations($identity, $profile); + + if (!empty($writableLocations)) { + $suggestion = array_values($writableLocations)[0]; + return response()->json([ + 'success' => false, + 'error' => 'Cannot create folder at specified location. You do not have write permissions.', + 'suggestion' => "Try creating the folder at: {$suggestion}", + 'writable_locations' => $writableLocations, + ], 403); + } else { + return response()->json([ + 'success' => false, + 'error' => 'No writable locations found in your pod.', + 'help' => 'You may need to configure ACL permissions. See: https://docs.solidproject.org/managing-permissions', + ], 403); + } } // Use POST with Slug header (Solid Protocol standard) @@ -246,13 +266,32 @@ public function importResources(Request $request) 'resource_types' => $resourceTypes, ]); - // Ensure pod root has write permissions before importing + // Check if pod URL is writable before importing $aclService = app(\Fleetbase\Solid\Services\AclService::class); - if (!$aclService->ensureWritePermissions($identity, $podUrl, $webId)) { - Log::warning('[IMPORT RESOURCES] Pod root lacks write permissions', [ + + if (!$aclService->isWritable($identity, $podUrl)) { + Log::warning('[IMPORT RESOURCES] Pod root not writable', [ 'pod_url' => $podUrl, 'webid' => $webId, ]); + + // Find writable locations + $writableLocations = $aclService->findWritableLocations($identity, $profile); + + if (!empty($writableLocations)) { + return response()->json([ + 'success' => false, + 'error' => 'Cannot import resources to pod root. You do not have write permissions.', + 'writable_locations' => $writableLocations, + 'help' => 'Resources can only be imported to writable locations.', + ], 403); + } else { + return response()->json([ + 'success' => false, + 'error' => 'No writable locations found in your pod.', + 'help' => 'You may need to configure ACL permissions. See: https://docs.solidproject.org/managing-permissions', + ], 403); + } } $result = $this->resourceSyncService->importResources($identity, $podUrl, $resourceTypes); diff --git a/server/src/Services/AclService.php b/server/src/Services/AclService.php index 69b1755..8b64f67 100644 --- a/server/src/Services/AclService.php +++ b/server/src/Services/AclService.php @@ -289,3 +289,118 @@ protected function generateFolderAcl(string $folderUrl, string $webId): string TURTLE; } } + + /** + * Check if a specific URL is writable (has write or append permissions). + * + * @param SolidIdentity $identity + * @param string $url + * @return bool + */ + public function isWritable(SolidIdentity $identity, string $url): bool + { + try { + $response = $identity->request('head', $url); + + if (!$response->successful()) { + return false; + } + + $wacAllow = $response->header('WAC-Allow'); + + if ($wacAllow && preg_match('/user="([^"]*)"/i', $wacAllow, $matches)) { + $modes = strtolower($matches[1]); + $isWritable = str_contains($modes, 'write') || str_contains($modes, 'append'); + + Log::debug('[ACL] Writable check', [ + 'url' => $url, + 'wac_allow' => $wacAllow, + 'is_writable' => $isWritable, + ]); + + return $isWritable; + } + + return false; + } catch (\Throwable $e) { + Log::debug('[ACL] Writable check failed', [ + 'url' => $url, + 'error' => $e->getMessage(), + ]); + return false; + } + } + + /** + * Find writable storage locations from user profile. + * + * @param SolidIdentity $identity + * @param array $profile + * @return array + */ + public function findWritableLocations(SolidIdentity $identity, array $profile): array + { + $writableLocations = []; + $webId = $profile['webid'] ?? null; + + if (!$webId) { + return $writableLocations; + } + + // Get pod URL from WebID + $podUrl = $this->podService->getPodUrlFromWebId($webId); + + // Check common storage locations + $commonLocations = [ + 'public' => rtrim($podUrl, '/') . '/public/', + 'private' => rtrim($podUrl, '/') . '/private/', + 'inbox' => rtrim($podUrl, '/') . '/inbox/', + ]; + + foreach ($commonLocations as $name => $url) { + if ($this->isWritable($identity, $url)) { + $writableLocations[$name] = $url; + } + } + + // Check storage locations from profile + foreach ($profile['storage_locations'] ?? [] as $storage) { + $storageUrl = $this->resolveStorageUrl($storage, $webId, $podUrl); + if ($storageUrl && $this->isWritable($identity, $storageUrl)) { + $writableLocations['storage_' . count($writableLocations)] = $storageUrl; + } + } + + Log::info('[ACL] Found writable locations', [ + 'count' => count($writableLocations), + 'locations' => $writableLocations, + ]); + + return $writableLocations; + } + + /** + * Resolve a storage URL from profile data. + * + * @param string $storage + * @param string $webId + * @param string $podUrl + * @return string|null + */ + protected function resolveStorageUrl(string $storage, string $webId, string $podUrl): ?string + { + // Handle relative URLs + if ($storage === '../' || $storage === './') { + return $podUrl; + } + + // Handle absolute URLs + if (str_starts_with($storage, 'http://') || str_starts_with($storage, 'https://')) { + return $storage; + } + + // Handle relative paths + $webIdBase = dirname($webId); + return rtrim($webIdBase, '/') . '/' . ltrim($storage, '/'); + } +} diff --git a/server/src/Services/PodService.php b/server/src/Services/PodService.php index 95193bb..13357da 100644 --- a/server/src/Services/PodService.php +++ b/server/src/Services/PodService.php @@ -712,20 +712,58 @@ private function parseContainerContents(string $content): array { $items = []; - // Parse contained resources + // Parse contained resources with ldp:contains if (preg_match_all('/ldp:contains\s+<([^>]+)>/', $content, $matches)) { foreach ($matches[1] as $resourceUrl) { - $items[] = [ + $item = [ 'url' => $resourceUrl, 'name' => $this->extractPodName($resourceUrl), 'type' => substr($resourceUrl, -1) === '/' ? 'container' : 'resource', ]; + + // Try to extract additional metadata for this resource + $item = array_merge($item, $this->extractResourceMetadata($content, $resourceUrl)); + + $items[] = $item; } } return $items; } + /** + * Extract metadata for a specific resource from Turtle content. + */ + private function extractResourceMetadata(string $content, string $resourceUrl): array + { + $metadata = []; + + // Escape special regex characters in URL + $escapedUrl = preg_quote($resourceUrl, '/'); + + // Extract resource type (e.g., ldp:BasicContainer, foaf:Document) + if (preg_match('/<' . $escapedUrl . '>\s+a\s+([^;\s]+)/', $content, $matches)) { + $metadata['rdf_type'] = trim($matches[1]); + } + + // Extract dc:title + if (preg_match('/<' . $escapedUrl . '>.*?dc:title\s+"([^"]+)"/', $content, $matches)) { + $metadata['title'] = $matches[1]; + } + + // Extract dc:modified or posix:mtime + if (preg_match('/<' . $escapedUrl . '>.*?(?:dc:modified|posix:mtime)\s+(\d+)/', $content, $matches)) { + $metadata['modified'] = (int)$matches[1]; + } + + // Extract posix:size + if (preg_match('/<' . $escapedUrl . '>.*?posix:size\s+(\d+)/', $content, $matches)) { + $metadata['size'] = (int)$matches[1]; + } + + return $metadata; + } + /** * Generate pod metadata in Turtle format. */