@fleetbase/solid-engine 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +24 -0
  2. package/addon/components/explorer-header.hbs +18 -0
  3. package/addon/components/explorer-header.js +25 -0
  4. package/addon/components/modals/backup-pod.hbs +3 -0
  5. package/addon/components/modals/create-pod.hbs +5 -0
  6. package/addon/components/modals/resync-pod.hbs +3 -0
  7. package/addon/components/table/cell/pod-content-actions.hbs +32 -0
  8. package/addon/components/table/cell/pod-content-actions.js +73 -0
  9. package/addon/components/table/cell/pod-content-name.hbs +8 -0
  10. package/addon/components/table/cell/pod-content-name.js +16 -0
  11. package/addon/controllers/account.js +130 -0
  12. package/addon/controllers/application.js +16 -13
  13. package/addon/controllers/home.js +23 -0
  14. package/addon/controllers/pods/explorer/content.js +12 -0
  15. package/addon/controllers/pods/explorer.js +149 -0
  16. package/addon/controllers/pods/index/pod.js +12 -0
  17. package/addon/controllers/pods/index.js +137 -0
  18. package/addon/routes/account.js +3 -0
  19. package/addon/routes/home.js +3 -0
  20. package/addon/routes/pods/explorer/content.js +10 -0
  21. package/addon/routes/pods/explorer.js +44 -0
  22. package/addon/routes/pods/index/pod.js +3 -0
  23. package/addon/routes/pods/index.js +21 -0
  24. package/addon/routes.js +12 -1
  25. package/addon/services/explorer-state.js +101 -0
  26. package/addon/styles/solid-engine.css +46 -0
  27. package/addon/templates/account.hbs +42 -0
  28. package/addon/templates/application.hbs +17 -12
  29. package/addon/templates/home.hbs +11 -0
  30. package/addon/templates/pods/explorer/content.hbs +19 -0
  31. package/addon/templates/pods/explorer.hbs +20 -0
  32. package/addon/templates/pods/index/pod.hbs +11 -0
  33. package/addon/templates/pods/index.hbs +19 -0
  34. package/app/components/explorer-header.js +1 -0
  35. package/app/components/modals/backup-pod.js +1 -0
  36. package/app/components/modals/create-pod.js +1 -0
  37. package/app/components/modals/resync-pod.js +1 -0
  38. package/app/components/table/cell/pod-content-actions.js +1 -0
  39. package/app/components/table/cell/pod-content-name.js +1 -0
  40. package/app/controllers/account.js +1 -0
  41. package/app/controllers/home.js +1 -0
  42. package/app/controllers/pods/explorer/content.js +1 -0
  43. package/app/controllers/pods/explorer.js +1 -0
  44. package/app/controllers/pods/index/pod.js +1 -0
  45. package/app/controllers/pods/index.js +1 -0
  46. package/app/routes/account.js +1 -0
  47. package/app/routes/home.js +1 -0
  48. package/app/routes/pods/explorer/content.js +1 -0
  49. package/app/routes/pods/explorer.js +1 -0
  50. package/app/routes/pods/index/pod.js +1 -0
  51. package/app/routes/pods/index.js +1 -0
  52. package/app/services/explorer-state.js +1 -0
  53. package/app/templates/account.js +1 -0
  54. package/app/templates/home.js +1 -0
  55. package/app/templates/pods/explorer/content.js +1 -0
  56. package/app/templates/pods/explorer.js +1 -0
  57. package/app/templates/pods/index/pod.js +1 -0
  58. package/app/templates/pods/index.js +1 -0
  59. package/composer.json +9 -3
  60. package/extension.json +1 -1
  61. package/package.json +3 -3
  62. package/server/data/pods.json +328 -0
  63. package/server/src/Client/OpenIDConnectClient.php +2 -2
  64. package/server/src/Client/SolidClient.php +1 -1
  65. package/server/src/Http/Controllers/SolidController.php +57 -4
  66. package/server/src/LegacyClient/OIDCClient.php +1 -1
  67. package/server/src/LegacyClient/SolidClient.php +2 -1
  68. package/server/src/Support/Utils.php +38 -0
  69. package/server/src/routes.php +1 -0
package/README.md CHANGED
@@ -61,6 +61,7 @@ Solid, an innovative technology developed by Sir Tim Berners-Lee, offers a groun
61
61
  - Input their server
62
62
  - Input their solid ID on the server
63
63
  - Once installed, users signing up or added to the company within this instance can utilize Solid for data management.
64
+ - More info: The interface which allows the instance administrator to link the Fleetbase Solid extension to the Solid server of their preference. Due to the nature of how Solid is built on an identity basis, and Fleetbase is built as a multi-tenant platform. Each organization on Fleetbase when accessing the instance will be prompted to link their Solid OIDC account [https://github.com/fleetbase/solid/blob/main/addon/controllers/application.js#L16] to their Fleetbase organization per the instance. Once linked all data synced between Solid and Fleetbase will be via the SolidIdentity [https://github.com/fleetbase/solid/blob/main/server/src/Models/SolidIdentity.php]
64
65
 
65
66
  ### User Authentication and Account Creation:
66
67
 
@@ -69,8 +70,31 @@ Solid, an innovative technology developed by Sir Tim Berners-Lee, offers a groun
69
70
  - Authorize fleetbase.io to access your Pod.
70
71
  - Solid allows precise control over data access permissions. Note: The current UI version (node-solid-server V5.1) supports toggling global access permissions only. If you prefer granular control, uncheck all boxes and authorize. Then, manage permissions explicitly.
71
72
 
73
+ ### Fleetbase UI Updates
74
+
75
+ - As per the latest release, this is the updated UI screenshots for Fleetbase for users to manage pods.
76
+
77
+ - You can see the full release details here: https://github.com/fleetbase/solid/pull/2
78
+
79
+ - Install Solid Extension and click link: 'Sign up for an account' This will take you to Solid to create your own Solid Server & Pods,
80
+
81
+ ![image](https://github.com/fleetbase/solid/assets/58805033/e4cf882a-d04f-4abd-9107-e04cb0a47949)
82
+
83
+ - Once you head to this link, you can create your own Solid Server. You should be able to generate as per the Screenshot:
84
+
85
+ <img width="1134" alt="image" src="https://github.com/fleetbase/solid/assets/58805033/97015745-a1a6-487a-a958-fe97d0a7bca7">
86
+
87
+
88
+ - Input your Solid Server details directly into Fleetbase in company admin settings
89
+
90
+ ![image](https://github.com/fleetbase/solid/assets/58805033/dcfe2953-71d4-41c0-9243-36811b52017e)
91
+
92
+ Next steps would be to continue to update the UI from feedback and also conduct thorough testing and documentation. UI enhancements will be things like viewing the specific pods created as well as last synced.
93
+
72
94
  ### Features:
73
95
 
96
+ Fleetbase has implemented a Solid Client which implements the Standard Solid authentication methods to communicate with the server. The Fleetbase SolidClient is able to communicate securely with the Solid protocol using the Standard DPoP encryption method for authentication provided by the Solid specification (https://solidproject.org/TR/oidc#tokens-id)
97
+
74
98
  - Ability to link Fleetbase account with Solid Web ID later via user settings.
75
99
  - View and manage data stored on Solid Pod:
76
100
  - Orders
@@ -0,0 +1,18 @@
1
+ <div id="next-view-section-subheader" class="next-view-section-subheader {{if @hideActions 'actions-hidden'}}" ...attributes>
2
+ <div id="next-view-section-subheader-left" class="next-view-section-subheader-left {{@leftSubheaderClass}}">
3
+ <div class="flex flex-row items-center">
4
+ <FaIcon @icon="folder-tree" @size="sm" class="{{@iconClass}} text-gray-200 dark:text-gray-600 mr-2" />
5
+ <div class="pod-explorer-breadcrumb-container">
6
+ {{#each this.state as |content|}}
7
+ <a href="#" class="pod-explorer-breadcrumb {{if (eq @current content.id) "active-breadcrumb"}} {{@breadcrumbClass}}" {{on "click" (fn this.onStateClicked content)}}>{{content.name}}</a>
8
+ {{/each}}
9
+ </div>
10
+ </div>
11
+ </div>
12
+ <div id="next-view-section-subheader-actions" class="next-view-section-subheader-actions {{@actionsWrapperClass}}">
13
+ {{#if @onSearch}}
14
+ <Input @type="text" @value={{@searchQuery}} aria-label={{t "common.search-input"}} placeholder={{or @searchPlaceholder (concat (t "common.search") " " (pluralize @title))}} class="w-64 mr-2 form-input form-input-sm {{@searchInputClass}}" {{on "keyup" @onSearch}} />
15
+ {{/if}}
16
+ {{yield}}
17
+ </div>
18
+ </div>
@@ -0,0 +1,25 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { action } from '@ember/object';
5
+
6
+ export default class ExplorerHeaderComponent extends Component {
7
+ @service explorerState;
8
+ @tracked state = [];
9
+
10
+ constructor(owner, { pod }) {
11
+ super(...arguments);
12
+ this.state = this.explorerState.get(pod);
13
+ this.explorerState.on('change', (id, state) => {
14
+ if (id === pod) {
15
+ this.state = state;
16
+ }
17
+ });
18
+ }
19
+
20
+ @action onStateClicked(content) {
21
+ if (typeof this.args.onStateClicked === 'function') {
22
+ this.args.onStateClicked(content);
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,3 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container"></div>
3
+ </Modal::Default>
@@ -0,0 +1,5 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container">
3
+ <InputGroup @name="Pod Name" @value={{@options.pod.name}} @helpText="Input a name for your new Pod" />
4
+ </div>
5
+ </Modal::Default>
@@ -0,0 +1,3 @@
1
+ <Modal::Default @modalIsOpened={{@modalIsOpened}} @options={{@options}} @confirm={{@onConfirm}} @decline={{@onDecline}}>
2
+ <div class="modal-body-container"></div>
3
+ </Modal::Default>
@@ -0,0 +1,32 @@
1
+ <div class="cell-dropdown-button overflow-visible {{@column.wrapperClass}}" {{did-insert this.setupComponent}} ...attributes>
2
+ <DropdownButton @icon={{@column.ddButtonIcon}} @iconPrefix={{@column.ddButtonIconPrefix}} @text={{this.buttonText}} @size="xs" @horizontalPosition="left" @calculatePosition={{this.calculatePosition}} @renderInPlace={{true}} as |dd|>
3
+ <div class="next-dd-menu mt-0i" role="menu" aria-orientation="vertical" aria-labelledby="user-menu">
4
+ {{#if @column.ddMenuLabel}}
5
+ <div class="px-1">
6
+ <div class="text-sm flex flex-row items-center px-3 py-1 rounded-md my-1 text-gray-800 dark:text-gray-300">
7
+ {{@column.ddMenuLabel}}
8
+ </div>
9
+ </div>
10
+ <div class="next-dd-menu-seperator"></div>
11
+ {{/if}}
12
+ {{#each this.actions as |action|}}
13
+ {{#if action.separator}}
14
+ <div class="next-dd-menu-seperator"></div>
15
+ {{else}}
16
+ {{#if (is-dd-item-visible @row action.isVisible)}}
17
+ <div role="group" class="px-1">
18
+ <a href="javascript:;" role="menuitem" class="next-dd-item {{action.class}}" {{on "click" (fn this.onDropdownItemClick action @row dd)}}>
19
+ {{#if action.icon}}
20
+ <span class="mr-1">
21
+ <FaIcon class={{action.iconClass}} @icon={{action.icon}} @prefix={{action.iconPrefix}} />
22
+ </span>
23
+ {{/if}}
24
+ {{action.label}}
25
+ </a>
26
+ </div>
27
+ {{/if}}
28
+ {{/if}}
29
+ {{/each}}
30
+ </div>
31
+ </DropdownButton>
32
+ </div>
@@ -0,0 +1,73 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { action, computed } from '@ember/object';
4
+ import { isArray } from '@ember/array';
5
+
6
+ export default class TableCellPodContentActionsComponent extends Component {
7
+ constructor(owner, { column, row }) {
8
+ super(...arguments);
9
+ if (isArray(column.actions)) {
10
+ this.actions = column.actions;
11
+ }
12
+
13
+ if (typeof column.actions === 'function') {
14
+ this.actions = column.actions(row);
15
+ }
16
+ }
17
+
18
+ @tracked actions = [];
19
+ @tracked defaultButtonText = 'Actions';
20
+
21
+ @computed('args.column.ddButtonText', 'defaultButtonText') get buttonText() {
22
+ const { ddButtonText } = this.args.column;
23
+
24
+ if (ddButtonText === undefined) {
25
+ return this.defaultButtonText;
26
+ }
27
+
28
+ if (ddButtonText === false) {
29
+ return null;
30
+ }
31
+
32
+ return ddButtonText;
33
+ }
34
+
35
+ @action setupComponent(dropdownWrapperNode) {
36
+ const tableCellNode = this.getOwnerTableCell(dropdownWrapperNode);
37
+ tableCellNode.style.overflow = 'visible';
38
+ }
39
+
40
+ @action getOwnerTableCell(dropdownWrapperNode) {
41
+ while (dropdownWrapperNode) {
42
+ dropdownWrapperNode = dropdownWrapperNode.parentNode;
43
+
44
+ if (dropdownWrapperNode.tagName.toLowerCase() === 'td') {
45
+ return dropdownWrapperNode;
46
+ }
47
+ }
48
+
49
+ return undefined;
50
+ }
51
+
52
+ @action onDropdownItemClick(columnAction, row, dd) {
53
+ if (typeof dd?.actions?.close === 'function') {
54
+ dd.actions.close();
55
+ }
56
+
57
+ if (typeof columnAction?.fn === 'function') {
58
+ columnAction.fn(row);
59
+ }
60
+ }
61
+
62
+ @action calculatePosition(trigger) {
63
+ let { width } = trigger.getBoundingClientRect();
64
+
65
+ let style = {
66
+ marginTop: '0px',
67
+ right: width + 3,
68
+ top: 0,
69
+ };
70
+
71
+ return { style };
72
+ }
73
+ }
@@ -0,0 +1,8 @@
1
+ <a href="#" {{on "click" this.onClick}} class={{@column.anchorClass}} disabled={{not @value}} ...attributes>
2
+ {{#if (has-block)}}
3
+ {{yield}}
4
+ {{else}}
5
+ <FaIcon @icon={{@row.type}} @size="xs" class="mr-1" />
6
+ <span class={{@column.anchorSpanClass}}>{{or @value @column.anchorText "-"}}</span>
7
+ {{/if}}
8
+ </a>
@@ -0,0 +1,16 @@
1
+ import Component from '@glimmer/component';
2
+ import { action } from '@ember/object';
3
+
4
+ export default class TableCellPodContentNameComponent extends Component {
5
+ @action onClick() {
6
+ const { column, row } = this.args;
7
+
8
+ if (typeof column?.action === 'function') {
9
+ column.action(row);
10
+ }
11
+
12
+ if (typeof column?.onClick === 'function') {
13
+ column.onClick(row, ...arguments);
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,130 @@
1
+ import Controller from '@ember/controller';
2
+ import { inject as service } from '@ember/service';
3
+ import { action } from '@ember/object';
4
+ import { alias } from '@ember/object/computed';
5
+ import { task } from 'ember-concurrency';
6
+
7
+ export default class AccountController extends Controller {
8
+ /**
9
+ * Inject the `currentUser` service.
10
+ *
11
+ * @memberof ConsoleAccountIndexController
12
+ */
13
+ @service currentUser;
14
+
15
+ /**
16
+ * Inject the `fetch` service.
17
+ *
18
+ * @memberof ConsoleAccountIndexController
19
+ */
20
+ @service fetch;
21
+
22
+ /**
23
+ * Inject the `notifications` service.
24
+ *
25
+ * @memberof ConsoleAccountIndexController
26
+ */
27
+ @service notifications;
28
+
29
+ /**
30
+ * Inject the `modalsManager` service.
31
+ *
32
+ * @memberof ConsoleAccountIndexController
33
+ */
34
+ @service modalsManager;
35
+
36
+ /**
37
+ * Alias to the currentUser service user record.
38
+ *
39
+ * @memberof ConsoleAccountIndexController
40
+ */
41
+ @alias('currentUser.user') user;
42
+
43
+ /**
44
+ * Handle upload of new photo
45
+ *
46
+ * @param {UploadFile} file
47
+ * @memberof ConsoleAccountIndexController
48
+ */
49
+ @action uploadNewPhoto(file) {
50
+ return this.fetch.uploadFile.perform(
51
+ file,
52
+ {
53
+ path: `uploads/${this.user.company_uuid}/users/${this.user.slug}`,
54
+ subject_uuid: this.user.id,
55
+ subject_type: 'user',
56
+ type: 'user_avatar',
57
+ },
58
+ (uploadedFile) => {
59
+ this.user.setProperties({
60
+ avatar_uuid: uploadedFile.id,
61
+ avatar_url: uploadedFile.url,
62
+ });
63
+
64
+ return this.user.save();
65
+ }
66
+ );
67
+ }
68
+
69
+ /**
70
+ * Starts the task to change password
71
+ *
72
+ * @param {Event} event
73
+ * @memberof ConsoleAccountIndexController
74
+ */
75
+ @task *saveProfile(event) {
76
+ // If from event fired
77
+ if (event instanceof Event) {
78
+ event.preventDefault();
79
+ }
80
+
81
+ let canUpdateProfile = true;
82
+ // If email has been changed prompt for password validation
83
+ if (this.changedUserAttribute('email')) {
84
+ canUpdateProfile = yield this.validatePassword.perform();
85
+ }
86
+
87
+ if (canUpdateProfile === true) {
88
+ try {
89
+ const user = yield this.user.save();
90
+ this.notifications.success('Profile changes saved.');
91
+ this.currentUser.set('user', user);
92
+ } catch (error) {
93
+ this.notifications.serverError(error);
94
+ }
95
+ } else {
96
+ this.user.rollbackAttributes();
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Task to validate current password
102
+ *
103
+ * @return {boolean}
104
+ * @memberof ConsoleAccountIndexController
105
+ */
106
+ @task *validatePassword() {
107
+ let isPasswordValid = false;
108
+
109
+ yield this.modalsManager.show('modals/validate-password', {
110
+ body: 'You must validate your password to update the account email address.',
111
+ onValidated: (isValid) => {
112
+ isPasswordValid = isValid;
113
+ },
114
+ });
115
+
116
+ return isPasswordValid;
117
+ }
118
+
119
+ /**
120
+ * Checks if any user attribute has been changed
121
+ *
122
+ * @param {string} attributeKey
123
+ * @return {boolean}
124
+ * @memberof ConsoleAccountIndexController
125
+ */
126
+ changedUserAttribute(attributeKey) {
127
+ const changedAttributes = this.user.changedAttributes();
128
+ return changedAttributes[attributeKey] !== undefined;
129
+ }
130
+ }
@@ -1,27 +1,30 @@
1
1
  import Controller from '@ember/controller';
2
2
  import { inject as service } from '@ember/service';
3
- import { task } from 'ember-concurrency';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { task, timeout } from 'ember-concurrency';
4
5
 
5
6
  export default class ApplicationController extends Controller {
6
- @service universe;
7
7
  @service fetch;
8
+ @service appCache;
9
+ @tracked pods = [];
8
10
 
9
11
  constructor() {
10
12
  super(...arguments);
11
- this.universe.on('sidebarContext.available', (sidebarContext) => {
12
- sidebarContext.hideNow();
13
- });
13
+ this.getPods.perform();
14
14
  }
15
15
 
16
- @task *authenticate() {
17
- const { authenticationUrl, identifier } = yield this.fetch.get('request-authentication', {}, { namespace: 'solid/int/v1' });
18
- if (authenticationUrl) {
19
- window.location.href = `${authenticationUrl}/${identifier}`;
16
+ @task *getPods() {
17
+ yield timeout(600);
18
+
19
+ if (this.appCache.has('solid:pods')) {
20
+ this.pods = this.appCache.get('solid:pods', []);
21
+ return;
20
22
  }
21
- }
22
23
 
23
- @task *getAccountIndex() {
24
- const response = yield this.fetch.get('account', {}, { namespace: 'solid/int/v1' });
25
- console.log('[response]', response);
24
+ try {
25
+ this.pods = yield this.fetch.get('pods', {}, { namespace: 'solid/int/v1' });
26
+ } catch (error) {
27
+ // silence
28
+ }
26
29
  }
27
30
  }
@@ -0,0 +1,23 @@
1
+ import Controller from '@ember/controller';
2
+ import { inject as service } from '@ember/service';
3
+ import { task } from 'ember-concurrency';
4
+
5
+ export default class HomeController extends Controller {
6
+ @service fetch;
7
+ @service notifications;
8
+
9
+ @task *authenticate() {
10
+ try {
11
+ const { authenticationUrl, identifier } = yield this.fetch.get('request-authentication', {}, { namespace: 'solid/int/v1' });
12
+ if (authenticationUrl) {
13
+ window.location.href = `${authenticationUrl}/${identifier}`;
14
+ }
15
+ } catch (error) {
16
+ this.notifications.serverError(error);
17
+ }
18
+ }
19
+
20
+ @task *getAccountIndex() {
21
+ yield this.fetch.get('account', {}, { namespace: 'solid/int/v1' });
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ import Controller from '@ember/controller';
2
+ import { action } from '@ember/object';
3
+
4
+ export default class PodsExplorerContentController extends Controller {
5
+ @action setOverlayContext(overlayContextApi) {
6
+ this.overlayContextApi = overlayContextApi;
7
+ }
8
+
9
+ @action onPressClose() {
10
+ window.history.back();
11
+ }
12
+ }
@@ -0,0 +1,149 @@
1
+ import Controller from '@ember/controller';
2
+ import { action } from '@ember/object';
3
+ import { inject as service } from '@ember/service';
4
+ import { tracked } from '@glimmer/tracking';
5
+ import { task, timeout } from 'ember-concurrency';
6
+
7
+ export default class PodsExplorerController extends Controller {
8
+ @service hostRouter;
9
+ @service fetch;
10
+ @service notifications;
11
+ @service explorerState;
12
+ @service modalsManager;
13
+ @service crud;
14
+ @tracked cursor = '';
15
+ @tracked pod = '';
16
+ @tracked query = '';
17
+ queryParams = ['cursor', 'pod', 'query'];
18
+ columns = [
19
+ {
20
+ label: 'Name',
21
+ valuePath: 'name',
22
+ width: '75%',
23
+ cellComponent: 'table/cell/pod-content-name',
24
+ onClick: this.viewContents,
25
+ },
26
+ {
27
+ label: 'Type',
28
+ valuePath: 'type',
29
+ cellClassNames: 'capitalize',
30
+ width: '5%',
31
+ },
32
+ {
33
+ label: 'Size',
34
+ valuePath: 'size',
35
+ width: '5%',
36
+ },
37
+ {
38
+ label: 'Created At',
39
+ valuePath: 'created_at',
40
+ width: '15%',
41
+ },
42
+ {
43
+ label: '',
44
+ cellComponent: 'table/cell/pod-content-actions',
45
+ ddButtonText: false,
46
+ ddButtonIcon: 'ellipsis-h',
47
+ ddButtonIconPrefix: 'fas',
48
+ ddMenuLabel: 'Actions',
49
+ cellClassNames: 'overflow-visible',
50
+ wrapperClass: 'flex items-center justify-end mx-2',
51
+ width: '10%',
52
+ actions: (content) => {
53
+ return [
54
+ {
55
+ label: content.type === 'folder' ? 'Browse Folder' : 'View Contents',
56
+ fn: this.viewContents,
57
+ },
58
+ {
59
+ separator: true,
60
+ },
61
+ {
62
+ label: 'Delete',
63
+ fn: this.deleteSomething,
64
+ },
65
+ ];
66
+ },
67
+ sortable: false,
68
+ filterable: false,
69
+ resizable: false,
70
+ searchable: false,
71
+ },
72
+ ];
73
+
74
+ @action reload() {
75
+ this.hostRouter.refresh();
76
+ }
77
+
78
+ @action back() {
79
+ if (typeof this.cursor === 'string' && this.cursor.length && this.cursor !== this.model.id) {
80
+ const current = this.reverseCursor();
81
+ return this.hostRouter.transitionTo('console.solid-protocol.pods.explorer', current, { queryParams: { cursor: this.cursor, pod: this.pod } });
82
+ }
83
+
84
+ this.hostRouter.transitionTo('console.solid-protocol.pods.index');
85
+ }
86
+
87
+ @action viewContents(content) {
88
+ if (content.type === 'folder') {
89
+ return this.hostRouter.transitionTo('console.solid-protocol.pods.explorer', content, { queryParams: { cursor: this.trackCursor(content), pod: this.pod } });
90
+ }
91
+
92
+ if (content.type === 'file') {
93
+ return this.hostRouter.transitionTo('console.solid-protocol.pods.explorer.content', content);
94
+ }
95
+
96
+ return this.hostRouter.transitionTo('console.solid-protocol.pods.explorer', this.pod, { queryParams: { cursor: this.trackCursor(content), pod: this.pod } });
97
+ }
98
+
99
+ @action deleteSomething() {
100
+ this.modalsManager.confirm({
101
+ title: 'Are you sure you want to delete this content?',
102
+ body: 'Deleting this Content will remove this content from this pod. This is irreversible!',
103
+ acceptButtonText: 'Delete Forever',
104
+ confirm: () => {},
105
+ });
106
+ }
107
+
108
+ @action deleteSelected() {
109
+ const selected = this.table.selectedRows;
110
+
111
+ this.crud.bulkDelete(selected, {
112
+ modelNamePath: 'name',
113
+ acceptButtonText: 'Delete All',
114
+ onSuccess: () => {
115
+ return this.hostRouter.refresh();
116
+ },
117
+ });
118
+ }
119
+
120
+ trackCursor(content) {
121
+ if (typeof this.cursor === 'string' && this.cursor.includes(content.id)) {
122
+ const segments = this.cursor.split(':');
123
+ const currentIndex = segments.findIndex((segment) => segment === content.id);
124
+
125
+ if (currentIndex > -1) {
126
+ const retainedSegments = segments.slice(0, currentIndex + 1);
127
+ this.cursor = retainedSegments.join(':');
128
+ return this.cursor;
129
+ }
130
+ }
131
+
132
+ this.cursor = this.cursor ? `${this.cursor}:${content.id}` : content.id;
133
+ return this.cursor;
134
+ }
135
+
136
+ reverseCursor() {
137
+ const segments = this.cursor.split(':');
138
+ segments.pop();
139
+ const current = segments[segments.length - 1];
140
+ this.cursor = segments.join(':');
141
+ return current;
142
+ }
143
+
144
+ @task({ restartable: true }) *search(event) {
145
+ yield timeout(300);
146
+ const query = typeof event.target.value === 'string' ? event.target.value : '';
147
+ this.hostRouter.transitionTo('console.solid-protocol.pods.explorer', this.model.id, { queryParams: { cursor: this.cursor, query } });
148
+ }
149
+ }
@@ -0,0 +1,12 @@
1
+ import Controller from '@ember/controller';
2
+ import { action } from '@ember/object';
3
+
4
+ export default class PodsIndexPodController extends Controller {
5
+ @action setOverlayContext(overlayContextApi) {
6
+ this.overlayContextApi = overlayContextApi;
7
+ }
8
+
9
+ @action onPressClose() {
10
+ window.history.back();
11
+ }
12
+ }