@fleetbase/solid-engine 0.0.4 → 0.0.5
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.
- package/ACL_SOLUTION.md +72 -0
- package/CSS_SCOPE_ISSUE.md +140 -0
- package/HOTFIX_SYNTAX_ERROR.md +100 -0
- package/MANUAL_ACL_SETUP.md +135 -0
- package/REFACTORING_SUMMARY.md +330 -0
- package/VERIFICATION_CHECKLIST.md +82 -0
- package/addon/components/modals/create-solid-folder.hbs +29 -0
- package/addon/components/modals/import-solid-resources.hbs +85 -0
- package/addon/controllers/data/content.js +17 -0
- package/addon/controllers/data/index.js +219 -0
- package/addon/controllers/home.js +84 -0
- package/addon/engine.js +1 -24
- package/addon/extension.js +26 -0
- package/addon/routes/data/content.js +11 -0
- package/addon/routes/data/index.js +17 -0
- package/addon/routes.js +2 -7
- package/addon/styles/solid-engine.css +1 -2
- package/addon/templates/account.hbs +3 -3
- package/addon/templates/application.hbs +2 -12
- package/addon/templates/data/content.hbs +48 -0
- package/addon/templates/{pods/explorer.hbs → data/index.hbs} +6 -5
- package/addon/templates/home.hbs +168 -10
- package/app/components/modals/{backup-pod.js → create-solid-folder.js} +1 -1
- package/app/components/modals/{resync-pod.js → import-solid-resources.js} +1 -1
- package/app/components/modals/{create-pod.js → setup-css-credentials.js} +1 -1
- package/composer.json +4 -10
- package/extension.json +1 -1
- package/index.js +0 -11
- package/package.json +8 -8
- package/server/migrations/2024_12_21_add_css_credentials_to_solid_identities_table.php +32 -0
- package/server/src/Client/OpenIDConnectClient.php +686 -15
- package/server/src/Client/SolidClient.php +104 -8
- package/server/src/Http/Controllers/DataController.php +261 -0
- package/server/src/Http/Controllers/OIDCController.php +42 -8
- package/server/src/Http/Controllers/SolidController.php +179 -85
- package/server/src/Models/SolidIdentity.php +13 -3
- package/server/src/Services/AclService.php +146 -0
- package/server/src/Services/PodService.php +863 -0
- package/server/src/Services/ResourceSyncService.php +336 -0
- package/server/src/Services/VehicleSyncService.php +289 -0
- package/server/src/Support/Utils.php +10 -0
- package/server/src/routes.php +25 -1
- package/addon/components/modals/backup-pod.hbs +0 -3
- package/addon/components/modals/create-pod.hbs +0 -5
- package/addon/components/modals/resync-pod.hbs +0 -3
- package/addon/controllers/pods/explorer/content.js +0 -12
- package/addon/controllers/pods/explorer.js +0 -149
- package/addon/controllers/pods/index/pod.js +0 -12
- package/addon/controllers/pods/index.js +0 -137
- package/addon/routes/pods/explorer/content.js +0 -10
- package/addon/routes/pods/explorer.js +0 -44
- package/addon/routes/pods/index/pod.js +0 -3
- package/addon/routes/pods/index.js +0 -21
- package/addon/templates/pods/explorer/content.hbs +0 -19
- package/addon/templates/pods/index/pod.hbs +0 -11
- package/addon/templates/pods/index.hbs +0 -19
- package/server/src/LegacyClient/Identity/IdentityProvider.php +0 -174
- package/server/src/LegacyClient/Identity/Profile.php +0 -18
- package/server/src/LegacyClient/OIDCClient.php +0 -350
- package/server/src/LegacyClient/Profile/WebID.php +0 -26
- package/server/src/LegacyClient/SolidClient.php +0 -271
package/addon/templates/home.hbs
CHANGED
|
@@ -1,11 +1,169 @@
|
|
|
1
|
-
<Layout::Section::Header @title="Solid for Fleetbase"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<Layout::Section::Header @title="Solid for Fleetbase">
|
|
2
|
+
{{#if this.isAuthenticated}}
|
|
3
|
+
<Button @icon="refresh" @onClick={{perform this.refreshStatus}} @helpText="Refresh Status" @isLoading={{this.refreshStatus.isRunning}} class="mr-2" />
|
|
4
|
+
<Button @icon="sign-out-alt" @onClick={{perform this.logout}} @helpText="Logout" @type="danger" @isLoading={{this.logout.isRunning}} @disabled={{or this.refreshStatus.isRunning this.checkAuthenticationStatus.isRunning}} />
|
|
5
|
+
{{/if}}
|
|
6
|
+
</Layout::Section::Header>
|
|
7
|
+
|
|
8
|
+
<Layout::Section::Body class="solid-fleetbase-home-container overflow-y-scroll h-full">
|
|
9
|
+
{{#if this.checkAuthenticationStatus.isRunning}}
|
|
10
|
+
<div class="flex items-center justify-center h-64">
|
|
11
|
+
<div class="text-center">
|
|
12
|
+
<Spinner class="mb-4" />
|
|
13
|
+
<p class="text-gray-600 dark:text-gray-400">Checking authentication status...</p>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
{{else}}
|
|
17
|
+
{{#if this.isAuthenticated}}
|
|
18
|
+
<div class="max-w-4xl mx-auto space-y-4">
|
|
19
|
+
<div class="pb-4">
|
|
20
|
+
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">Welcome back!</h1>
|
|
21
|
+
<p class="text-gray-600 dark:text-gray-400">You are successfully connected to your Solid storage.</p>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<ContentPanel @title="Your Solid Identity" @open={{true}} @wrapperClass="bordered-classic">
|
|
25
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
26
|
+
<div>
|
|
27
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Profile Information</h3>
|
|
28
|
+
<div class="space-y-3">
|
|
29
|
+
<div>
|
|
30
|
+
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Name</label>
|
|
31
|
+
<p class="text-gray-900 dark:text-white">{{this.userName}}</p>
|
|
32
|
+
</div>
|
|
33
|
+
<div>
|
|
34
|
+
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Email</label>
|
|
35
|
+
<p class="text-gray-900 dark:text-white">{{this.userEmail}}</p>
|
|
36
|
+
</div>
|
|
37
|
+
<div>
|
|
38
|
+
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">WebID</label>
|
|
39
|
+
<p class="text-sm text-blue-600 dark:text-blue-400 break-all">{{this.webId}}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
<div>
|
|
44
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-4">Storage Information</h3>
|
|
45
|
+
{{#if this.hasStorageLocations}}
|
|
46
|
+
<div class="space-y-2">
|
|
47
|
+
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Storage Locations</label>
|
|
48
|
+
{{#each this.storageLocations as |location|}}
|
|
49
|
+
<div class="bg-gray-50 dark:bg-gray-700 p-2 rounded text-sm">
|
|
50
|
+
<p class="text-blue-600 dark:text-blue-400 break-all">{{location}}</p>
|
|
51
|
+
</div>
|
|
52
|
+
{{/each}}
|
|
53
|
+
</div>
|
|
54
|
+
{{else}}
|
|
55
|
+
<p class="text-gray-600 dark:text-gray-400">No storage locations found</p>
|
|
56
|
+
{{/if}}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</ContentPanel>
|
|
60
|
+
|
|
61
|
+
<ContentPanel @title="Quick Actions" @open={{true}} @wrapperClass="bordered-classic">
|
|
62
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
63
|
+
<div class="text-center p-6 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
|
64
|
+
<FaIcon @icon="folder-tree" @size="2x" class="text-blue-600 dark:text-blue-400 mb-3" />
|
|
65
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Browse Data</h3>
|
|
66
|
+
<p class="text-gray-600 dark:text-gray-400 mb-4">Explore and manage your Fleetops data in Solid</p>
|
|
67
|
+
<div class="flex items-center justify-center">
|
|
68
|
+
<Button @text="Browse Data" @icon="database" @type="primary" class="w-full" @onClick={{perform this.navigateToPods}} />
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<div class="text-center p-6 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
|
72
|
+
<FaIcon @icon="user" @size="2x" class="text-green-600 dark:text-green-400 mb-3" />
|
|
73
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Account Settings</h3>
|
|
74
|
+
<p class="text-gray-600 dark:text-gray-400 mb-4">Manage your account and profile settings</p>
|
|
75
|
+
<div class="flex items-center justify-center">
|
|
76
|
+
<Button @text="Account" @icon="user" @type="secondary" class="w-full" @onClick={{perform this.navigateToAccount}} />
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="text-center p-6 bg-gray-50 dark:bg-gray-700 rounded-lg">
|
|
80
|
+
<FaIcon @icon="sync" @size="2x" class="text-purple-600 dark:text-purple-400 mb-3" />
|
|
81
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">Sync Data</h3>
|
|
82
|
+
<p class="text-gray-600 dark:text-gray-400 mb-4">Sync your Fleetbase data to Solid storage</p>
|
|
83
|
+
<div class="flex items-center justify-center">
|
|
84
|
+
<Button @text="Coming Soon" @icon="sync" @disabled={{true}} />
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</ContentPanel>
|
|
89
|
+
|
|
90
|
+
<ContentPanel @title="Connection Status" @open={{true}} @wrapperClass="bordered-classic">
|
|
91
|
+
<div class="space-y-3">
|
|
92
|
+
<div class="flex items-center">
|
|
93
|
+
<div class="w-3 h-3 bg-green-500 rounded-full mr-3"></div>
|
|
94
|
+
<span class="text-gray-900 dark:text-white">Connected to Solid server</span>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="flex items-center">
|
|
97
|
+
<div class="w-3 h-3 bg-green-500 rounded-full mr-3"></div>
|
|
98
|
+
<span class="text-gray-900 dark:text-white">Authentication active</span>
|
|
99
|
+
</div>
|
|
100
|
+
{{#if this.hasStorageLocations}}
|
|
101
|
+
<div class="flex items-center">
|
|
102
|
+
<div class="w-3 h-3 bg-green-500 rounded-full mr-3"></div>
|
|
103
|
+
<span class="text-gray-900 dark:text-white">{{this.storageLocations.length}} storage location(s) available</span>
|
|
104
|
+
</div>
|
|
105
|
+
{{/if}}
|
|
106
|
+
</div>
|
|
107
|
+
</ContentPanel>
|
|
108
|
+
</div>
|
|
109
|
+
{{else}}
|
|
110
|
+
<div class="max-w-2xl mx-auto text-center">
|
|
111
|
+
<div class="mb-8">
|
|
112
|
+
<FaIcon @icon="shield-alt" @size="4x" class="text-gray-400 dark:text-gray-600 mb-4" />
|
|
113
|
+
<h1 class="text-3xl font-bold text-gray-900 dark:text-white mb-4">Welcome to Solid for Fleetbase</h1>
|
|
114
|
+
<p class="text-lg text-gray-600 dark:text-gray-400 mb-8">
|
|
115
|
+
Connect your Fleetbase account with Solid to manage your data in a decentralized way.
|
|
116
|
+
</p>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<ContentPanel @title="Getting Started" @open={{true}} @wrapperClass="bordered-classic">
|
|
120
|
+
<div class="text-left space-y-4">
|
|
121
|
+
<div class="flex items-start">
|
|
122
|
+
<div class="flex-shrink-0 w-8 h-8 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center mr-4">
|
|
123
|
+
<span class="text-blue-600 dark:text-blue-400 font-semibold">1</span>
|
|
124
|
+
</div>
|
|
125
|
+
<div>
|
|
126
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Sign up for a Solid account</h3>
|
|
127
|
+
<p class="text-gray-600 dark:text-gray-400">Create your Solid account and WebID to get started with decentralized data storage.</p>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="flex items-start">
|
|
131
|
+
<div class="flex-shrink-0 w-8 h-8 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center mr-4">
|
|
132
|
+
<span class="text-blue-600 dark:text-blue-400 font-semibold">2</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Connect your account</h3>
|
|
136
|
+
<p class="text-gray-600 dark:text-gray-400">Authenticate with your Solid provider to enable data synchronization.</p>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
<div class="flex items-start">
|
|
140
|
+
<div class="flex-shrink-0 w-8 h-8 bg-blue-100 dark:bg-blue-900 rounded-full flex items-center justify-center mr-4">
|
|
141
|
+
<span class="text-blue-600 dark:text-blue-400 font-semibold">3</span>
|
|
142
|
+
</div>
|
|
143
|
+
<div>
|
|
144
|
+
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Sync your data</h3>
|
|
145
|
+
<p class="text-gray-600 dark:text-gray-400">Sync vehicles, drivers, and orders from Fleetbase to your Solid storage.</p>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="mt-8 text-center">
|
|
150
|
+
<Button @text="Connect to Solid" @icon="link" @type="primary" @size="lg" @onClick={{perform this.authenticate}} @isLoading={{this.authenticate.isRunning}} />
|
|
151
|
+
</div>
|
|
152
|
+
</ContentPanel>
|
|
153
|
+
|
|
154
|
+
{{#if this.authStatus.error}}
|
|
155
|
+
<div class="bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-700 rounded-lg p-4 mt-4">
|
|
156
|
+
<div class="flex">
|
|
157
|
+
<FaIcon @icon="exclamation-triangle" class="text-red-400 mr-3 mt-1" />
|
|
158
|
+
<div>
|
|
159
|
+
<h3 class="text-sm font-medium text-red-800 dark:text-red-200">Authentication Error</h3>
|
|
160
|
+
<p class="text-sm text-red-700 dark:text-red-300 mt-1">{{this.authStatus.error}}</p>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
{{/if}}
|
|
165
|
+
</div>
|
|
166
|
+
{{/if}}
|
|
167
|
+
{{/if}}
|
|
168
|
+
<Spacer @height="200px" />
|
|
11
169
|
</Layout::Section::Body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from '@fleetbase/solid-engine/components/modals/
|
|
1
|
+
export { default } from '@fleetbase/solid-engine/components/modals/create-solid-folder';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from '@fleetbase/solid-engine/components/modals/
|
|
1
|
+
export { default } from '@fleetbase/solid-engine/components/modals/import-solid-resources';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default } from '@fleetbase/solid-engine/components/modals/
|
|
1
|
+
export { default } from '@fleetbase/solid-engine/components/modals/setup-css-credentials';
|
package/composer.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fleetbase/solid-api",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Solid Protocol Extension to Store and Share Data with Fleetbase",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fleetbase-extension",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
],
|
|
29
29
|
"require": {
|
|
30
30
|
"php": "^8.0",
|
|
31
|
-
"fleetbase/core-api": "
|
|
32
|
-
"fleetbase/fleetops-api": "
|
|
31
|
+
"fleetbase/core-api": "*",
|
|
32
|
+
"fleetbase/fleetops-api": "*",
|
|
33
33
|
"php-http/guzzle7-adapter": "^1.0",
|
|
34
34
|
"psr/http-factory-implementation": "*",
|
|
35
|
-
"jumbojett/openid-connect-php": "^0.
|
|
35
|
+
"jumbojett/openid-connect-php": "^1.0.2",
|
|
36
36
|
"easyrdf/easyrdf": "^1.1",
|
|
37
37
|
"ml/json-ld": "^1.2",
|
|
38
38
|
"web-token/jwt-core": "^3.0",
|
|
@@ -50,12 +50,6 @@
|
|
|
50
50
|
"phpstan/phpstan": "^1.10.38",
|
|
51
51
|
"symfony/var-dumper": "^5.4.29"
|
|
52
52
|
},
|
|
53
|
-
"repositories": [
|
|
54
|
-
{
|
|
55
|
-
"type": "vcs",
|
|
56
|
-
"url": "https://github.com/fleetbase/laravel-model-caching"
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
53
|
"autoload": {
|
|
60
54
|
"psr-4": {
|
|
61
55
|
"Fleetbase\\Solid\\": "server/src/",
|
package/extension.json
CHANGED
package/index.js
CHANGED
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const { buildEngine } = require('ember-engines/lib/engine-addon');
|
|
3
3
|
const { name } = require('./package');
|
|
4
|
-
const Funnel = require('broccoli-funnel');
|
|
5
4
|
|
|
6
5
|
module.exports = buildEngine({
|
|
7
6
|
name,
|
|
8
7
|
|
|
9
|
-
postprocessTree(type, tree) {
|
|
10
|
-
if (type === 'css') {
|
|
11
|
-
tree = new Funnel(tree, {
|
|
12
|
-
exclude: ['**/@fleetbase/ember-ui/**/*.css'],
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return tree;
|
|
17
|
-
},
|
|
18
|
-
|
|
19
8
|
lazyLoading: {
|
|
20
9
|
enabled: true,
|
|
21
10
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fleetbase/solid-engine",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Solid Protocol Extension to Store and Share Data with Fleetbase",
|
|
5
5
|
"fleetbase": {
|
|
6
6
|
"route": "solid-protocol"
|
|
@@ -45,13 +45,14 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@babel/core": "^7.23.2",
|
|
48
|
-
"@fleetbase/ember-core": "^0.
|
|
49
|
-
"@fleetbase/ember-ui": "^0.
|
|
50
|
-
"@fleetbase/fleetops-data": "^0.1.
|
|
51
|
-
"@fortawesome/ember-fontawesome": "^0.
|
|
52
|
-
"@fortawesome/fontawesome-svg-core": "
|
|
53
|
-
"@fortawesome/free-solid-svg-icons": "
|
|
48
|
+
"@fleetbase/ember-core": "^0.3.9",
|
|
49
|
+
"@fleetbase/ember-ui": "^0.3.15",
|
|
50
|
+
"@fleetbase/fleetops-data": "^0.1.24",
|
|
51
|
+
"@fortawesome/ember-fontawesome": "^2.0.0",
|
|
52
|
+
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
|
53
|
+
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
|
54
54
|
"broccoli-funnel": "^3.0.8",
|
|
55
|
+
"ember-auto-import": "^2.7.4",
|
|
55
56
|
"ember-cli-babel": "^8.2.0",
|
|
56
57
|
"ember-cli-htmlbars": "^6.3.0",
|
|
57
58
|
"ember-intl": "6.3.2",
|
|
@@ -68,7 +69,6 @@
|
|
|
68
69
|
"@glimmer/tracking": "^1.1.2",
|
|
69
70
|
"broccoli-asset-rev": "^3.0.0",
|
|
70
71
|
"concurrently": "^8.2.2",
|
|
71
|
-
"ember-auto-import": "^2.6.3",
|
|
72
72
|
"ember-cli": "~5.4.1",
|
|
73
73
|
"ember-cli-clean-css": "^3.0.0",
|
|
74
74
|
"ember-cli-dependency-checker": "^3.3.2",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
use Illuminate\Database\Migrations\Migration;
|
|
4
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
5
|
+
use Illuminate\Support\Facades\Schema;
|
|
6
|
+
|
|
7
|
+
return new class extends Migration
|
|
8
|
+
{
|
|
9
|
+
/**
|
|
10
|
+
* Run the migrations.
|
|
11
|
+
*/
|
|
12
|
+
public function up(): void
|
|
13
|
+
{
|
|
14
|
+
Schema::table('solid_identities', function (Blueprint $table) {
|
|
15
|
+
$table->string('css_email')->nullable()->after('identifier');
|
|
16
|
+
$table->text('css_password')->nullable()->after('css_email');
|
|
17
|
+
$table->string('css_client_id')->nullable()->after('css_password');
|
|
18
|
+
$table->text('css_client_secret')->nullable()->after('css_client_id');
|
|
19
|
+
$table->string('css_client_resource_url')->nullable()->after('css_client_secret');
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Reverse the migrations.
|
|
25
|
+
*/
|
|
26
|
+
public function down(): void
|
|
27
|
+
{
|
|
28
|
+
Schema::table('solid_identities', function (Blueprint $table) {
|
|
29
|
+
$table->dropColumn(['css_email', 'css_password', 'css_client_id', 'css_client_secret', 'css_client_resource_url']);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
};
|