New project structure
Beta of extract.js for extracting english locale
This commit is contained in:
parent
e18d2b2f84
commit
2edbd67205
247 changed files with 6405 additions and 4237 deletions
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -4,19 +4,6 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
config/development.*
|
||||
config/production.*
|
||||
config/test.*
|
||||
workers/reports/config/development.*
|
||||
workers/reports/config/production.*
|
||||
workers/reports/config/test.*
|
||||
dump.rdb
|
||||
|
||||
# generate POT file every time you want to update your PO file
|
||||
languages/mailtrain.pot
|
||||
|
||||
config/production.toml
|
||||
workers/reports/config/production.toml
|
||||
docker-compose.override.yml
|
||||
|
||||
/files
|
||||
|
|
19
BACKERS.md
19
BACKERS.md
|
@ -1,19 +0,0 @@
|
|||
# Crowdfunding Backers of Mailtrain v1
|
||||
|
||||
Mailtrain v1 received funding from a [crowdfunding campaign](https://www.indiegogo.com/at/mailtrain/8720095).
|
||||
This was to enable me to spend the time required to get automation support into Mailtrain v1. These are the people who contributed to this fund raiser.
|
||||
|
||||
* iRedMail - free, open source mail server solution <[www.iredmail.org](http://www.iredmail.org/)>
|
||||
* Richard Adleta
|
||||
* Wes Bos
|
||||
* Christophe Lombart
|
||||
* Anselm Hannemann
|
||||
* Jens Carroll
|
||||
* Anonymous
|
||||
* Brett Nelson
|
||||
* Jason Pelker
|
||||
* Leif Singer
|
||||
* Eve Land
|
||||
* Diana Espino
|
||||
* Moussa Clarke
|
||||
* Carl Hauschke
|
148
CHANGELOG.md
148
CHANGELOG.md
|
@ -1,149 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
## 1.23.2 2017-04-04
|
||||
## 2.0.0 2018-11-23
|
||||
|
||||
* Allow skipping DNS check for imports
|
||||
* Added option to use subscription widgets
|
||||
|
||||
## 1.23.0 2017-03-19
|
||||
|
||||
* Fixed security issue where description tags were able to include script tags. Reported by Andreas Lindh. Fixed with [ae6affda](https://github.com/Mailtrain-org/mailtrain/commit/ae6affda8193f034e06f7e095ee23821a83d5190)
|
||||
* Fixed security issue where templates that looked like file paths loaded content from arbitrary files. Reported by Andreas Lindh. Fixed with [0879fa41](https://github.com/Mailtrain-org/mailtrain/commit/0879fa412a2d4a417aeca5cd5092a8f86531e7ef)
|
||||
* Fixed security issue where users were able to use html tags in subscription values. Reported by Andreas Lindh. Fixed with [9d5fb816](https://github.com/Mailtrain-org/mailtrain/commit/9d5fb816c937114966d4f589e1ad4e164ff3a187)
|
||||
* Support for multiple HTML editors (Mosaico, Grapesjs, Summernote, HTML code)
|
||||
|
||||
## 1.22.0 2017-03-02
|
||||
|
||||
* Reverted license back to GPL-v3 to support Mosaico
|
||||
|
||||
## 1.21.0 2017-02-17
|
||||
|
||||
* Changed license from MIT to EUPL-1.1
|
||||
* Added support for sending mail using AWS SES
|
||||
|
||||
## 1.20.0 2016-12-11
|
||||
|
||||
* Added option to distribute sending queue between multiple processes to speed up delivery
|
||||
|
||||
## 1.19.0 2016-09-15
|
||||
|
||||
* Changed license from GPL-V3 to MIT
|
||||
|
||||
## 1.18.0 2016-09-08
|
||||
|
||||
* Updated installation script to bundle ZoneMTA as the default sending engine
|
||||
* Added new option to disable clicked and opened tracking
|
||||
* Store remote IP for subscription confirmations
|
||||
|
||||
## 1.17.0 2016-08-29
|
||||
|
||||
* Added new custom field for JSON data that is rendered using Handlebars when included in an email
|
||||
|
||||
## 1.16.0 2016-08-29
|
||||
|
||||
* Render list values using Handlebars templates
|
||||
* Added new API method to create custom fields
|
||||
* Added LDAP authentication support
|
||||
|
||||
## 1.15.0 2016-07-28
|
||||
|
||||
* Check SMTP settings using AJAX instead of posting entire form
|
||||
|
||||
## 1.14.0 2016-07-09
|
||||
|
||||
* Fixed ANY match segments with range queries
|
||||
* Added an option to disable un/subscribe confirmation messages
|
||||
* Added support for throttling when sending messages
|
||||
* Added preview links in message lists
|
||||
|
||||
## 1.13.0 2016-06-23
|
||||
|
||||
* Added API method to delete subscribers
|
||||
* Added a counter to triggers with a view to list all subscribers that caused this trigger to fire
|
||||
|
||||
## 1.12.1 2016-06-23
|
||||
|
||||
* Fixed invalid base SQL dump
|
||||
|
||||
## 1.12.0 2016-06-22
|
||||
|
||||
* Automation support. Create triggers that send a campaign once fired
|
||||
* Fixed an issue with unresolved URL redirects
|
||||
* Added support for relative date ranges in segments
|
||||
|
||||
## 1.11.0 2016-05-31
|
||||
|
||||
* Retry transactional mail if failed with soft error (4xx)
|
||||
* New feature to preview campaigns using selected test users
|
||||
|
||||
## 1.10.1 2016-05-26
|
||||
|
||||
* Fix a bug with SMTP transport instance where campaign sending stalled until server was restarted
|
||||
|
||||
## 1.10.0 2016-05-25
|
||||
|
||||
* Fetch multiple unsent messages at once to speed up delivery
|
||||
* Fixed a bug of counting unsubscribers correctly
|
||||
* Use LONGTEXT for template text fields (messages might include inlined images which are larger than 64kB)
|
||||
|
||||
## 1.9.0 2016-05-16
|
||||
|
||||
* New look
|
||||
* Added views for bounced/unsubscribed/complained etc.
|
||||
|
||||
## 1.8.2 2016-05-13
|
||||
|
||||
* Added missing views for subscribers who clicked on any link and subscribers who opened the message
|
||||
|
||||
## 1.8.1 2016-05-13
|
||||
|
||||
* Fixed an issue in API
|
||||
|
||||
## 1.8.0 2016-05-13
|
||||
|
||||
* Show details about subscribers who clicked on a specific link
|
||||
|
||||
## 1.7.0 2016-05-11
|
||||
|
||||
* Updated API, added new option **REQUIRE_CONFIRMATION** for subscriptions to send confirmation email before subscribing
|
||||
|
||||
## 1.6.0 2016-05-07
|
||||
|
||||
* Added simple API support for adding and removing list subscriptions
|
||||
|
||||
## 1.5.0 2016-05-05
|
||||
|
||||
* Fixed a bug in unsubscribing through the admin interface
|
||||
* Added individual link click stats
|
||||
|
||||
## 1.4.1 2016-05-04
|
||||
|
||||
* Added support for RSS templates
|
||||
|
||||
## 1.4.0 2016-05-04
|
||||
|
||||
* Added support for RSS campaigns
|
||||
* Subscribers get timezone attached to their profile
|
||||
* Outgoing messages are preprocessed using juice
|
||||
* Added installation script for easier setup
|
||||
|
||||
## 1.3.0 2016-04-29
|
||||
|
||||
* Added option to use an URL as message source (when message needs to be rendered a POST request with Merge Tags as the POST body is made against that URL)
|
||||
* Added option to schedule sending. You can set optional delay time when starting campaign sending. Once this time is reached sending starts automatically
|
||||
* Show meaningful MySQL error when connection fails
|
||||
|
||||
## 1.2.0 2016-04-25
|
||||
|
||||
* Rewrite merge tags in links (allows using links like `http://example.com/?u=[FIRST_NAME]` in messages)
|
||||
* Added view for Imports to list failed addresses
|
||||
* Automatic SQL table creation on initial run (no need for the `mysql` command anymore)
|
||||
* Automatic SQL table updates on startup
|
||||
* Send welcome and unsubscribe confirmation emails for subscribers
|
||||
* Added support for GPG encryption for outgoing messages (requires custom field "GPG Key" set up for the list)
|
||||
* Added new SMTP option: allow self-signed certs
|
||||
* Added new setting: Disable WYSIWG editor (allows better handling of complex HTML templates)
|
||||
* Allow downgrading user when server started as root (user is downgraded once all ports are bound)
|
||||
* Added Nitrous.io one-click install button for easy try-out
|
||||
* Added Max Post Size option to allow larger payloads from bounce webhooks
|
||||
* Added VERP support to catch bounces using built in VERP smtp-server (disabled by default)
|
||||
* This is a complete rewrite of Mailtrain v1 with many features added. Just check it out.
|
||||
|
|
18
Gruntfile.js
18
Gruntfile.js
|
@ -1,18 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
eslint: {
|
||||
all: ['lib/**/*.js', 'test/**/*.js', 'config/**/*.js', 'services/**/*.js', 'Gruntfile.js', 'app.js', 'index.js', 'routes/editorapi.js']
|
||||
}
|
||||
});
|
||||
|
||||
// Load the plugin(s)
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.task.loadTasks('tasks');
|
||||
|
||||
// Tasks
|
||||
grunt.registerTask('default', ['eslint']);
|
||||
};
|
19
README.md
19
README.md
|
@ -1,6 +1,6 @@
|
|||
# Mailtrain
|
||||
|
||||
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB).
|
||||
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v10+) and MySQL (v8+) or MariaDB (v10+).
|
||||
|
||||

|
||||
|
||||
|
@ -9,14 +9,16 @@
|
|||
* Subscriber list management
|
||||
* List segmentation
|
||||
* Custom fields
|
||||
* Email templates
|
||||
* Email templates (including MJML-based templates)
|
||||
* Large CSV list import files
|
||||
|
||||
Subscribe to Mailtrain Newsletter [here](https://mailtrain.org/subscription/S18sew2wM) (uses Mailtrain obviously)
|
||||
* Custom reports
|
||||
* Automation (triggered and RSS campaigns)
|
||||
* Multiple users with granular user permissions and flexible sharing
|
||||
* Hierarchical namespaces for enterprise-level situations
|
||||
|
||||
## Hardware Requirements
|
||||
* 1 vCPU
|
||||
* 1024 MB RAM
|
||||
* 2048 MB RAM
|
||||
|
||||
## Quick Start - Deploy with Docker
|
||||
#### Requirements:
|
||||
|
@ -39,7 +41,7 @@ Depending on how you have configured your system and Docker you may need to prep
|
|||
## Quick Start - Manual Install (any OS that supports Node.js)
|
||||
|
||||
### Requirements:
|
||||
* Mailtrain requires at least **Node.js v7**. If you want to use an older version of Node.js then you should use version v1.24 of Mailtrain. You can either download it [here](https://github.com/Mailtrain-org/mailtrain/archive/v1.24.0.zip) or if using git then run `git checkout v1.24.0` before starting it
|
||||
* Mailtrain requires at least **Node.js v10**.
|
||||
|
||||
1. Download Mailtrain files using git: `git clone git://github.com/Mailtrain-org/mailtrain.git` (or download [zipped repo](https://github.com/Mailtrain-org/mailtrain/archive/master.zip)) and open Mailtrain folder `cd mailtrain`
|
||||
2. Run `npm install --production` in the Mailtrain folder to install required dependencies
|
||||
|
@ -56,7 +58,4 @@ For more information, please [read the docs](http://docs.mailtrain.org/).
|
|||
|
||||
## License
|
||||
|
||||
* Versions 1.22.0 and up **GPL-V3.0**
|
||||
* Versions 1.21.0 and up: **EUPL-1.1**
|
||||
* Versions 1.19.0 and up: **MIT**
|
||||
* Up to versions 1.18.0 **GPL-V3.0**
|
||||
**GPL-V3.0**
|
||||
|
|
9
TODO.md
9
TODO.md
|
@ -1,18 +1,9 @@
|
|||
### Front page
|
||||
- Some dashboard
|
||||
|
||||
### Templates
|
||||
- Add MJML template editor
|
||||
- Include Grapesjs with MJML support
|
||||
- CKEditor to sandbox
|
||||
- Add Files support to CKEditor
|
||||
|
||||
### Message delivery
|
||||
- Better integration with ZoneMTA to allow multiple send configurations (with different DKIM) against one ZoneMTA instance via different HTTP configuration of ZoneMTA. This may need an extension of ZoneMTA to provide some header entry that identifies the campaign.
|
||||
|
||||
### Lists
|
||||
- CSV Export
|
||||
|
||||
### Campaigns
|
||||
- Statistics for a sent campaign
|
||||
- List of sent RSS campaigns (?)
|
||||
|
|
3
client/package-lock.json
generated
3
client/package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mailtrain-client",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -5319,7 +5319,6 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mailtrain-client",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Self hosted email newsletter app - client components",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
@ -11,7 +11,6 @@
|
|||
"type": "git",
|
||||
"url": "git://github.com/Mailtrain-org/mailtrain.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
"homepage": "https://mailtrain.org/",
|
||||
"dependencies": {
|
||||
|
|
|
@ -43,9 +43,9 @@ export function getMailerTypes(t) {
|
|||
function validateNumber(state, field, label, emptyAllowed = false) {
|
||||
const value = state.getIn([field, 'value']);
|
||||
if (typeof value === 'string' && value.trim() === '' && !emptyAllowed) { // After load, the numerical values can be still numbers
|
||||
state.setIn([field, 'error'], t(`${label} must not be empty`));
|
||||
state.setIn([field, 'error'], t('{{label}} must not be empty', {label}));
|
||||
} else if (isNaN(value)) {
|
||||
state.setIn([field, 'error'], t(`${label} must be a number`));
|
||||
state.setIn([field, 'error'], t('{{label}} must be a number', {label}));
|
||||
} else {
|
||||
state.setIn([field, 'error'], null);
|
||||
}
|
||||
|
|
|
@ -578,6 +578,9 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
{owner.templateTypes[typeKey].getHTMLEditor(owner)}
|
||||
|
||||
<ACEEditor id={prefix + 'text'} height="400px" mode="text" label={t('Template content (plain text)')} help={<Trans>To extract the text from HTML click <ActionLink onClickAsync={::owner.extractPlainText}>here</ActionLink>. Please note that your existing plaintext in the field above will be overwritten. This feature uses the <a href="http://premailer.dialect.ca/api">Premailer API</a>, a third party service. Their Terms of Service and Privacy Policy apply.</Trans>}/>
|
||||
/*prefix:helpers*/<Trans i18nKey="userMessagesUnread" count={count}>
|
||||
Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.
|
||||
</Trans>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
FROM node:8.6
|
||||
|
||||
# First install dependencies
|
||||
COPY ./package.json ./app/
|
||||
COPY ../server/package.json ./app/
|
||||
WORKDIR /app/
|
||||
ENV NODE_ENV production
|
||||
RUN npm install --no-progress --production && npm install --no-progress passport-ldapjs passport-ldapauth
|
||||
# Later, copy the app files. That improves development speed as buiding the Docker image will not have
|
||||
# to download and install all the NPM dependencies every time there's a change in the source code
|
||||
COPY . /app
|
||||
COPY .. /app
|
||||
EXPOSE 3000
|
||||
ENTRYPOINT ["bash", "/app/docker-entrypoint.sh"]
|
||||
CMD ["node", "index.js"]
|
|
@ -1,10 +1,11 @@
|
|||
# Mailtrain
|
||||
# Mailtrain v2
|
||||
|
||||
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v7+) and MySQL (v5.5+ or MariaDB).
|
||||
[Mailtrain](http://mailtrain.org) is a self hosted newsletter application built on Node.js (v10+) and MySQL (v8+) or MariaDB (v10+).
|
||||
|
||||

|
||||
|
||||
> Mailtrain requires at least **Node.js v7**. If you want to use an older version of Node.js then you should use version v1.24 of Mailtrain. You can either download it [here](https://github.com/Mailtrain-org/mailtrain/archive/v1.24.0.zip) or if using git then run `git checkout v1.24.0` before starting it
|
||||
FIXME: The info below needs to be updated !!!
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -95,58 +95,6 @@
|
|||
"namespace": {
|
||||
"mustBeSelected": "Namespace must be selected"
|
||||
},
|
||||
"": {
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": ""
|
||||
},
|
||||
"": {
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": ""
|
||||
},
|
||||
"": {
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": ""
|
||||
},
|
||||
"": {
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": ""
|
||||
},
|
||||
|
||||
"close": "Close",
|
||||
"name": "Name",
|
||||
|
@ -158,7 +106,6 @@
|
|||
"loading": "Loading ...",
|
||||
"email": "Email",
|
||||
"update": "Update",
|
||||
"": "",
|
||||
|
||||
"namespace": "Namespace",
|
||||
"namespace_plural": "Namespaces",
|
||||
|
@ -190,17 +137,7 @@
|
|||
"validationInProgress": "Validation is in progress...",
|
||||
"errorsInForm": "There are errors in the form. Please fix them and submit again.",
|
||||
"updatesCannotBeSaved": "Your updates cannot be saved.",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
"": "",
|
||||
|
||||
"mjml": "MJML",
|
||||
"html": "HTML",
|
||||
"": "",
|
||||
"": "",
|
||||
"": ""
|
||||
"html": "HTML"
|
||||
}
|
297
locales/extract.js
Normal file
297
locales/extract.js
Normal file
|
@ -0,0 +1,297 @@
|
|||
'use strict';
|
||||
|
||||
// Example:
|
||||
// tUI(/*prefix:account*/'account.passwordChangeRequest', language)
|
||||
// /*prefix:helpers*/<Trans i18nKey="userMessagesUnread" count={count}>Hello <strong title={t('nameTitle')}>{{name}}</strong>, you have {{count}} unread message. <Link to="/msgs">Go to messages</Link>.</Trans>
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const klawSync = require('klaw-sync');
|
||||
const acorn = require("acorn");
|
||||
const acornJsx = require("acorn-jsx");
|
||||
const ellipsize = require('ellipsize');
|
||||
const camelCase = require('camelcase');
|
||||
const slugify = require('slugify');
|
||||
const readline = require('readline');
|
||||
|
||||
const localeFile = 'common/en.json';
|
||||
const searchDirs = [
|
||||
'../client/src',
|
||||
'../server',
|
||||
'../shared'
|
||||
];
|
||||
|
||||
function findInDict(dict, key) {
|
||||
const keyElems = key.split('.');
|
||||
|
||||
let val = dict;
|
||||
for (const keyElem of keyElems) {
|
||||
if (val) {
|
||||
val = val[keyElem];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
function setInDict(dict, key, value) {
|
||||
const keyElems = key.split('.');
|
||||
|
||||
let val = dict;
|
||||
for (const keyElem of keyElems.slice(0, -1)) {
|
||||
if (val[keyElem]) {
|
||||
if (typeof val[keyElem] === 'string') {
|
||||
throw new Error(`Overlapping key ${key}`);
|
||||
}
|
||||
} else {
|
||||
val[keyElem] = {}
|
||||
}
|
||||
|
||||
val = val[keyElem];
|
||||
}
|
||||
|
||||
val[keyElems[keyElems.length - 1]] = value;
|
||||
}
|
||||
|
||||
const assignedKeys = new Map();
|
||||
function getKeyFromValue(spec, value) {
|
||||
let key = value.replace(/<\/?[0-9]+>/g, ''); // Remove Trans markup
|
||||
key = slugify(key, { replacement: ' ', remove: /[()"':.,;\[\]\{\}*+-]/g, lower: false });
|
||||
key = camelCase(key);
|
||||
key = ellipsize(key, 40, {
|
||||
chars: [...Array(26)].map((_, i) => String.fromCharCode('A'.charCodeAt(0) + i)) /* This is an array of characters A-Z */,
|
||||
ellipse: ''
|
||||
});
|
||||
|
||||
if (spec.prefix) {
|
||||
key = spec.prefix + '.' + key;
|
||||
}
|
||||
|
||||
let idx = 0;
|
||||
while (true) {
|
||||
const keyExt = key + (idx ? '-' + idx : '')
|
||||
if (assignedKeys.has(keyExt)) {
|
||||
if (assignedKeys.get(keyExt) === value) {
|
||||
assignedKeys.set(key, value);
|
||||
return keyExt;
|
||||
}
|
||||
} else {
|
||||
assignedKeys.set(key, value);
|
||||
return keyExt;
|
||||
}
|
||||
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
function allowedDirOrFile(item) {
|
||||
const pp = path.parse(item.path)
|
||||
|
||||
return (
|
||||
(item.stats.isDirectory() &&
|
||||
pp.base !== 'node_modules'
|
||||
) ||
|
||||
(item.stats.isFile() &&
|
||||
( pp.ext === '.js' || pp.ext === '.jsx')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function parseSpec(specStr) {
|
||||
const spec = {};
|
||||
|
||||
if (specStr) {
|
||||
const entryMatcher = /([a-zA-Z]*)\s*:\s*(.*)/
|
||||
|
||||
const entries = specStr.split(/\s*,\s*/);
|
||||
for (const entry of entries) {
|
||||
const elems = entry.match(entryMatcher);
|
||||
if (elems) {
|
||||
spec[elems[1]] = elems[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return spec;
|
||||
}
|
||||
|
||||
// see http://blog.stevenlevithan.com/archives/match-quoted-string
|
||||
const tMatcher = /(^|[ {+(=])((?:tUI|tLog|t|tMark)\s*\(\s*(?:\/\*(.*?)\*\/)?\s*)(["'])((?:(?!\1)[^\\]|\\.)*)(\4)/;
|
||||
const transMatcher = /(\/\*(.*?)\*\/\s*)?(\<Trans[ >][\s\S]*?\<\/Trans\>)/;
|
||||
|
||||
const jsxParser = acorn.Parser.extend(acornJsx());
|
||||
function parseTrans(fragment) {
|
||||
const match = fragment.match(transMatcher);
|
||||
const spec = parseSpec(match[2]);
|
||||
const jsxStr = match[3];
|
||||
|
||||
const jsxStrSmpl = jsxStr.replace('{::', '{ '); // Acorn does not handle bind (::) operator. So we just leave it out because we are not interested in the code anyway.
|
||||
const ast = jsxParser.parse(jsxStrSmpl);
|
||||
|
||||
function convertChildren(children) {
|
||||
const entries = [];
|
||||
let childNo = 0;
|
||||
|
||||
for (const child of children) {
|
||||
const type = child.type;
|
||||
|
||||
if (type === 'JSXText') {
|
||||
entries.push(child.value);
|
||||
childNo++;
|
||||
|
||||
} else if (type === 'JSXElement') {
|
||||
const inner = convertChildren(child.children);
|
||||
entries.push(`<${childNo}>${convertChildren(child.children)}</${childNo}>`);
|
||||
childNo++;
|
||||
|
||||
} else if (type === 'JSXExpressionContainer') {
|
||||
entries.push(jsxStr.substring(child.start, child.end));
|
||||
childNo++;
|
||||
|
||||
} else {
|
||||
throw new Error('Unknown JSX node: ' + child);
|
||||
}
|
||||
}
|
||||
|
||||
return entries.join('');
|
||||
}
|
||||
|
||||
const expr = ast.body[0].expression;
|
||||
|
||||
let originalKey;
|
||||
for (const attr of expr.openingElement.attributes) {
|
||||
const name = attr.name.name;
|
||||
if (name === 'i18nKey') {
|
||||
originalKey = attr.value.value;
|
||||
}
|
||||
}
|
||||
|
||||
const convValue = convertChildren(expr.children);
|
||||
|
||||
if (originalKey === undefined) {
|
||||
originalKey = convValue;
|
||||
}
|
||||
|
||||
let value;
|
||||
const originalValue = findInDict(originalResDict, originalKey);
|
||||
|
||||
if (originalValue === undefined) {
|
||||
value = convValue;
|
||||
} else {
|
||||
value = originalValue;
|
||||
}
|
||||
|
||||
const key = getKeyFromValue(spec, value);
|
||||
|
||||
const replacement = `${match[1] || ''}<Trans i18nKey="${key}">${jsxStr.substring(expr.openingElement.end, expr.closingElement.start)}</Trans>`;
|
||||
|
||||
return { key, originalKey, value, replacement };
|
||||
}
|
||||
|
||||
|
||||
function parseT(fragment) {
|
||||
const match = fragment.match(tMatcher);
|
||||
|
||||
const originalKey = match[5];
|
||||
const spec = parseSpec(match[3]);
|
||||
|
||||
// console.log(`${file}: ${line}`);
|
||||
// console.log(` |${match[1]}|${match[2]}|${match[4]}|${match[5]}|${match[6]}| - ${JSON.stringify(spec)}`);
|
||||
|
||||
let value;
|
||||
const originalValue = findInDict(originalResDict, originalKey);
|
||||
|
||||
if (originalValue === undefined) {
|
||||
value = originalKey;
|
||||
} else {
|
||||
value = originalValue;
|
||||
}
|
||||
|
||||
const key = getKeyFromValue(spec, value);
|
||||
|
||||
const replacement = `${match[1]}${match[2]}${match[4]}${key}${match[6]}`;
|
||||
|
||||
return { key, originalKey, value, originalValue, replacement };
|
||||
}
|
||||
|
||||
const renamedKeys = new Map();
|
||||
const resDict = {};
|
||||
let anyUpdatesToResDict = false;
|
||||
|
||||
function processFile(file) {
|
||||
let source = fs.readFileSync(file, 'utf8');
|
||||
let anyUpdates = false;
|
||||
|
||||
function update(fragments, parseFun) {
|
||||
if (fragments) {
|
||||
for (const fragment of fragments) {
|
||||
const {key, originalKey, value, originalValue, replacement} = parseFun(fragment);
|
||||
console.log(`${key} <- ${originalKey} | ${value} <- ${originalValue} | ${fragment} -> ${replacement}`);
|
||||
|
||||
source = source.split(fragment).join(replacement);
|
||||
setInDict(resDict, key, value);
|
||||
|
||||
if (originalKey) {
|
||||
renamedKeys.set(originalKey, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const lines = source.split(/\r?\n/g);
|
||||
for (const line of lines) {
|
||||
const fragments = line.match(new RegExp(tMatcher, 'g'));
|
||||
update(fragments, parseT);
|
||||
}
|
||||
|
||||
const fragments = source.match(new RegExp(transMatcher, 'g'));
|
||||
update(fragments, parseTrans);
|
||||
|
||||
if (false) {
|
||||
console.log(`Updating ${file}`);
|
||||
fs.writeFileSync(file, source);
|
||||
|
||||
anyUpdatesToResDict = true;
|
||||
}
|
||||
}
|
||||
|
||||
const originalResDict = JSON.parse(fs.readFileSync(localeFile));
|
||||
|
||||
function run() {
|
||||
/*
|
||||
for (const dir of searchDirs) {
|
||||
const files = klawSync(dir, { nodir: true, filter: allowedDirOrFile })
|
||||
|
||||
for (const file of files) {
|
||||
processFile(file.path);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
processFile('../client/src/templates/helpers.js');
|
||||
|
||||
if (anyUpdatesToResDict) {
|
||||
console.log(`Updating ${localeFile}`);
|
||||
fs.writeFileSync(localeFile, JSON.stringify(resDict));
|
||||
}
|
||||
}
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
run();
|
||||
|
||||
/*
|
||||
console.log('This script does modifications in the source tree. You should first commit all your files in git before proceeding.');
|
||||
rl.question('To proceed type YES: ', (answer) => {
|
||||
if (answer === 'YES') {
|
||||
run();
|
||||
}
|
||||
|
||||
rl.close();
|
||||
});
|
||||
*/
|
46
locales/package-lock.json
generated
Normal file
46
locales/package-lock.json
generated
Normal file
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "mailtrain-locales-extractor",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"acorn": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz",
|
||||
"integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg=="
|
||||
},
|
||||
"acorn-jsx": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.0.tgz",
|
||||
"integrity": "sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg=="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA=="
|
||||
},
|
||||
"ellipsize": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ellipsize/-/ellipsize-0.1.0.tgz",
|
||||
"integrity": "sha1-nUNoLUS5GtFuvYQmisEDFwplU/g="
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.15",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
|
||||
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
|
||||
},
|
||||
"klaw-sync": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
|
||||
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.11"
|
||||
}
|
||||
},
|
||||
"slugify": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/slugify/-/slugify-1.3.3.tgz",
|
||||
"integrity": "sha512-aFvcXobuowA7RqU4IBVJvqmhkREDIqsj4oIJKk6JuZ5EO1PCwtAAwDCl8TdsMs4J9zCoDAVkB9FLUElDjNcRSg=="
|
||||
}
|
||||
}
|
||||
}
|
21
locales/package.json
Normal file
21
locales/package.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "mailtrain-locales-extractor",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"description": "Extractor for t-functions in the code (client and server)",
|
||||
"main": "extract.js",
|
||||
"scripts": {},
|
||||
"license": "GPL-3.0",
|
||||
"homepage": "https://mailtrain.org/",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": "^6.0.4",
|
||||
"acorn-jsx": "^5.0.0",
|
||||
"camelcase": "^5.0.0",
|
||||
"ellipsize": "^0.1.0",
|
||||
"klaw-sync": "^6.0.0",
|
||||
"slugify": "^1.3.3"
|
||||
}
|
||||
}
|
7
server/.gitignore
vendored
Normal file
7
server/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/config/development.*
|
||||
/config/production.*
|
||||
/config/test.*
|
||||
/workers/reports/config/development.*
|
||||
/workers/reports/config/production.*
|
||||
/workers/reports/config/test.*
|
||||
/files
|
29
server/Gruntfile.js
Normal file
29
server/Gruntfile.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
eslint: {
|
||||
all: [
|
||||
'lib/**/*.js',
|
||||
'models/**/*.js',
|
||||
'routes/**/*.js',
|
||||
'services/**/*.js',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'workers/**/*.js',
|
||||
'app-builder.js',
|
||||
'index.js',
|
||||
'Gruntfile.js',
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Load the plugin(s)
|
||||
grunt.loadNpmTasks('grunt-eslint');
|
||||
grunt.task.loadTasks('tasks');
|
||||
|
||||
// Tasks
|
||||
grunt.registerTask('default', ['eslint']);
|
||||
};
|
|
@ -57,10 +57,10 @@ const settingsRest = require('./routes/rest/settings');
|
|||
|
||||
const index = require('./routes/index');
|
||||
|
||||
const interoperableErrors = require('./shared/interoperable-errors');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
|
||||
const { getTrustedUrl } = require('./lib/urls');
|
||||
const { AppType } = require('./shared/app');
|
||||
const { AppType } = require('../shared/app');
|
||||
|
||||
hbs.registerPartials(__dirname + '/views/partials');
|
||||
hbs.registerPartials(__dirname + '/views/subscription/partials/');
|
24
server/config/test.toml
Normal file
24
server/config/test.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
[www]
|
||||
port=3000
|
||||
[mysql]
|
||||
user="mailtrain_test"
|
||||
password="bahquaiphoor"
|
||||
database="mailtrain_test"
|
||||
[testServer]
|
||||
enabled=true
|
||||
[seleniumWebDriver]
|
||||
browser="phantomjs"
|
||||
[ldap]
|
||||
# enable to use ldap user backend
|
||||
enabled=false
|
||||
host="localhost"
|
||||
port=3002
|
||||
baseDN="ou=users,dc=example"
|
||||
filter="(|(username={{username}})(mail={{username}}))"
|
||||
#Username field in LDAP (uid/cn/username)
|
||||
uidTag="username"
|
||||
# nameTag identifies the attribute to be used for user's full name
|
||||
nameTag="username"
|
||||
passwordresetlink="xxx"
|
||||
[reports]
|
||||
enabled=true
|
|
@ -18,7 +18,7 @@ const executor = require('./lib/executor');
|
|||
const privilegeHelpers = require('./lib/privilege-helpers');
|
||||
const knex = require('./lib/knex');
|
||||
const shares = require('./models/shares');
|
||||
const { AppType } = require('./shared/app');
|
||||
const { AppType } = require('../shared/app');
|
||||
|
||||
const trustedPort = config.www.trustedPort;
|
||||
const sandboxPort = config.www.sandboxPort;
|
|
@ -1,23 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('config');
|
||||
const mailers = require('../lib/mailers');
|
||||
const knex = require('../lib/knex');
|
||||
const mailers = require('./mailers');
|
||||
const knex = require('./knex');
|
||||
const subscriptions = require('../models/subscriptions');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const contextHelpers = require('./context-helpers');
|
||||
const campaigns = require('../models/campaigns');
|
||||
const templates = require('../models/templates');
|
||||
const lists = require('../models/lists');
|
||||
const fields = require('../models/fields');
|
||||
const sendConfigurations = require('../models/send-configurations');
|
||||
const links = require('../models/links');
|
||||
const {CampaignSource, CampaignType} = require('../shared/campaigns');
|
||||
const {SubscriptionStatus} = require('../shared/lists');
|
||||
const tools = require('../lib/tools');
|
||||
const {CampaignSource, CampaignType} = require('../../shared/campaigns');
|
||||
const {SubscriptionStatus} = require('../../shared/lists');
|
||||
const tools = require('./tools');
|
||||
const request = require('request-promise');
|
||||
const files = require('../models/files');
|
||||
const htmlToText = require('html-to-text');
|
||||
const {getPublicUrl} = require('../lib/urls');
|
||||
const {getPublicUrl} = require('./urls');
|
||||
const blacklist = require('../models/blacklist');
|
||||
const libmime = require('libmime');
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const knex = require('../lib/knex');
|
||||
const knex = require('./knex');
|
||||
|
||||
function getRequestContext(req) {
|
||||
const context = {
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const knex = require('./knex');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const entitySettings = require('./entity-settings');
|
||||
const shares = require('../models/shares');
|
||||
const { enforce } = require('./helpers');
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const knex = require('../lib/knex');
|
||||
const knex = require('./knex');
|
||||
const entitySettings = require('./entity-settings');
|
||||
|
||||
async function ajaxListTx(tx, params, queryFun, columns, options) {
|
|
@ -4,7 +4,7 @@ const knex = require('./knex');
|
|||
const fork = require('child_process').fork;
|
||||
const log = require('./log');
|
||||
const path = require('path');
|
||||
const {ImportStatus, RunStatus} = require('../shared/imports');
|
||||
const {ImportStatus, RunStatus} = require('../../shared/imports');
|
||||
|
||||
let messageTid = 0;
|
||||
let importerProcess;
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const config = require('config');
|
||||
|
||||
const knex = require('knex')({
|
||||
const knex = require('server/lib/knex')({
|
||||
client: 'mysql2',
|
||||
connection: config.mysql,
|
||||
migrations: {
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const { enforce } = require('./helpers');
|
||||
const shares = require('../models/shares');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
|
||||
async function validateEntity(tx, entity) {
|
||||
enforce(entity.namespace, 'Entity namespace not set');
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const nodeify = require('nodeify');
|
||||
const nodeify = require('server/lib/nodeify');
|
||||
|
||||
module.exports.nodeifyPromise = nodeify;
|
||||
|
|
@ -5,7 +5,7 @@ const log = require('./log');
|
|||
const _ = require('./translate')._;
|
||||
const util = require('util');
|
||||
|
||||
const passport = require('passport');
|
||||
const passport = require('server/lib/passport');
|
||||
const LocalStrategy = require('passport-local').Strategy;
|
||||
|
||||
const csrf = require('csurf');
|
||||
|
@ -13,7 +13,7 @@ const bodyParser = require('body-parser');
|
|||
|
||||
const users = require('../models/users');
|
||||
const { nodeifyFunction, nodeifyPromise } = require('./nodeify');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const contextHelpers = require('./context-helpers');
|
||||
|
||||
let authMode = 'local';
|
|
@ -3,7 +3,7 @@
|
|||
const log = require('./log');
|
||||
const reports = require('../models/reports');
|
||||
const executor = require('./executor');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const contextHelpers = require('./context-helpers');
|
||||
|
||||
let runningWorkersCount = 0;
|
||||
let maxWorkersCount = 1;
|
|
@ -3,8 +3,8 @@
|
|||
const fork = require('child_process').fork;
|
||||
const log = require('./log');
|
||||
const path = require('path');
|
||||
const knex = require('../lib/knex');
|
||||
const {CampaignStatus} = require('../shared/campaigns');
|
||||
const knex = require('./knex');
|
||||
const {CampaignStatus} = require('../../shared/campaigns');
|
||||
|
||||
let messageTid = 0;
|
||||
let senderProcess;
|
|
@ -4,10 +4,10 @@ const log = require('npmlog');
|
|||
const fields = require('../models/fields');
|
||||
const settings = require('../models/settings');
|
||||
const {getTrustedUrl, getPublicUrl} = require('./urls');
|
||||
const { tUI } = require('./translate');
|
||||
const { tUI, tMark } = require('./translate');
|
||||
const util = require('util');
|
||||
const contextHelpers = require('./context-helpers');
|
||||
const {getFieldColumn} = require('../shared/lists');
|
||||
const {getFieldColumn} = require('../../shared/lists');
|
||||
const forms = require('../models/forms');
|
||||
const mailers = require('./mailers');
|
||||
|
||||
|
@ -26,7 +26,7 @@ async function sendSubscriptionConfirmed(lang, list, email, subscription) {
|
|||
unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid
|
||||
};
|
||||
|
||||
await _sendMail(list, email, 'subscription_confirmed', lang, 'subscription.confirmed', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'subscription_confirmed', lang, tMark('subscription.confirmed'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
async function sendAlreadySubscribed(lang, list, email, subscription) {
|
||||
|
@ -34,35 +34,35 @@ async function sendAlreadySubscribed(lang, list, email, subscription) {
|
|||
preferencesUrl: '/subscription/' + list.cid + '/manage/' + subscription.cid,
|
||||
unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid
|
||||
};
|
||||
await _sendMail(list, email, 'already_subscribed', lang, 'subscription.alreadyRegistered', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'already_subscribed', lang, tMark('subscription.alreadyRegistered'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
async function sendConfirmAddressChange(lang, list, email, cid, subscription) {
|
||||
const relativeUrls = {
|
||||
confirmUrl: '/subscription/confirm/change-address/' + cid
|
||||
};
|
||||
await _sendMail(list, email, 'confirm_address_change', lang, 'subscription.confirmEmailChange', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'confirm_address_change', lang, tMark('subscription.confirmEmailChange'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
async function sendConfirmSubscription(lang, list, email, cid, subscription) {
|
||||
const relativeUrls = {
|
||||
confirmUrl: '/subscription/confirm/subscribe/' + cid
|
||||
};
|
||||
await _sendMail(list, email, 'confirm_subscription', lang, 'subscription.confirmSubscription', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'confirm_subscription', lang, tMark('subscription.confirmSubscription'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
async function sendConfirmUnsubscription(lang, list, email, cid, subscription) {
|
||||
const relativeUrls = {
|
||||
confirmUrl: '/subscription/confirm/unsubscribe/' + cid
|
||||
};
|
||||
await _sendMail(list, email, 'confirm_unsubscription', lang, 'subscription.confirmUnsubscription', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'confirm_unsubscription', lang, tMark('subscription.confirmUnsubscription'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
async function sendUnsubscriptionConfirmed(lang, list, email, subscription) {
|
||||
const relativeUrls = {
|
||||
subscribeUrl: '/subscription/' + list.cid + '?cid=' + subscription.cid
|
||||
};
|
||||
await _sendMail(list, email, 'unsubscription_confirmed', lang, 'subscription.unsubscriptionConfirmed', relativeUrls, subscription);
|
||||
await _sendMail(list, email, 'unsubscription_confirmed', lang, tMark('subscription.unsubscriptionConfirmed'), relativeUrls, subscription);
|
||||
}
|
||||
|
||||
function getDisplayName(flds, subscription) {
|
||||
|
@ -148,7 +148,7 @@ async function _sendMail(list, email, template, language, subjectKey, relativeUr
|
|||
name: getDisplayName(flds, subscription),
|
||||
address: email
|
||||
},
|
||||
subject: tUI(language, subjectKey, { list: list.name }),
|
||||
subject: tUI(subjectKey, language, { list: list.name }),
|
||||
encryptionKeys
|
||||
}, {
|
||||
html,
|
|
@ -93,7 +93,7 @@ async function validateEmail(address) {
|
|||
function validateEmailGetMessage(result, address, language) {
|
||||
let t;
|
||||
if (language) {
|
||||
t = (key, args) => tUI(language, key, args);
|
||||
t = (key, args) => tUI(key, language, args);
|
||||
} else {
|
||||
t = (key, args) => tLog(key, args);
|
||||
}
|
|
@ -34,7 +34,7 @@ function tLog(key, args) {
|
|||
return JSON.stringify([key, args]);
|
||||
}
|
||||
|
||||
function tUI(lang, key, args) {
|
||||
function tUI(key, lang, args) {
|
||||
if (!args) {
|
||||
args = {};
|
||||
}
|
||||
|
@ -42,5 +42,10 @@ function tUI(lang, key, args) {
|
|||
return i18n.t(key, { ...args, defaultValue, lng: lang });
|
||||
}
|
||||
|
||||
function tMark(key) {
|
||||
return key;
|
||||
}
|
||||
|
||||
module.exports.tLog = tLog;
|
||||
module.exports.tUI = tUI;
|
||||
module.exports.tMark = tMark;
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const config = require('config');
|
||||
const urllib = require('url');
|
||||
const {anonymousRestrictedAccessToken} = require('../shared/urls');
|
||||
const {anonymousRestrictedAccessToken} = require('../../shared/urls');
|
||||
|
||||
function getTrustedUrlBase() {
|
||||
return urllib.resolve(config.www.trustedUrlBase, '');
|
|
@ -3,17 +3,17 @@
|
|||
const knex = require('../lib/knex');
|
||||
const hasher = require('node-object-hash')();
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shortid = require('shortid');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const shares = require('./shares');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const files = require('./files');
|
||||
const templates = require('./templates');
|
||||
const { CampaignStatus, CampaignSource, CampaignType, getSendConfigurationPermissionRequiredForSend} = require('../shared/campaigns');
|
||||
const { CampaignStatus, CampaignSource, CampaignType, getSendConfigurationPermissionRequiredForSend} = require('../../shared/campaigns');
|
||||
const sendConfigurations = require('./send-configurations');
|
||||
const triggers = require('./triggers');
|
||||
const {SubscriptionStatus} = require('../shared/lists');
|
||||
const {SubscriptionStatus} = require('../../shared/lists');
|
||||
const subscriptions = require('./subscriptions');
|
||||
const segments = require('./segments');
|
||||
const senders = require('../lib/senders');
|
|
@ -5,17 +5,17 @@ const hasher = require('node-object-hash')();
|
|||
const slugify = require('slugify');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const validators = require('../shared/validators');
|
||||
const validators = require('../../shared/validators');
|
||||
const shortid = require('shortid');
|
||||
const segments = require('./segments');
|
||||
const { formatDate, formatBirthday, parseDate, parseBirthday } = require('../shared/date');
|
||||
const { getFieldColumn } = require('../shared/lists');
|
||||
const { formatDate, formatBirthday, parseDate, parseBirthday } = require('../../shared/date');
|
||||
const { getFieldColumn } = require('../../shared/lists');
|
||||
const { cleanupFromPost } = require('../lib/helpers');
|
||||
const Handlebars = require('handlebars');
|
||||
const { getTrustedUrl, getSandboxUrl, getPublicUrl } = require('../lib/urls');
|
||||
const { getMergeTagsForBases } = require('../shared/templates');
|
||||
const { getMergeTagsForBases } = require('../../shared/templates');
|
||||
|
||||
|
||||
const allowedKeysCreate = new Set(['name', 'key', 'default_value', 'type', 'group', 'settings']);
|
|
@ -6,7 +6,7 @@ const dtHelpers = require('../lib/dt-helpers');
|
|||
const shares = require('./shares');
|
||||
const fs = require('fs-extra-promise');
|
||||
const path = require('path');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const entitySettings = require('../lib/entity-settings');
|
||||
const {getPublicUrl} = require('../lib/urls');
|
||||
|
|
@ -4,7 +4,7 @@ const knex = require('../lib/knex');
|
|||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const hasher = require('node-object-hash')();
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const bluebird = require('bluebird');
|
|
@ -3,7 +3,7 @@
|
|||
const knex = require('../lib/knex');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
|
||||
async function getById(context, listId, importId, id) {
|
|
@ -4,9 +4,9 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const {ImportSource, MappingType, ImportStatus, RunStatus, prepFinished, prepFinishedAndNotInProgress, runInProgress} = require('../shared/imports');
|
||||
const {ImportSource, MappingType, ImportStatus, RunStatus, prepFinished, prepFinishedAndNotInProgress, runInProgress} = require('../../shared/imports');
|
||||
const fs = require('fs-extra-promise');
|
||||
const path = require('path');
|
||||
const importer = require('../lib/importer');
|
|
@ -5,7 +5,7 @@ const hasher = require('node-object-hash')();
|
|||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const shortid = require('shortid');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const fields = require('./fields');
|
||||
|
@ -14,7 +14,7 @@ const imports = require('./imports');
|
|||
const entitySettings = require('../lib/entity-settings');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
|
||||
const UnsubscriptionMode = require('../shared/lists').UnsubscriptionMode;
|
||||
const UnsubscriptionMode = require('../../shared/lists').UnsubscriptionMode;
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'default_form', 'public_subscribe', 'unsubscription_mode', 'contact_email', 'homepage', 'namespace', 'to_name', 'listunsubscribe_disabled']);
|
||||
|
|
@ -4,7 +4,7 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const shares = require('./shares');
|
||||
const files = require('./files');
|
|
@ -3,7 +3,7 @@
|
|||
const knex = require('../lib/knex');
|
||||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const entitySettings = require('../lib/entity-settings');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
|
@ -4,7 +4,7 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const shares = require('./shares');
|
||||
const reports = require('./reports');
|
|
@ -4,14 +4,14 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const fields = require('./fields');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const shares = require('./shares');
|
||||
const reportHelpers = require('../lib/report-helpers');
|
||||
const fs = require('fs-extra-promise');
|
||||
|
||||
const ReportState = require('../shared/reports').ReportState;
|
||||
const ReportState = require('../../shared/reports').ReportState;
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'report_template', 'params', 'namespace']);
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const knex = require('../lib/knex');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const hasher = require('node-object-hash')();
|
||||
|
@ -11,7 +11,7 @@ const fields = require('./fields');
|
|||
const subscriptions = require('./subscriptions');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
|
||||
const { parseDate, parseBirthday, DateFormat } = require('../shared/date');
|
||||
const { parseDate, parseBirthday, DateFormat } = require('../../shared/date');
|
||||
|
||||
const allowedKeys = new Set(['name', 'settings']);
|
||||
|
|
@ -5,10 +5,10 @@ const hasher = require('node-object-hash')();
|
|||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const shortid = require('shortid');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const {MailerType, getSystemSendConfigurationId} = require('../shared/send-configurations');
|
||||
const {MailerType, getSystemSendConfigurationId} = require('../../shared/send-configurations');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const mailers = require('../lib/mailers');
|
||||
const senders = require('../lib/senders');
|
|
@ -5,10 +5,10 @@ const config = require('config');
|
|||
const { enforce } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const entitySettings = require('../lib/entity-settings');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const log = require('../lib/log');
|
||||
const {getGlobalNamespaceId} = require('../shared/namespaces');
|
||||
const {getAdminId} = require('../shared/users');
|
||||
const {getGlobalNamespaceId} = require('../../shared/namespaces');
|
||||
const {getAdminId} = require('../../shared/users');
|
||||
|
||||
// TODO: This would really benefit from some permission cache connected to rebuildPermissions
|
||||
// A bit of the problem is that the cache would have to expunged as the result of other processes modifying entites/permissions
|
|
@ -4,14 +4,14 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const shortid = require('shortid');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const fields = require('./fields');
|
||||
const { SubscriptionStatus, getFieldColumn } = require('../shared/lists');
|
||||
const { SubscriptionStatus, getFieldColumn } = require('../../shared/lists');
|
||||
const segments = require('./segments');
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const moment = require('moment');
|
||||
const { formatDate, formatBirthday } = require('../shared/date');
|
||||
const { formatDate, formatBirthday } = require('../../shared/date');
|
||||
const crypto = require('crypto');
|
||||
const campaigns = require('./campaigns');
|
||||
const lists = require('./lists');
|
|
@ -4,7 +4,7 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||
const shares = require('./shares');
|
||||
const reports = require('./reports');
|
|
@ -4,9 +4,9 @@ const knex = require('../lib/knex');
|
|||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const shares = require('./shares');
|
||||
const {EntityVals, EventVals, Entity} = require('../shared/triggers');
|
||||
const {EntityVals, EventVals, Entity} = require('../../shared/triggers');
|
||||
const campaigns = require('./campaigns');
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'entity', 'event', 'seconds', 'enabled', 'source_campaign']);
|
|
@ -4,8 +4,8 @@ const config = require('config');
|
|||
const knex = require('../lib/knex');
|
||||
const hasher = require('node-object-hash')();
|
||||
const { enforce, filterObject } = require('../lib/helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const passwordValidator = require('../shared/password-validator')();
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const passwordValidator = require('../../shared/password-validator')();
|
||||
const dtHelpers = require('../lib/dt-helpers');
|
||||
const tools = require('../lib/tools');
|
||||
const crypto = require('crypto');
|
||||
|
@ -310,7 +310,7 @@ async function sendPasswordReset(language, usernameOrEmail) {
|
|||
to: {
|
||||
address: user.email
|
||||
},
|
||||
subject: tUI(language, 'account.passwordChangeRequest')
|
||||
subject: tUI('account.passwordChangeRequest', language)
|
||||
}, {
|
||||
html: 'emails/password-reset-html.hbs',
|
||||
text: 'emails/password-reset-text.hbs',
|
176
package-lock.json → server/package-lock.json
generated
176
package-lock.json → server/package-lock.json
generated
|
@ -907,11 +907,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"brorand": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||
},
|
||||
"browser-process-hrtime": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
|
||||
|
@ -1325,24 +1320,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"compressjs": {
|
||||
"version": "github:openpgpjs/compressjs#bfbb371a34d1750afa34bfa49156461acdab79a9",
|
||||
"from": "github:openpgpjs/compressjs",
|
||||
"requires": {
|
||||
"amdefine": "~1.0.0",
|
||||
"commander": "~2.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
|
||||
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
|
||||
"requires": {
|
||||
"graceful-readlink": ">= 1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
@ -1888,19 +1865,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"from": "github:openpgpjs/elliptic",
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"email-addresses": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.0.1.tgz",
|
||||
|
@ -2906,11 +2870,13 @@
|
|||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -2923,15 +2889,18 @@
|
|||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -3034,7 +3003,8 @@
|
|||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -3044,6 +3014,7 @@
|
|||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -3056,17 +3027,20 @@
|
|||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -3083,6 +3057,7 @@
|
|||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -3155,7 +3130,8 @@
|
|||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -3165,6 +3141,7 @@
|
|||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -3270,6 +3247,7 @@
|
|||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -3569,11 +3547,6 @@
|
|||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
|
||||
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
|
||||
},
|
||||
"graceful-readlink": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
|
||||
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
|
||||
},
|
||||
"growl": {
|
||||
"version": "1.10.5",
|
||||
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
|
||||
|
@ -3959,16 +3932,6 @@
|
|||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||
"requires": {
|
||||
"hash.js": "^1.0.3",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"homedir-polyfill": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
|
||||
|
@ -5415,11 +5378,6 @@
|
|||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||
},
|
||||
"minimalistic-crypto-utils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
|
@ -6271,6 +6229,35 @@
|
|||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
|
||||
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
|
||||
"requires": {
|
||||
"graceful-readlink": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"compressjs": {
|
||||
"version": "github:openpgpjs/compressjs#bfbb371a34d1750afa34bfa49156461acdab79a9",
|
||||
"from": "github:openpgpjs/compressjs#bfbb371a34d1750afa34bfa49156461acdab79a9",
|
||||
"requires": {
|
||||
"amdefine": "~1.0.0",
|
||||
"commander": "~2.8.1"
|
||||
}
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"from": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"openpgp": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/openpgp/-/openpgp-3.0.11.tgz",
|
||||
|
@ -6281,8 +6268,6 @@
|
|||
"asn1.js": "^5.0.0",
|
||||
"bn.js": "^4.11.8",
|
||||
"buffer": "^5.0.8",
|
||||
"compressjs": "github:openpgpjs/compressjs#bfbb371a34d1750afa34bfa49156461acdab79a9",
|
||||
"elliptic": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"hash.js": "^1.1.3",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-localstorage": "~1.3.0",
|
||||
|
@ -7466,22 +7451,18 @@
|
|||
"requires": {
|
||||
"@mattiasbuelens/web-streams-polyfill": "0.1.0-alpha.4",
|
||||
"address-rfc2822": "^2.0.3",
|
||||
"asmcrypto.js": "github:openpgpjs/asmcrypto#6e4e407b9b8ae317925a9e677cc7b4de3e447e83",
|
||||
"asn1.js": "^5.0.0",
|
||||
"bn.js": "^4.11.8",
|
||||
"buffer": "^5.0.8",
|
||||
"elliptic": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"hash.js": "^1.1.3",
|
||||
"node-fetch": "^2.1.2",
|
||||
"node-localstorage": "~1.3.0",
|
||||
"pako": "^1.0.6",
|
||||
"seek-bzip": "github:openpgpjs/seek-bzip#3aca608ffedc055a1da1d898ecb244804ef32209",
|
||||
"web-stream-tools": "github:openpgpjs/web-stream-tools#9ab800d46add161db496506d67338202ad0114ce"
|
||||
"pako": "^1.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"asmcrypto.js": {
|
||||
"version": "github:openpgpjs/asmcrypto#6e4e407b9b8ae317925a9e677cc7b4de3e447e83",
|
||||
"from": "github:openpgpjs/asmcrypto"
|
||||
"from": "github:openpgpjs/asmcrypto#6e4e407b9b8ae317925a9e677cc7b4de3e447e83"
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.2.1",
|
||||
|
@ -7492,10 +7473,42 @@
|
|||
"ieee754": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
|
||||
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
|
||||
"requires": {
|
||||
"graceful-readlink": ">= 1.0.0"
|
||||
}
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"from": "github:openpgpjs/elliptic#e187e706e11fa51bcd20e46e5119054be4e2a4a6",
|
||||
"requires": {
|
||||
"bn.js": "^4.4.0",
|
||||
"brorand": "^1.0.1",
|
||||
"hash.js": "^1.0.0",
|
||||
"hmac-drbg": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"minimalistic-assert": "^1.0.0",
|
||||
"minimalistic-crypto-utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
|
||||
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
|
||||
},
|
||||
"seek-bzip": {
|
||||
"version": "github:openpgpjs/seek-bzip#3aca608ffedc055a1da1d898ecb244804ef32209",
|
||||
"from": "github:openpgpjs/seek-bzip#3aca608ffedc055a1da1d898ecb244804ef32209",
|
||||
"requires": {
|
||||
"commander": "~2.8.1"
|
||||
}
|
||||
},
|
||||
"web-stream-tools": {
|
||||
"version": "github:openpgpjs/web-stream-tools#9ab800d46add161db496506d67338202ad0114ce",
|
||||
"from": "github:openpgpjs/web-stream-tools#9ab800d46add161db496506d67338202ad0114ce"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -8354,23 +8367,6 @@
|
|||
"xmlchars": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"seek-bzip": {
|
||||
"version": "github:openpgpjs/seek-bzip#3aca608ffedc055a1da1d898ecb244804ef32209",
|
||||
"from": "github:openpgpjs/seek-bzip",
|
||||
"requires": {
|
||||
"commander": "~2.8.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
|
||||
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
|
||||
"requires": {
|
||||
"graceful-readlink": ">= 1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selenium-webdriver": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
|
||||
|
@ -9420,10 +9416,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"web-stream-tools": {
|
||||
"version": "github:openpgpjs/web-stream-tools#9ab800d46add161db496506d67338202ad0114ce",
|
||||
"from": "github:openpgpjs/web-stream-tools"
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
|
@ -2,10 +2,9 @@
|
|||
"name": "mailtrain",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"description": "Self hosted email newsletter app",
|
||||
"description": "Self hosted email newsletter app - server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "grunt",
|
||||
"start": "node index.js",
|
||||
"sqlinit": "node setup/sql/init.js",
|
||||
"sqldump": "node setup/sql/dump.js | sed -e '/^\\/\\*.*\\*\\/;$/d' -e 's/.[0-9]\\{4\\}-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]./NOW()/g' > setup/sql/mailtrain${DUMP_NAME_SUFFIX}.sql",
|
|
@ -5,13 +5,13 @@ const lists = require('../models/lists');
|
|||
const tools = require('../lib/tools');
|
||||
const blacklist = require('../models/blacklist');
|
||||
const fields = require('../models/fields');
|
||||
const { SubscriptionStatus, SubscriptionSource } = require('../shared/lists');
|
||||
const { SubscriptionStatus, SubscriptionSource } = require('../../shared/lists');
|
||||
const subscriptions = require('../models/subscriptions');
|
||||
const confirmations = require('../models/confirmations');
|
||||
const log = require('../lib/log');
|
||||
const router = require('../lib/router-async').create();
|
||||
const mailHelpers = require('../lib/subscription-mail-helpers');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const shares = require('../models/shares');
|
||||
const slugify = require('slugify');
|
|
@ -4,7 +4,7 @@ const passport = require('../lib/passport');
|
|||
const _ = require('../lib/translate')._;
|
||||
const clientHelpers = require('../lib/client-helpers');
|
||||
const { getTrustedUrl } = require('../lib/urls');
|
||||
const { AppType } = require('../shared/app');
|
||||
const { AppType } = require('../../shared/app');
|
||||
|
||||
const routerFactory = require('../lib/router-async');
|
||||
|
|
@ -4,7 +4,7 @@ const log = require('../lib/log');
|
|||
const config = require('config');
|
||||
const router = require('../lib/router-async').create();
|
||||
const links = require('../models/links');
|
||||
const interoperableErrors = require('../shared/interoperable-errors');
|
||||
const interoperableErrors = require('../../shared/interoperable-errors');
|
||||
|
||||
const trackImg = Buffer.from('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', 'base64');
|
||||
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue