Skip to content

Commit

Permalink
The settings are made from the control panel (Closes #2)
Browse files Browse the repository at this point in the history
No more parameters are exposed to the api (this may change)
  • Loading branch information
06Games committed Aug 29, 2022
1 parent e8e1175 commit 807d69e
Show file tree
Hide file tree
Showing 10 changed files with 450 additions and 94 deletions.
11 changes: 10 additions & 1 deletion .idea/deployment.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 35 additions & 0 deletions AdminAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace EvanG\Modules\MailSystem;

use Fisharebest\Webtrees\FlashMessages;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Validator;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AdminAction implements RequestHandlerInterface
{
protected MailSystem $module;

public function __construct(MailSystem $msys) { $this->module = $msys; }

public function handle(ServerRequestInterface $request): ResponseInterface
{
$params = Validator::queryParams($request);
$settings = $this->module->getSettings();

$settings->setTrees($params->array("EVANG_MAILSYSTEM_TREES"));
$settings->setUsers($params->array("EVANG_MAILSYSTEM_USERS"));
$settings->setTags($params->array("EVANG_MAILSYSTEM_TAGS"));
$settings->setEmpty($params->integer("EVANG_MAILSYSTEM_EMPTY"));
$settings->setDays($params->integer("EVANG_MAILSYSTEM_DAYS"));
$settings->setImageFormat($params->string("EVANG_MAILSYSTEM_IMAGEFORMAT"));

FlashMessages::addMessage(I18N::translate('The Mail System preferences have been updated.'), 'success');
return redirect(route(AdminPage::class));
}
}
49 changes: 49 additions & 0 deletions AdminPage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace EvanG\Modules\MailSystem;

use Fisharebest\Webtrees\Http\ViewResponseTrait;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Services\TreeService;
use Fisharebest\Webtrees\Services\UserService;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;

/**
* Edit the site preferences.
*/
class AdminPage implements RequestHandlerInterface
{
use ViewResponseTrait;

protected UserService $userService;
protected TreeService $treeService;

protected MailSystem $module;

public function __construct(MailSystem $msys)
{
$this->module = $msys;
$this->userService = app(UserService::class);
$this->treeService = app(TreeService::class);
}

/**
* @param ServerRequestInterface $request
*
* @return ResponseInterface
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
$settings = $this->module->getSettings();
$this->layout = 'layouts/administration';

$title = I18N::translate('Mail system preferences');

return $this->viewResponse($this->module->name() . '::admin', [
'title' => $title,
'settings' => $settings
]);
}
}
27 changes: 6 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
# Mail System
Sends a mail with recent changes to [Webtrees](https://github.com/fisharebest/webtrees) users
Sends a mail with recent changes to [Webtrees](https://github.com/fisharebest/webtrees) users.

To use the plugin, you just have to add a cron task (replacing `YOURWEBTREESSERVER` by the url of your Webtrees installation)
```
0 0 * * 1 wget -O - -q "YOURWEBTREESSERVER/mail-sys/send"
```
> *Make sure to configure the cron job as described in the module settings.*
## Endpoints
## APIs

* `/mail-sys/api`: a (fairly simple) api
* `/mail-sys/html`: a preview of the mail that will be sent
* `/mail-sys/send`: sends the mail

## Parameters

| Parameter | Type | Description | Default value |
|:---------:|:---------:|:------------------------------------------------------------------------------------------------------------:|:-----------------------------:|
| `users` | `list` | Users to send the mail to | All |
| `trees` | `list` | Names of trees to be considered | All |
| `days` | `integer` | The period (in days) to be considered | `7` |
| `tags` | `list` | Types of changes to be considered | `INDI,FAM` |
| `empty` | `boolean` | Display trees without changes <br>(If the value is `False` and there is no changes, the email won't be sent) | `True` |
| `title` | `string` | Customised title of the mail | `Changes in the last %s days` |
| `format` | `string` | The image format (either `png` or `svg`) | `png` |
* `/mail-sys/get`: lists all the changes with a JSON format.
* `/mail-sys/html`: a preview of the mail that will be sent.
* `/mail-sys/cron`: needs to be called regularly, it will check if a mail needs to be sent.
122 changes: 63 additions & 59 deletions RequestHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,35 @@

namespace EvanG\Modules\MailSystem;

use DateInterval;
use DateTime;
use DateTimeImmutable;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\NoReplyUser;
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Services\EmailService;
use Fisharebest\Webtrees\Services\TreeService;
use Fisharebest\Webtrees\Site;
use Fisharebest\Webtrees\SiteUser;
use Fisharebest\Webtrees\Tree;
use Fisharebest\Webtrees\User;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Database\Query\Expression;
use Illuminate\Support\Collection;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Fisharebest\Webtrees\Exceptions\HttpNotFoundException;

// Services
use Fisharebest\Webtrees\Services\TreeService;
use Fisharebest\Webtrees\Services\UserService;
use Fisharebest\Webtrees\Services\EmailService;

// Mail
use Fisharebest\Webtrees\User;
use Fisharebest\Webtrees\SiteUser;
use Fisharebest\Webtrees\NoReplyUser;

// getChanges
use Fisharebest\Webtrees\Registry;
use Fisharebest\Webtrees\Tree;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Database\Query\Expression;
use stdClass;

use Fisharebest\Webtrees\Individual;
use Fisharebest\Webtrees\Family;

class RequestHandler implements RequestHandlerInterface
{
public const ROUTE_PREFIX = 'mail-sys';
const ROUTE_PREFIX = "/mail-sys";

protected MailSystem $module;
protected array $actions;

protected UserService $users;
protected TreeService $trees;
Expand All @@ -46,87 +44,93 @@ public function __construct(MailSystem $msys)
$this->users = app(UserService::class);
$this->trees = app(TreeService::class);
$this->email = app(EmailService::class);

$this->actions = [
'help' => function () { return response($this->help()); },
'cron' => function () { return $this->cron(); },
'get' => function () { return response($this->api($this->module->getSettings())); },
'html' => function () { return response($this->html($this->module->getSettings())); },
//'send' => function () { return response($this->sendMails($this->module->getSettings())); }
];
}

public function handle(Request $request): Response
{
switch ($request->getAttribute('action')) {
case 'api':
return response($this->api($this->parseRequestArgs($request)));
case 'html':
return response($this->html($this->parseRequestArgs($request)));
case 'send':
return response($this->sendMails($this->parseRequestArgs($request)));
default:
throw new HttpNotFoundException();
$action = $request->getAttribute('action');
if (key_exists($action, $this->actions)) return $this->actions[$action]();
else return redirect(route(RequestHandler::class, ['action' => 'help']));
}

function help(): array
{
$help = [];
foreach ($this->actions as $action => $fct) {
$help[$action] = [
"name" => $action,
"url" => route(RequestHandler::class, ['action' => $action])
];
}
return $help;
}

function parseRequestArgs(Request $request): object
function cron(): Response
{
$params = $request->getQueryParams();
$getValue = function ($param, $default, $exists = null) use ($params) {
if (array_key_exists($param, $params)) return $exists === null ? $params[$param] : $exists($params[$param]);
else return $default;
};

$strToBool = function ($str) { return $str === null || $str === "" || $str === "True"; };
$strToInt = function ($str) { return intval($str); };
$strToArray = function ($str) { return explode(',', $str); };

$days = $getValue("days", 7, $strToInt);
return (object)[
'days' => $days,
'title' => $getValue("title", I18N::plural('Changes in the last %s day', 'Changes in the last %s days', $days, $days)),
'tags' => $getValue("tags", [Individual::RECORD_TYPE, Family::RECORD_TYPE], $strToArray),
'users' => $getValue("users", [], $strToArray),
'trees' => $getValue("trees", null, $strToArray),
'showEmptyTrees' => $getValue("empty", true, $strToBool),
'format' => $getValue("format", "png")
];
$settings = $this->module->getSettings();

$lastCronTxt = Site::getPreference('EVANG_MAILSYSTEM_LASTCRONDATE');
if(!empty($lastCronTxt)){
$lastCronDate = new DateTimeImmutable($lastCronTxt);
$nextCron = $lastCronDate->add(new DateInterval('P'.$settings->getDays().'D'));
$today = new DateTime("midnight");
if($today < $nextCron) return response(["message" => "Skip", "today" => $today, "next" => $nextCron]);
}

Site::setPreference('EVANG_MAILSYSTEM_LASTCRONDATE', date("Y-m-d"));
return response($this->sendMails($settings));
}

function api(object $args): array
function api(Settings $args): array
{
$changes = [];
foreach ($this->trees->all() as $tree)
if ($args->trees == null || in_array($tree->name(), $args->trees)) {
$treeChanges = $this->getChanges($tree, $args->days)
->filter(static function (stdClass $row) use ($args): bool { return in_array($row->record["tag"], $args->tags); })
if ($args->getTrees() == null || in_array($tree->name(), $args->getTrees())) {
$treeChanges = $this->getChanges($tree, $args->getDays())
->filter(static function (stdClass $row) use ($args): bool { return in_array($row->record["tag"], $args->getTags()); })
->groupBy(static function (stdClass $row) { return Registry::timestampFactory()->fromString($row->time)->format('Y-m-d'); })
->sortKeys();
if ($args->showEmptyTrees || !$treeChanges->isEmpty()) $changes[$tree->name()] = $treeChanges;
if ($args->getEmpty() || !$treeChanges->isEmpty()) $changes[$tree->name()] = $treeChanges;
}
return $changes;
}


function html(object $args): ?string
function html(Settings $args): ?string
{
$items = $this->api($args);
if (empty($items)) return null;
return view("{$this->module->name()}::email", [
'args' => $args,
'subject' => $args->title,
'subject' => I18N::plural('Changes in the last %s day', 'Changes in the last %s days', $args->getDays(), $args->getDays()),
'items' => $items,
'module' => $this->module
]);
}

function sendMails(object $args): array
function sendMails(Settings $args): array
{
$sent = [];
foreach ($this->users->all() as $user) {
if ($args->users == null || in_array($user->username(), $args->users) && $this->sendMail($user, $args)) $sent[] = $user->username();
if ($args->getUsers() == null || in_array($user->username(), $args->getUsers()) && $this->sendMail($user, $args)) $sent[] = $user->username();
}
return ["users" => $sent];
}

function sendMail(User $user, $args): bool
function sendMail(User $user, Settings $args): bool
{
$html = $this->html($args);
if ($html == null) return false;
return $this->email->send(new SiteUser(), $user, new NoReplyUser(), $args->title, strip_tags($html), $html);
return $this->email->send(new SiteUser(), $user, new NoReplyUser(),
I18N::plural('Changes in the last %s day', 'Changes in the last %s days', $args->getDays(), $args->getDays()), strip_tags($html), $html);
}

function getChanges(Tree $tree, int $days): Collection // From getRecentChangesFromDatabase in RecentChangesModule
Expand Down
Loading

0 comments on commit 807d69e

Please sign in to comment.