Skip to content

Commit 92fa8b9

Browse files
committed
FEATURE: preview recepients list
1 parent 92dd421 commit 92fa8b9

File tree

11 files changed

+331
-63
lines changed

11 files changed

+331
-63
lines changed

Classes/Psmb/Newsletter/Controller/NewsletterController.php

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function getSubscriptionsAction($nodeType) {
7676
});
7777
}
7878
$subscriptionsJsonArray = array_map(function ($item) {
79-
return ['label' => $item['label'], 'value' => $item['identifier']];
79+
return ['label' => isset($item['label']) ? $item['label'] : '', 'value' => $item['identifier']];
8080
}, $subscriptions);
8181
$this->view->assign('value', array_values($subscriptionsJsonArray));
8282
}
@@ -86,20 +86,44 @@ public function getSubscriptionsAction($nodeType) {
8686
*
8787
* @param string $subscription Subscription id to send newsletter to
8888
* @param NodeInterface $node Node of the current newsletter item
89+
* @param array $dataSourceAdditionalArguments
8990
* @return void
9091
*/
91-
public function sendAction($subscription, NodeInterface $node)
92+
public function sendAction($subscription, NodeInterface $node, array $dataSourceAdditionalArguments = null)
9293
{
9394
$subscriptions = array_filter($this->subscriptions, function ($item) use ($subscription) {
9495
return $item['identifier'] == $subscription;
9596
});
9697
array_walk($subscriptions, function ($subscription) use ($node) {
9798
$subscription['isSendFromUi'] = true;
99+
if ($dataSourceAdditionalArguments) {
100+
$subscription['dataSourceAdditionalArguments'] = $dataSourceAdditionalArguments;
101+
}
98102
$this->sendLettersForSubscription($subscription, $node);
99103
});
100104
$this->view->assign('value', ['status' => 'success']);
101105
}
102106

107+
/**
108+
* Registers a new subscriber
109+
*
110+
* @param string $subscription Subscription id to send newsletter to
111+
* @param array $dataSourceAdditionalArguments
112+
* @return void
113+
*/
114+
public function previewAction($subscription, $dataSourceAdditionalArguments = null)
115+
{
116+
$subscriptions = array_filter($this->subscriptions, function ($item) use ($subscription) {
117+
return $item['identifier'] == $subscription;
118+
});
119+
$subscription = reset($subscriptions);
120+
if ($dataSourceAdditionalArguments) {
121+
$subscription['dataSourceAdditionalArguments'] = $dataSourceAdditionalArguments;
122+
}
123+
$subscribers = $this->subscribersService->getSubscribers($subscription);
124+
$this->view->assign('value', $subscribers);
125+
}
126+
103127
/**
104128
* Sends a test letter for subscription
105129
*

Classes/Psmb/Newsletter/Service/DataSource/JsonDataSource.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ public function getData(array $subscription)
2020
if (!isset($subscription['dataSourceOptions']['uri'])) {
2121
throw new \Exception('dataSourceOptions.uri must be set for the Json datasource' . print_r($subscription, 1));
2222
}
23-
$response = file_get_contents($subscription['dataSourceOptions']['uri']);
23+
$uri = $subscription['dataSourceOptions']['uri'];
24+
if (isset($subscription['dataSourceAdditionalArguments'])) {
25+
$uri .= strpos($uri, '?') === false ? '?' : '&';
26+
$uri .= http_build_query($subscription['dataSourceAdditionalArguments']);
27+
}
28+
$response = file_get_contents($uri);
2429
return json_decode($response, true);
2530
}
26-
}
31+
}

Configuration/NodeTypes.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,17 @@
2424
label: 'Newsletter view'
2525
group: newsletter
2626
view: Psmb.Newsletter/Views/NewsletterView
27+
# viewOptions:
28+
# dataSourceAdditionalArguments:
29+
# startDate: 'ClientEval:node.properties.sampleProperty'
30+
# properties:
31+
# sampleProperty:
32+
# type: DateTime
33+
# ui:
34+
# label: 'Жертвователи с'
35+
# reloadIfChanged: true
36+
# inspector:
37+
# group: newsletter
38+
# position: 1
39+
# editorOptions:
40+
# format: 'Y-m-d'

Configuration/Settings.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
Flowpack:
32
JobQueue:
43
Common:

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,42 @@ Psmb:
228228

229229
Alternatively you may provide your custom datasources. See implementation of JsonDataSource.php to see how to do that.
230230

231+
It is also possible to provide some additional arguments for the datasource that would be filled from other properties of a node.
232+
Here is an example NodeTypes.yaml for this:
233+
234+
```
235+
'Psmb.Newsletter:NewsletterMixin':
236+
abstract: true
237+
ui:
238+
inspector:
239+
tabs:
240+
newsletter:
241+
label: i18n
242+
position: 100
243+
icon: icon-send
244+
groups:
245+
newsletter:
246+
label: i18n
247+
tab: newsletter
248+
views:
249+
newsletter:
250+
viewOptions:
251+
dataSourceAdditionalArguments:
252+
sampleArgument: 'ClientEval:node.properties.sampleProperty'
253+
properties:
254+
sampleProperty:
255+
type: DateTime
256+
ui:
257+
label: 'Subscribers since'
258+
inspector:
259+
group: newsletter
260+
position: 1
261+
editorOptions:
262+
format: 'Y-m-d'
263+
```
264+
265+
Then the dataSourceAdditionalArguments would be passed to a datasource. You may check out how Json datasource handles it.
266+
231267
## Acknowledgements
232268

233269
This is my first Flow package, and it wouldn't have been possible without a support of the community by answering dozens of n00b questions on Slack, by Christian Müller in particular.
Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,78 @@
1-
import React from 'react';
1+
import React, {PureComponent} from 'react';
22
import PropTypes from 'prop-types';
33
import {Button, Dialog} from '@neos-project/react-ui-components';
44

5-
const ConfirmationDialog = ({isOpen, translate, close, send}) => {
6-
return (
7-
<Dialog
8-
isOpen={isOpen}
9-
title={translate('Psmb.Newsletter:Main:js.testConfirmationTitle')}
10-
onRequestClose={close}
11-
actions={[
12-
<Button onClick={close} style="clean">{translate('Neos.Neos:Main:cancel')}</Button>,
13-
<Button onClick={send} style="brand">{translate('Psmb.Newsletter:Main:js.send')}</Button>
14-
]}
15-
>
16-
<div style={{padding: '16px'}}>{translate('Psmb.Newsletter:Main:js.confirmationDescription')}</div>
17-
</Dialog>
18-
);
19-
};
20-
ConfirmationDialog.propTypes = {
21-
isOpen: PropTypes.bool,
22-
translate: PropTypes.func.isRequired,
23-
close: PropTypes.func.isRequired,
24-
send: PropTypes.func.isRequired
5+
class ConfirmationDialog extends PureComponent {
6+
static propTypes = {
7+
isOpen: PropTypes.bool,
8+
translate: PropTypes.func.isRequired,
9+
close: PropTypes.func.isRequired,
10+
send: PropTypes.func.isRequired,
11+
subscription: PropTypes.string.isRequired,
12+
dataSourceAdditionalArguments: PropTypes.object
13+
};
14+
15+
state = {
16+
isLoading: false,
17+
subscribers: []
18+
};
19+
20+
componentDidUpdate(prevProps) {
21+
if (this.props.subscription !== prevProps.subscription || (prevProps.isOpen === false && this.props.isOpen === true)) {
22+
this.fetchPreview();
23+
}
24+
}
25+
26+
fetchPreview() {
27+
if (this.props.subscription && this.props.isOpen) {
28+
this.setState({isLoading: true, subscribers: []});
29+
const dataSourceAdditionalArguments = this.props.dataSourceAdditionalArguments;
30+
const data = new URLSearchParams();
31+
if (dataSourceAdditionalArguments) {
32+
Object.keys(dataSourceAdditionalArguments).forEach(option => {
33+
data.set('dataSourceAdditionalArguments[' + option + ']', dataSourceAdditionalArguments[option]);
34+
});
35+
}
36+
fetch(`/newsletter/preview?subscription=${this.props.subscription}&${data.toString()}`, {
37+
credentials: 'include'
38+
})
39+
.then(response => response.json())
40+
.then(subscribers => {
41+
this.setState({subscribers, isLoading: false});
42+
});
43+
}
44+
}
45+
render() {
46+
const {isOpen, translate, close, send} = this.props;
47+
48+
const keys = this.state.subscribers[0] ? Object.keys(this.state.subscribers[0]) : [];
49+
return (
50+
<Dialog
51+
isOpen={isOpen}
52+
title={translate('Psmb.Newsletter:Main:js.confirmationTitle')}
53+
onRequestClose={close}
54+
actions={[
55+
<Button onClick={close} style="clean">{translate('Neos.Neos:Main:cancel')}</Button>,
56+
<Button onClick={send} style="brand">{translate('Psmb.Newsletter:Main:js.send')}</Button>
57+
]}
58+
>
59+
<div style={{padding: '16px'}}>
60+
<div>{translate('Psmb.Newsletter:Main:js.confirmationDescription')}</div>
61+
{this.state.isLoading ? translate('Psmb.Newsletter:Main:js.loading') : (
62+
<div>
63+
<div style={{padding: '16px 0'}}>{translate('Psmb.Newsletter:Main:js.recepients')}: <strong>{this.state.subscribers.length}</strong></div>
64+
<table>
65+
<tr>{keys.map(key => <th>{key}</th>)}</tr>
66+
{this.state.subscribers.map(subscriber => {
67+
return <tr>{keys.map(key => <td>{subscriber[key]}</td>)}</tr>;
68+
})}
69+
</table>
70+
</div>
71+
)}
72+
</div>
73+
</Dialog>
74+
);
75+
}
2576
};
2677

2778
export default ConfirmationDialog;

Resources/Private/NewsletterView/src/NewsletterView.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const fetchSubscriptions = nodeType => fetch(`/newsletter/getSubscriptions?nodeT
1212
credentials: 'include'
1313
}).then(response => response.json());
1414

15-
const sendNewsletter = (focusedNodeContextPath, subscription, isTest, email) => {
16-
const sendEndpointUrl = isTest ? '/newsletter/testSend' : '/newsletter/send';
15+
const sendNewsletter = (focusedNodeContextPath, subscription, isTest, email, dataSourceAdditionalArguments) => {
16+
let sendEndpointUrl = isTest ? '/newsletter/testSend' : '/newsletter/send';
1717
const csrfToken = document.getElementById('appContainer').dataset.csrfToken;
1818
const data = new URLSearchParams();
1919
data.set('node', focusedNodeContextPath.replace(/user-.+\;/, 'live;'));
@@ -22,6 +22,11 @@ const sendNewsletter = (focusedNodeContextPath, subscription, isTest, email) =>
2222
if (isTest && email) {
2323
data.set('email', email);
2424
}
25+
if (dataSourceAdditionalArguments) {
26+
Object.keys(dataSourceAdditionalArguments).forEach(option => {
27+
data.set('dataSourceAdditionalArguments[' + pair[0] + ']', dataSourceAdditionalArguments[option]);
28+
});
29+
}
2530
return fetch(sendEndpointUrl, {
2631
credentials: 'include',
2732
method: 'POST',
@@ -126,6 +131,8 @@ export default class NewsletterView extends Component {
126131
translate={this.props.i18nRegistry.translate.bind(this.props.i18nRegistry)}
127132
close={() => this.toggleConfirmationDialog(false)}
128133
send={this.sendNewsletter}
134+
subscription={this.state.selectedSubscription}
135+
dataSourceAdditionalArguments={this.props.options && this.props.options.dataSourceAdditionalArguments}
129136
/>
130137
</div>
131138
);

Resources/Private/Translations/en/Main.xlf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
<source>Loading...</source>
5353
</trans-unit>
5454

55+
<trans-unit id="js.recepients" xml:space="preserve">
56+
<source>Total number of recepients</source>
57+
</trans-unit>
5558
<trans-unit id="js.confirmationTitle" xml:space="preserve">
5659
<source>Send Newsletter</source>
5760
</trans-unit>

Resources/Private/Translations/ru/Main.xlf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
<target>Загрузка...</target>
6868
</trans-unit>
6969

70+
<trans-unit id="js.recepients" xml:space="preserve">
71+
<target>Общее количество адресатов</target>
72+
</trans-unit>
73+
7074
<trans-unit id="js.confirmationTitle" xml:space="preserve">
7175
<source>Send newsletter</source>
7276
<target>Отправить рассылку</target>

0 commit comments

Comments
 (0)