@pinelab/vendure-plugin-sendcloud 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.
- package/README.md +87 -0
- package/dist/util/src/index.d.ts +1 -0
- package/dist/util/src/index.js +17 -0
- package/dist/util/src/order-state-util.d.ts +19 -0
- package/dist/util/src/order-state-util.js +62 -0
- package/dist/util/src/raw-body.d.ts +6 -0
- package/dist/util/src/raw-body.js +26 -0
- package/dist/vendure-plugin-sendcloud/src/api/additional-parcel-input-items.d.ts +10 -0
- package/dist/vendure-plugin-sendcloud/src/api/additional-parcel-input-items.js +47 -0
- package/dist/vendure-plugin-sendcloud/src/api/constants.d.ts +2 -0
- package/dist/vendure-plugin-sendcloud/src/api/constants.js +5 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud-config.entity.d.ts +8 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud-config.entity.js +40 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.adapter.d.ts +11 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.adapter.js +62 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.client.d.ts +18 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.client.js +59 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.controller.d.ts +8 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.controller.js +65 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.handler.d.ts +7 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.handler.js +28 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.resolver.d.ts +16 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.resolver.js +75 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.service.d.ts +48 -0
- package/dist/vendure-plugin-sendcloud/src/api/sendcloud.service.js +245 -0
- package/dist/vendure-plugin-sendcloud/src/api/types/sendcloud-api.types.d.ts +82 -0
- package/dist/vendure-plugin-sendcloud/src/api/types/sendcloud-api.types.js +2 -0
- package/dist/vendure-plugin-sendcloud/src/api/types/sendcloud.types.d.ts +41 -0
- package/dist/vendure-plugin-sendcloud/src/api/types/sendcloud.types.js +157 -0
- package/dist/vendure-plugin-sendcloud/src/index.d.ts +5 -0
- package/dist/vendure-plugin-sendcloud/src/index.js +21 -0
- package/dist/vendure-plugin-sendcloud/src/sendcloud.plugin.d.ts +7 -0
- package/dist/vendure-plugin-sendcloud/src/sendcloud.plugin.js +94 -0
- package/dist/vendure-plugin-sendcloud/src/ui/history-entry.component.ts +69 -0
- package/dist/vendure-plugin-sendcloud/src/ui/queries.ts +23 -0
- package/dist/vendure-plugin-sendcloud/src/ui/sendcloud-nav.module.ts +29 -0
- package/dist/vendure-plugin-sendcloud/src/ui/sendcloud.component.ts +112 -0
- package/dist/vendure-plugin-sendcloud/src/ui/sendcloud.module.ts +21 -0
- package/package.json +40 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
+
};
|
|
11
|
+
var SendcloudPlugin_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SendcloudPlugin = void 0;
|
|
14
|
+
const core_1 = require("@vendure/core");
|
|
15
|
+
const apollo_server_core_1 = require("apollo-server-core");
|
|
16
|
+
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const sendcloud_resolver_1 = require("./api/sendcloud.resolver");
|
|
18
|
+
const sendcloud_service_1 = require("./api/sendcloud.service");
|
|
19
|
+
const constants_1 = require("./api/constants");
|
|
20
|
+
const sendcloud_controller_1 = require("./api/sendcloud.controller");
|
|
21
|
+
const sendcloud_config_entity_1 = require("./api/sendcloud-config.entity");
|
|
22
|
+
const sendcloud_handler_1 = require("./api/sendcloud.handler");
|
|
23
|
+
const raw_body_1 = require("../../util/src/raw-body");
|
|
24
|
+
let SendcloudPlugin = SendcloudPlugin_1 = class SendcloudPlugin {
|
|
25
|
+
static init(options) {
|
|
26
|
+
this.options = options;
|
|
27
|
+
return SendcloudPlugin_1;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
SendcloudPlugin.ui = {
|
|
31
|
+
extensionPath: path_1.default.join(__dirname, 'ui'),
|
|
32
|
+
ngModules: [
|
|
33
|
+
{
|
|
34
|
+
type: 'lazy',
|
|
35
|
+
route: 'sendcloud',
|
|
36
|
+
ngModuleFileName: 'sendcloud.module.ts',
|
|
37
|
+
ngModuleName: 'SendcloudModule',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: 'shared',
|
|
41
|
+
ngModuleFileName: 'sendcloud-nav.module.ts',
|
|
42
|
+
ngModuleName: 'SendcloudNavModule',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
SendcloudPlugin = SendcloudPlugin_1 = __decorate([
|
|
47
|
+
(0, core_1.VendurePlugin)({
|
|
48
|
+
adminApiExtensions: {
|
|
49
|
+
schema: (0, apollo_server_core_1.gql) `
|
|
50
|
+
extend enum HistoryEntryType {
|
|
51
|
+
SENDCLOUD_NOTIFICATION
|
|
52
|
+
}
|
|
53
|
+
type SendCloudConfig {
|
|
54
|
+
id: ID!
|
|
55
|
+
secret: String
|
|
56
|
+
publicKey: String
|
|
57
|
+
defaultPhoneNr: String
|
|
58
|
+
}
|
|
59
|
+
input SendCloudConfigInput {
|
|
60
|
+
secret: String
|
|
61
|
+
publicKey: String
|
|
62
|
+
defaultPhoneNr: String
|
|
63
|
+
}
|
|
64
|
+
extend type Mutation {
|
|
65
|
+
sendToSendCloud(orderId: ID!): Boolean!
|
|
66
|
+
updateSendCloudConfig(input: SendCloudConfigInput): SendCloudConfig!
|
|
67
|
+
}
|
|
68
|
+
extend type Query {
|
|
69
|
+
sendCloudConfig: SendCloudConfig
|
|
70
|
+
}
|
|
71
|
+
`,
|
|
72
|
+
resolvers: [sendcloud_resolver_1.SendcloudResolver],
|
|
73
|
+
},
|
|
74
|
+
providers: [
|
|
75
|
+
sendcloud_service_1.SendcloudService,
|
|
76
|
+
{
|
|
77
|
+
provide: constants_1.PLUGIN_OPTIONS,
|
|
78
|
+
useFactory: () => SendcloudPlugin_1.options,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
imports: [core_1.PluginCommonModule],
|
|
82
|
+
controllers: [sendcloud_controller_1.SendcloudController],
|
|
83
|
+
entities: [sendcloud_config_entity_1.SendcloudConfigEntity],
|
|
84
|
+
configuration: (config) => {
|
|
85
|
+
config.shippingOptions.fulfillmentHandlers.push(sendcloud_handler_1.sendcloudHandler);
|
|
86
|
+
config.authOptions.customPermissions.push(sendcloud_resolver_1.sendcloudPermission);
|
|
87
|
+
// save rawBody for signature verification
|
|
88
|
+
config.apiOptions.middleware.push((0, raw_body_1.createRawBodyMiddleWare)('/sendcloud/webhook*'));
|
|
89
|
+
return config;
|
|
90
|
+
},
|
|
91
|
+
compatibility: '^2.0.0',
|
|
92
|
+
})
|
|
93
|
+
], SendcloudPlugin);
|
|
94
|
+
exports.SendcloudPlugin = SendcloudPlugin;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Component } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
DataService,
|
|
4
|
+
NotificationService,
|
|
5
|
+
OrderDetailFragment,
|
|
6
|
+
OrderHistoryEntryComponent,
|
|
7
|
+
TimelineDisplayType,
|
|
8
|
+
TimelineHistoryEntry,
|
|
9
|
+
} from '@vendure/admin-ui/core';
|
|
10
|
+
import gql from 'graphql-tag';
|
|
11
|
+
|
|
12
|
+
@Component({
|
|
13
|
+
selector: 'sendcloud-notification-component',
|
|
14
|
+
template: `
|
|
15
|
+
<span *ngIf="entry.data.valid"> Synced to {{ getName(entry) }} </span>
|
|
16
|
+
<span *ngIf="!entry.data.valid">
|
|
17
|
+
Failed to sync to {{ getName(entry) }}
|
|
18
|
+
</span>
|
|
19
|
+
<button (click)="mutate()" class="btn btn-link btn-sm details-button">
|
|
20
|
+
<clr-icon shape="sync" size="16"></clr-icon>
|
|
21
|
+
</button>
|
|
22
|
+
<br />
|
|
23
|
+
<vdr-history-entry-detail *ngIf="entry.data.error">
|
|
24
|
+
<vdr-object-tree [value]="entry.data.error"></vdr-object-tree>
|
|
25
|
+
</vdr-history-entry-detail>
|
|
26
|
+
`,
|
|
27
|
+
})
|
|
28
|
+
export class HistoryEntryComponent implements OrderHistoryEntryComponent {
|
|
29
|
+
entry!: TimelineHistoryEntry;
|
|
30
|
+
order!: OrderDetailFragment;
|
|
31
|
+
|
|
32
|
+
constructor(
|
|
33
|
+
protected dataService: DataService,
|
|
34
|
+
protected notificationService: NotificationService
|
|
35
|
+
) {}
|
|
36
|
+
|
|
37
|
+
async mutate(): Promise<void> {
|
|
38
|
+
try {
|
|
39
|
+
await this.dataService
|
|
40
|
+
.mutate(
|
|
41
|
+
gql`
|
|
42
|
+
mutation {
|
|
43
|
+
sendToSendCloud(orderId: "${this.order.id}")
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
)
|
|
47
|
+
.toPromise();
|
|
48
|
+
this.notificationService.success('Success');
|
|
49
|
+
} catch (e) {
|
|
50
|
+
this.notificationService.error('Error');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getDisplayType(entry: TimelineHistoryEntry): TimelineDisplayType {
|
|
55
|
+
return entry.data.valid ? 'success' : 'error';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getName(entry: TimelineHistoryEntry): string {
|
|
59
|
+
return entry.data.name;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
isFeatured(entry: TimelineHistoryEntry): boolean {
|
|
63
|
+
return !entry.data.valid;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getIconShape(entry: TimelineHistoryEntry) {
|
|
67
|
+
return entry.data.valid ? 'check-circle' : 'exclamation-circle';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import gql from 'graphql-tag';
|
|
2
|
+
|
|
3
|
+
export const UPDATE_SENDCLOUD_CONFIG = gql`
|
|
4
|
+
mutation updateSendCloudConfig($input: SendCloudConfigInput) {
|
|
5
|
+
updateSendCloudConfig(input: $input) {
|
|
6
|
+
id
|
|
7
|
+
secret
|
|
8
|
+
publicKey
|
|
9
|
+
defaultPhoneNr
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
export const GET_SENDCLOUD_CONFIG = gql`
|
|
15
|
+
query sendCloudConfig {
|
|
16
|
+
sendCloudConfig {
|
|
17
|
+
id
|
|
18
|
+
secret
|
|
19
|
+
publicKey
|
|
20
|
+
defaultPhoneNr
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import {
|
|
3
|
+
addNavMenuItem,
|
|
4
|
+
registerHistoryEntryComponent,
|
|
5
|
+
SharedModule,
|
|
6
|
+
} from '@vendure/admin-ui/core';
|
|
7
|
+
import { HistoryEntryComponent } from './history-entry.component';
|
|
8
|
+
|
|
9
|
+
@NgModule({
|
|
10
|
+
imports: [SharedModule],
|
|
11
|
+
declarations: [HistoryEntryComponent],
|
|
12
|
+
providers: [
|
|
13
|
+
addNavMenuItem(
|
|
14
|
+
{
|
|
15
|
+
id: 'sendcloud',
|
|
16
|
+
label: 'SendCloud',
|
|
17
|
+
routerLink: ['/extensions/sendcloud'],
|
|
18
|
+
icon: 'cloud',
|
|
19
|
+
requiresPermission: 'SetSendCloudConfig',
|
|
20
|
+
},
|
|
21
|
+
'settings'
|
|
22
|
+
),
|
|
23
|
+
registerHistoryEntryComponent({
|
|
24
|
+
type: 'SENDCLOUD_NOTIFICATION',
|
|
25
|
+
component: HistoryEntryComponent,
|
|
26
|
+
}),
|
|
27
|
+
],
|
|
28
|
+
})
|
|
29
|
+
export class SendcloudNavModule {}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
|
2
|
+
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
3
|
+
import { DataService, NotificationService } from '@vendure/admin-ui/core';
|
|
4
|
+
import { GET_SENDCLOUD_CONFIG, UPDATE_SENDCLOUD_CONFIG } from './queries';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'sendcloud-component',
|
|
8
|
+
template: `
|
|
9
|
+
<div class="clr-row">
|
|
10
|
+
<div class="clr-col">
|
|
11
|
+
<form [formGroup]="form" clrForm>
|
|
12
|
+
<clr-input-container>
|
|
13
|
+
<label>SendCloud secret</label>
|
|
14
|
+
<input
|
|
15
|
+
id="secret"
|
|
16
|
+
type="text"
|
|
17
|
+
formControlName="secret"
|
|
18
|
+
clrInput
|
|
19
|
+
size="28"
|
|
20
|
+
/>
|
|
21
|
+
</clr-input-container>
|
|
22
|
+
<clr-input-container>
|
|
23
|
+
<label>SendCloud public key</label>
|
|
24
|
+
<input
|
|
25
|
+
id="publicKey"
|
|
26
|
+
type="text"
|
|
27
|
+
formControlName="publicKey"
|
|
28
|
+
clrInput
|
|
29
|
+
size="28"
|
|
30
|
+
/>
|
|
31
|
+
</clr-input-container>
|
|
32
|
+
<clr-input-container>
|
|
33
|
+
<label>Fallback phone nr.</label>
|
|
34
|
+
<input
|
|
35
|
+
id="defaultPhoneNr"
|
|
36
|
+
type="text"
|
|
37
|
+
formControlName="defaultPhoneNr"
|
|
38
|
+
clrInput
|
|
39
|
+
size="28"
|
|
40
|
+
/>
|
|
41
|
+
<clr-control-helper
|
|
42
|
+
>Used when a customer hasn't entered a phone number. <br />
|
|
43
|
+
Phone number is required in some cases by
|
|
44
|
+
Sendcloud</clr-control-helper
|
|
45
|
+
>
|
|
46
|
+
</clr-input-container>
|
|
47
|
+
<button
|
|
48
|
+
class="btn btn-primary"
|
|
49
|
+
(click)="save()"
|
|
50
|
+
style="margin-left: 20rem"
|
|
51
|
+
[disabled]="form.invalid || form.pristine"
|
|
52
|
+
>
|
|
53
|
+
Save
|
|
54
|
+
</button>
|
|
55
|
+
</form>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
`,
|
|
59
|
+
})
|
|
60
|
+
export class SendcloudComponent implements OnInit {
|
|
61
|
+
form: FormGroup;
|
|
62
|
+
|
|
63
|
+
constructor(
|
|
64
|
+
private formBuilder: FormBuilder,
|
|
65
|
+
protected dataService: DataService,
|
|
66
|
+
private changeDetector: ChangeDetectorRef,
|
|
67
|
+
private notificationService: NotificationService
|
|
68
|
+
) {
|
|
69
|
+
this.form = this.formBuilder.group({
|
|
70
|
+
secret: ['your-secret'],
|
|
71
|
+
publicKey: ['your-public-key'],
|
|
72
|
+
defaultPhoneNr: ['your-phone-number'],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async ngOnInit(): Promise<void> {
|
|
77
|
+
await this.dataService
|
|
78
|
+
.query(GET_SENDCLOUD_CONFIG)
|
|
79
|
+
.mapStream((d: any) => d.sendCloudConfig)
|
|
80
|
+
.subscribe((config) => {
|
|
81
|
+
this.form.controls['secret'].setValue(config.secret);
|
|
82
|
+
this.form.controls['publicKey'].setValue(config.publicKey);
|
|
83
|
+
this.form.controls['defaultPhoneNr'].setValue(config.defaultPhoneNr);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async save(): Promise<void> {
|
|
88
|
+
try {
|
|
89
|
+
if (this.form.dirty) {
|
|
90
|
+
const formValue = this.form.value;
|
|
91
|
+
await this.dataService
|
|
92
|
+
.mutate(UPDATE_SENDCLOUD_CONFIG, {
|
|
93
|
+
input: {
|
|
94
|
+
secret: formValue.secret,
|
|
95
|
+
publicKey: formValue.publicKey,
|
|
96
|
+
defaultPhoneNr: formValue.defaultPhoneNr,
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
.toPromise();
|
|
100
|
+
}
|
|
101
|
+
this.form.markAsPristine();
|
|
102
|
+
this.changeDetector.markForCheck();
|
|
103
|
+
this.notificationService.success('common.notify-update-success', {
|
|
104
|
+
entity: 'SendCloud config',
|
|
105
|
+
});
|
|
106
|
+
} catch (e: any) {
|
|
107
|
+
this.notificationService.error('common.notify-update-error', {
|
|
108
|
+
entity: 'SendCloud config',
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NgModule } from '@angular/core';
|
|
2
|
+
import { RouterModule } from '@angular/router';
|
|
3
|
+
import { addNavMenuItem, SharedModule } from '@vendure/admin-ui/core';
|
|
4
|
+
import { SendcloudComponent } from './sendcloud.component';
|
|
5
|
+
|
|
6
|
+
@NgModule({
|
|
7
|
+
imports: [
|
|
8
|
+
SharedModule,
|
|
9
|
+
RouterModule.forChild([
|
|
10
|
+
{
|
|
11
|
+
path: '',
|
|
12
|
+
pathMatch: 'full',
|
|
13
|
+
component: SendcloudComponent,
|
|
14
|
+
data: { breadcrumb: 'SendCloud' },
|
|
15
|
+
},
|
|
16
|
+
]),
|
|
17
|
+
],
|
|
18
|
+
providers: [],
|
|
19
|
+
declarations: [SendcloudComponent],
|
|
20
|
+
})
|
|
21
|
+
export class SendcloudModule {}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pinelab/vendure-plugin-sendcloud",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Vendure plugin for syncing orders with SendCloud",
|
|
5
|
+
"author": "Martijn van de Brug <martijn@pinelab.studio>",
|
|
6
|
+
"homepage": "https://pinelab-plugins.com/",
|
|
7
|
+
"repository": "https://github.com/Pinelab-studio/pinelab-vendure-plugins",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"private": false,
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"main": "dist/vendure-plugin-sendcloud/src/index.js",
|
|
14
|
+
"types": "dist/vendure-plugin-sendcloud/src/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "rimraf dist && tsc && copyfiles -u 1 'src/ui/**/*' dist/vendure-plugin-sendcloud/src/",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"start": "yarn ts-node test/dev-server.ts"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@swc/core": "^1.3.59",
|
|
26
|
+
"@vendure/admin-ui-plugin": "2.0.4",
|
|
27
|
+
"@vendure/core": "2.0.4",
|
|
28
|
+
"@vendure/email-plugin": "2.0.4",
|
|
29
|
+
"@vendure/testing": "2.0.4",
|
|
30
|
+
"@vendure/ui-devkit": "2.0.4",
|
|
31
|
+
"copyfiles": "^2.4.1",
|
|
32
|
+
"nock": "^13.2.9",
|
|
33
|
+
"node-fetch": "2.x",
|
|
34
|
+
"ts-node": "^10.7.0",
|
|
35
|
+
"typescript": "4.9.5",
|
|
36
|
+
"unplugin-swc": "^1.3.2",
|
|
37
|
+
"vitest": "^0.30.1"
|
|
38
|
+
},
|
|
39
|
+
"gitHead": "5673faf91c029dc4716226965ce3531931b434b6"
|
|
40
|
+
}
|