PHPackages                             psmb/newsletter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. psmb/newsletter

ActiveNeos-package

psmb/newsletter
===============

Generate Newsletters based on Fusion

v2.5.0(7y ago)121.4k↓100%8[1 issues](https://github.com/psmb/Psmb.Newsletter/issues)[1 PRs](https://github.com/psmb/Psmb.Newsletter/pulls)GPL-3.0+PHP

Since Jan 27Pushed 7y ago4 watchersCompare

[ Source](https://github.com/psmb/Psmb.Newsletter)[ Packagist](https://packagist.org/packages/psmb/newsletter)[ RSS](/packages/psmb-newsletter/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (10)Dependencies (6)Versions (21)Used By (0)

Psmb.Newsletter
===============

[](#psmbnewsletter)

With this package you can generate newsletters based on content of your Neos CMS website (e.g. digest of latest articles).

The cool part is that you render those newsletters using Fusion, which allows you to reuse any of your existing website Fusion objects and be completely flexible.

Installation
------------

[](#installation)

`composer require "psmb/newsletter:@dev"`

Configuration
-------------

[](#configuration)

Put something like this into your Settings.yaml:

```
Psmb:
  Newsletter:
    globalSettings:
      senderAddress: 'robot@server.com'
      senderName: 'Your robot'
    subscriptions:
      -
        identifier: daily
        renderer: 'Your.NameSpace:DailyLetterRenderer'
        label: 'Our daily newsletter'
        interval: P1D
      -
        identifier: weekly
        renderer: 'Your.NameSpace:WeeklyLetterRenderer'
        label: 'Our weekly newsletter (In Russian)'
        interval: P1W
        dimensions:
          language: ['ru']
      -
        identifier: handcrafted
        renderer: 'Your.NameSpace:HandcraftedDigestRenderer'
        sendFromUiNodeType: 'Your.NameSpace:HandcraftedDigest'
        label: 'Manually crafted newsletter'
        interval: manual

```

You also have to configure a baseUri in your Settings.yaml

```
Neos:
  Flow:
    http:
      baseUri: 'http://yourdomain.tld/'

```

Define as many subscription types under Psmb.Newsletter.subscriptions as you need.

setting keydescription`identifier`Identifier of the subscription`renderer`Fusion object that would be used for rendering emails sent to this subscription. Defaults to `Psmb.Newsletter:MailRenderer`. Must inherit from it.`sendFromUiNodeType`Show this subscription for the nodes of given nodetype when sending from UI.`interval`Time interval identifier that would be used for selecting subscriptions. Can be used for cron jobs. Those marked as `manual` would appear as options when sending from the UI. Optional.`senderName`, `senderAddress`Override options from `globalSettings`.`dimensions`Array of dimensions in form of "dimensionName: \['dimensionValues'\]". Falls back to default dimension values.Also you may need to configure SwiftMailer, [see its docs](http://swiftmailer-for-flow.readthedocs.io/en/latest/#configuration) how to do that.

Rendering newsletters
---------------------

[](#rendering-newsletters)

As said before, we render letters purely in Fusion, so it's completely up to you to define the rendering objects. Entry point to rendering is `newsletter = Psmb.Newsletter:SubscriptionCase`, so you may hook straight into `Psmb.Newsletter:SubscriptionCase` to intercept the rendering process. But the intended usage is to provide a different renderer per each subscription preset via the `renderer` setting key (see above).

Then define a Fusion object like this for each renderer:

```
prototype(Your.NameSpace:WeeklyLetterRenderer) < prototype(Psmb.Newsletter:MailRenderer) {
	subject = 'Our weekly digest'
	body = 'Generate message body. Use your imagination.'

  # You may automatically inline all css styles.
	# body.@process.cssToInline = Psmb.Newsletter:CssToInline {
	#	  cssPath = 'resource://Your.Package/Public/Path/To/Your/Styles.css'
	# }
  # # You may also override these settings, but usually no need to
  # format = 'plaintext' # defaults to 'html'
	# recipientAddress = ${subscriber.email}
	# recipientName = ${subscriber.name}
	# replyToAddress = ${subscription.senderAddress || globalSettings.senderAddress}
	# senderAddress = ${subscription.senderAddress || globalSettings.senderAddress}
	# senderName = ${subscription.senderName || globalSettings.senderName}
}

```

### You'd have the following context variables available:

[](#youd-have-the-following-context-variables-available)

Context variable nameDescription`site`,`documentNode`,`node`All point to `site` root node.`subscriber``Subscriber` object. Has `name` and `email` keys.`subscription`Settings array for current subscription. `interval` key may be the most interesting one for generating lists of latest articles for given period.`globalSettings`Global settings array.`activationLink`Only available when generating email confirmation letters.### A more practical example:

[](#a-more-practical-example)

```
prototype(Sfi.Site:DigestMail) < prototype(Psmb.Newsletter:MailRenderer) {
    # ElasticSearch used here, but could be a simple FlowQuery as well
    @context.nodes = ${Search.query(site).nodeType('Sfi.Site:News').exactMatch('type', 'ourNews').greaterThan('date', Date.format(Date.subtract(Date.now(), subscription.interval), "Y-m-d\TH:i:sP")).sortDesc('date').execute()}
    @if.notEmpty = ${q(nodes).count() > 0}
    subject = ${Translation.translate('newsletter.digestSubject', null, [], null, 'Sfi.Site')}
    body = Neos.Fusion:Collection {
        collection = ${nodes}
        itemName = 'node'
        itemRenderer = Sfi.Site:DigestArticle
    }
    @process.cssToInline = Psmb.Newsletter:CssToInline {
        cssPath = 'resource://Sfi.Site/Public/built/index.css'
    }
}

prototype(Sfi.Site:DigestArticle) < prototype(Neos.Fusion:Tag) {
    tagName = 'a'
    attributes.href = NodeUri {
        node = ${node}
        absolute = ${true}
    }
    content = ${node.properties.title}
}

```

### Provided Fusion objects

[](#provided-fusion-objects)

Object nameDescription`Psmb.Newsletter:MailRenderer`Abstract mail renderer. Returns a RawArray of `body`, `subject` and other mail options passed to swiftmailer. See above for details.`Psmb.Newsletter:EditSubscriptionLink`Generated a link to edit action of the plugin`Psmb.Newsletter:UnsubscribeLink`Generated a link to unsubscribe action of the plugin`Psmb.Newsletter:CssToInline`Processor to automatically inline CSS styles`Psmb.Newsletter:SubscriptionCase`Rendering entry point. Usually no need to touch this one`Psmb.Newsletter:SubscriptionPlugin`Configured `Neos.Neos:Plugin` instance that insert Flow signup plugin.To customize the confirmation mail, override `Psmb.Newsletter:ConfirmationMailRenderer`.

Rendering signup plugin
-----------------------

[](#rendering-signup-plugin)

Insert `Psmb.Newsletter:SubscriptionPlugin` nodetype or render it manually.

Subscription has the following flow:

1. Fill in the sign up form. Asks for name, and email, and allows to choose between available subscription plans.
2. Confirm email.
3. Edit subscription options or unsubscribe from email links.

You absolutely have to override the default templates, the default ones are there just for demo purpose. Create a `Views.yaml` file in Configuration:

```
-
  requestFilter: 'isPackage("Psmb.Newsletter") && isController("Subscription")'
  options:
    templateRootPaths:
      - 'resource://Sfi.Site/Private/Newsletter/Templates/'
      - 'resource://Psmb.Newsletter/Private/Templates'
    partialRootPaths:
      - 'resource://Sfi.Site/Private/Newsletter/Partials/'
      - 'resource://Psmb.Newsletter/Private/Partials/'
    layoutRootPaths:
      - 'resource://Sfi.Site/Private/Newsletter/Layouts/'

```

Sending things out
------------------

[](#sending-things-out)

Once you are ready setting up rendering of your subscriptions, it's time to send the out! There's a CLI command for it.

`./flow newsletter:send --subscription="daily"` would send out newsletter to all users subscribed to subscription with identifier "daily".

`./flow newsletter:send --interval="P1H"` would find all subscriptions with interval equal to "P1H" and send out letters to them. This is useful for setting up cron tasks based on time interval.

Manual sending from the UI
--------------------------

[](#manual-sending-from-the-ui)

[![image](https://cloud.githubusercontent.com/assets/837032/22371056/cccc522e-e4a5-11e6-929c-29f15d8b632c.png)](https://cloud.githubusercontent.com/assets/837032/22371056/cccc522e-e4a5-11e6-929c-29f15d8b632c.png)

If your nodetype inherits from `Psmb.Newsletter:NewsletterMixin` you would see a new inspector tab from which you would be able to send manual newsletters, based on the current document node.

For this to work, one or more of your subscription presets must have `interval: manual`. Those presets would appear in the inspector view selectbox. Click send, and the newsletter would be send out to all subscribers of the chosen subscription group. Current document node would be available in the Fusion renderer as `documentNode` and `node`.

Improving reliabillity with job queue
-------------------------------------

[](#improving-reliabillity-with-job-queue)

This package uses [flowpack/jobqueue](https://github.com/Flowpack/jobqueue-common) package to generate and deliver messages. Refer to its docs how to improve its reliabillity via proper job queue implementations.

Importing subscribers from CSV
------------------------------

[](#importing-subscribers-from-csv)

Create a CSV file with your subscribers data and put it somewhere on your server.

The file should have the following format:

```
"user@email.com","User Name","subscriptionId1|subscriptionId2"
"user1@email.com","User1 Name","subscriptionId2"

```

Then run (file path is relative to installation root):

`./flow newsletter:importCsv --filename="test.csv"`

Here's a quick example how to create CSV exports from mysql:

```
SELECT email, name INTO OUTFILE '/path/test.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' FROM yourTable;

```

External Datasources
--------------------

[](#external-datasources)

The default datasource for fetching subscribers has an identifier `Repository`. It fetches all subscribers subscribed via the default subscription plugin.

You may fetch subscribers from an external JSON source via the `Json` datasource. Here's an example:

```
Psmb:
  Newsletter:
    subscriptions:
      -
        dataSourceIdentifier: 'Json'
        dataSourceOptions:
          uri: 'http://some.host/some-url'

```

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

It is also possible to provide some additional arguments for the datasource that would be filled from other properties of a node. Here is an example NodeTypes.yaml for this:

```
'Psmb.Newsletter:NewsletterMixin':
  abstract: true
  ui:
    inspector:
      tabs:
        newsletter:
          label: i18n
          position: 100
          icon: icon-send
      groups:
        newsletter:
          label: i18n
          tab: newsletter
      views:
        newsletter:
          viewOptions:
            dataSourceAdditionalArguments:
              sampleArgument: 'ClientEval:node.properties.sampleProperty'
  properties:
    sampleProperty:
      type: DateTime
      ui:
        label: 'Subscribers since'
        inspector:
          group: newsletter
          position: 1
          editorOptions:
            format: 'Y-m-d'

```

Then the dataSourceAdditionalArguments would be passed to a datasource. You may check out how Json datasource handles it.

Acknowledgements
----------------

[](#acknowledgements)

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.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance18

Infrequent updates — may be unmaintained

Popularity27

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity72

Established project with proven stability

 Bus Factor1

Top contributor holds 87.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~37 days

Recently: every ~93 days

Total

20

Last Release

2687d ago

Major Versions

v1.1 → v2.02017-02-01

### Community

Maintainers

![](https://www.gravatar.com/avatar/6e7861363bc4d9a01d804fb1823366c3e59f339542e3a4ea89f7cea7b0ea91af?d=identicon)[dimaip](/maintainers/dimaip)

---

Top Contributors

[![dimaip](https://avatars.githubusercontent.com/u/837032?v=4)](https://github.com/dimaip "dimaip (82 commits)")[![mgoldbeck](https://avatars.githubusercontent.com/u/1336044?v=4)](https://github.com/mgoldbeck "mgoldbeck (12 commits)")

---

Tags

neoscms

### Embed Badge

![Health badge](/badges/psmb-newsletter/health.svg)

```
[![Health](https://phpackages.com/badges/psmb-newsletter/health.svg)](https://phpackages.com/packages/psmb-newsletter)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.6k509.9M17.0k](/packages/laravel-framework)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[fedeisas/laravel-mail-css-inliner

Inline the CSS of your HTML emails using Laravel

5974.6M3](/packages/fedeisas-laravel-mail-css-inliner)[twig/cssinliner-extra

A Twig extension to allow inlining CSS

23018.5M55](/packages/twig-cssinliner-extra)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

728272.9k20](/packages/civicrm-civicrm-core)[neos/neos-ui

Neos CMS UI written in React

2661.0M104](/packages/neos-neos-ui)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
