### English
-[![Download most recent Galette release (1.0.2)](https://img.shields.io/badge/1.0.2-Latest_Galette-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-1.0.2.tar.bz2)
+[![Download most recent Galette release (1.0.3)](https://img.shields.io/badge/1.0.3-Latest_Galette-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-1.0.3.tar.bz2)
[![Download Galette development (nightly) build](https://img.shields.io/badge/nightly-Galette_development-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-dev.tar.bz2)
Galette is a membership management web application towards non profit organizations; released under GPLv3.
### Français
-[![Télécharger la version de Galette la plus récente (1.0.2)](https://img.shields.io/badge/1.0.2-Dernière_Galette-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-1.0.2.tar.bz2)
+[![Télécharger la version de Galette la plus récente (1.0.3)](https://img.shields.io/badge/1.0.3-Dernière_Galette-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-1.0.3.tar.bz2)
[![Télécharger la version de développement (nighly) de Galette](https://img.shields.io/badge/nightly-Galette_développement-ffb619.svg?logo=php&logoColor=white&style=for-the-badge)](https://download.tuxfamily.org/galette/galette-dev.tar.bz2)
Galette est un outil de gestion d’adhérents et de cotisations en ligne à destination des associations, sous license GPLV3.
galette.close()
#set galette nightly version
- includes_dir = os.path.join(src_dir, rel_name, 'galette', 'includes')
+ config_dir = os.path.join(src_dir, rel_name, 'galette', 'config')
if nightly_version != None:
- sed_cmd = 'sed -e "s/GALETTE_NIGHTLY\', false/GALETTE_NIGHTLY\', \'%s\'/" -i galette.inc.php' % nightly_version
+ sed_cmd = 'sed -e "s/GALETTE_NIGHTLY\', false/GALETTE_NIGHTLY\', \'%s\'/" -i versions.inc.php' % nightly_version
print(sed_cmd)
- p1 = subprocess.Popen(sed_cmd, shell=True, cwd=includes_dir)
+ p1 = subprocess.Popen(sed_cmd, shell=True, cwd=config_dir)
p1.wait()
#install npm modules
define('GALETTE_COMPILE_DIR', GALETTE_DATA_PATH . 'templates_c/');
}
if (!defined('GALETTE_CACHE_DIR')) {
- define('GALETTE_CACHE_DIR', GALETTE_DATA_PATH . 'cache/');
+ define('GALETTE_CACHE_DIR', GALETTE_DATA_PATH . 'cache/' . GALETTE_VERSION . '/');
}
if (!defined('GALETTE_EXPORTS_PATH')) {
define('GALETTE_EXPORTS_PATH', GALETTE_DATA_PATH . 'exports/');
define('GALETTE_PHP_MIN', '8.1');
define('GALETTE_MYSQL_MIN', '5.7');
define('GALETTE_MARIADB_MIN', '10.4');
-define('GALETTE_PGSQL_MIN', '11') ;
\ No newline at end of file
+define('GALETTE_PGSQL_MIN', '11') ;
+define('GALETTE_NIGHTLY', false);
+define('GALETTE_VERSION', 'v1.0.3');
+define('GALETTE_COMPAT_VERSION', '1.0.0');
+define('GALETTE_DB_VERSION', '0.960');
Changes
-------
+1.0.2 -> 1.0.3
+
+* Logo in mail signature is not shown
+* Missing HTML editor for dynamic fields information field
+* Update and maintainance pages are no longer working
+* Do not throw events on mass edition
+* Make cache version dependent
+* Check preferences website is valid
+* Link to asso website from logo
+* Rework UI messages
+
1.0.1 -> 1.0.2
- Public pages access restriction (CVE-2024-24761)
$view = Twig::create(
$templates,
[
- 'cache' => rtrim(GALETTE_CACHE_DIR, DIRECTORY_SEPARATOR),
+ 'cache' => rtrim(GALETTE_CACHE_DIR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'templates',
'debug' => \Galette\Core\Galette::isDebugEnabled(),
'strict_variables' => \Galette\Core\Galette::isDebugEnabled()
]
$profiler->start();
}
-define('GALETTE_NIGHTLY', false);
-define('GALETTE_VERSION', 'v1.0.2');
-
//Version to display
if (!defined('GALETTE_HIDE_VERSION')) {
define('GALETTE_DISPLAY_VERSION', \Galette\Core\Galette::gitVersion(false));
}
-define('GALETTE_COMPAT_VERSION', '1.0.0');
-define('GALETTE_DB_VERSION', '0.960');
if (!defined('GALETTE_MODE')) {
define('GALETTE_MODE', \Galette\Core\Galette::MODE_PROD);
}
require GALETTE_ROOT . '/includes/dependencies.php';
$app->add($app->getContainer()->get('csrf'));
+/**
+ * Authentication middleware
+ */
+$authenticate = new Authenticate($container);
+
+require_once GALETTE_ROOT . 'includes/routes/main.routes.php';
+
if ($needs_update) {
$app->add(
new UpdateAndMaintenance(
$container->get('i18n'),
+ $container->get(RouteParser::class),
UpdateAndMaintenance::NEED_UPDATE
)
);
die();
}
-/**
- * Authentication middleware
- */
-$authenticate = new Authenticate($container);
-
//FIXME: remove in 1.1.0; routes/groups should call middleware directly
$showPublicPages = new \Galette\Middleware\PublicPages($container);
if (Galette::MODE_MAINT === GALETTE_MODE && !$container->get('login')->isSuperAdmin()) {
$app->add(
new UpdateAndMaintenance(
- $i18n,
+ $container->get('i18n'),
+ $container->get(RouteParser::class),
UpdateAndMaintenance::MAINTENANCE
)
);
//Telemetry update middleware
$app->add(Telemetry::class);
-require_once GALETTE_ROOT . 'includes/routes/main.routes.php';
require_once GALETTE_ROOT . 'includes/routes/authentication.routes.php';
require_once GALETTE_ROOT . 'includes/routes/management.routes.php';
require_once GALETTE_ROOT . 'includes/routes/members.routes.php';
//system information - keep old route with typo ('s' on 'information') for now (0.9.4)
$app->get(
'/system-informations',
- function ($request, $response) use ($routeparser) {
+ function ($request, $response) use ($container) {
+ $routeparser = $container->get(\Slim\Routing\RouteParser::class);
return $response
->withStatus(302)
->withHeader('Location', $routeparser->urlFor('sysinfos'));
$files_need_rw = array(
_T("Compilation") => GALETTE_COMPILE_DIR,
_T("Photos") => GALETTE_PHOTOS_PATH,
- _T("Cache") => GALETTE_CACHE_DIR,
- _T("Temporary images") => GALETTE_TEMPIMAGES_PATH,
+ _T("Cache") => str_replace(GALETTE_VERSION, '', GALETTE_CACHE_DIR),
+ _T("Temporary images") => GALETTE_TEMPIMAGES_PATH,
_T("Configuration") => GALETTE_CONFIG_PATH,
_T("Exports") => GALETTE_EXPORTS_PATH,
_T("Imports") => GALETTE_IMPORTS_PATH,
class AjaxController extends AbstractController
{
/**
- * Messages
+ * Messages as JSON array
*
* @param Request $request PSR Request
* @param Response $response PSR Response
*/
public function messages(Request $request, Response $response): Response
{
- $this->view->render(
- $response,
- 'elements/ajax_messages.html.twig'
- );
- return $response;
+ $messages = [];
+
+ $errors = $this->flash->getMessage('loginfault') ?? [];
+ $errors = array_merge($errors, $this->flash->getMessage('error_detected') ?? []);
+ $errors = array_merge($errors, $this->flash->getMessage('error') ?? []);
+
+ if (count($errors) > 0) {
+ $messages['error'] = [
+ 'title' => _T('- ERROR -'),
+ 'icon' => 'exclamation circle',
+ 'messages' => $errors
+ ];
+ }
+
+ $warnings = $this->flash->getMessage('warning_detected') ?? [];
+ $warnings = array_merge($warnings, $this->flash->getMessage('warning') ?? []);
+
+ if (count($warnings) > 0) {
+ $messages['warning'] = [
+ 'title' => _T('- WARNING -'),
+ 'icon' => 'exclamation triangle',
+ 'messages' => $warnings
+ ];
+ }
+
+ $info = $this->flash->getMessage('info_detected') ?? [];
+ $info = array_merge($info, $this->flash->getMessage('info') ?? []);
+
+ if (count($info) > 0) {
+ $messages['info'] = [
+ 'title' => '',
+ 'icon' => 'info',
+ 'messages' => $info
+ ];
+ }
+
+ $success = $this->flash->getMessage('success_detected') ?? [];
+ $success = array_merge($success, $this->flash->getMessage('succes') ?? []);
+
+ if (count($success) > 0) {
+ $messages['success'] = [
+ 'title' => '',
+ 'icon' => 'check circle outline',
+ 'messages' => $success
+ ];
+ }
+
+ return $this->withJson($response, $messages);
}
/**
}
if ($this->login->isLogged()) {
+ if (defined('NON_UTF_DBCONNECT')) {
+ $this->flash->addMessage(
+ 'warning',
+ 'It appears you are using NON_UTF_DBCONNECT constant, it will be in next major release.'
+ );
+ }
+
if (!$checkpass->isValid($password)) {
//password is no longer valid with current rules, must be changed
$this->flash->addMessage(
foreach ($members_ids as $member_id) {
$post[Adherent::PK] = (int)$member_id;
$contrib = new Contribution($this->zdb, $this->login);
+ $contrib->disableEvents();
// regular fields
$valid = $contrib->check($post, $contrib->getRequired(), $disabled);
'fields_list' => $fields_list,
'form_name' => $form_name,
'form_title' => DynamicField::getFormTitle($form_name),
- 'page_title' => _T("Dynamic fields configuration")
+ 'page_title' => _T("Dynamic fields configuration"),
+ 'html_editor' => true,
+ 'html_editor_active' => $this->preferences->pref_editor_enabled
+
];
$tpl = 'pages/configuration_dynamic_fields.html.twig';
'form_name' => $form_name,
'perm_names' => DynamicField::getPermsNames(),
'mode' => (($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') ? 'ajax' : ''),
- 'df' => $df
+ 'df' => $df,
+ 'html_editor' => true,
+ 'html_editor_active' => $this->preferences->pref_editor_enabled
];
// display page
&& !$this->login->isStaff()
&& $this->login->isGroupManager();
$member = new Adherent($this->zdb);
- $member->disableAllDeps();
+ $member
+ ->disableAllDeps()
+ ->disableEvents();
if ($is_manager) {
$member->enableDep('groups');
}
);
}
- $signature = $this->preferences->getMailSignature();
+ $signature = $this->preferences->getMailSignature($this->mail);
if ($signature != '') {
if ($this->html) {
//we are sending html message
use Galette\Entity\Social;
use Galette\Features\Replacements;
use Galette\Features\Socials;
+use PHPMailer\PHPMailer\PHPMailer;
use Throwable;
use Analog\Analog;
use Galette\Entity\Adherent;
case 'pref_footer':
$value = $this->cleanHtmlValue($value);
break;
+ case 'pref_website':
+ if (!isValidWebUrl($value)) {
+ $this->errors[] = _T("- Invalid website URL.");
+ }
+ break;
}
return $value;
/**
* Get email signature
*
+ * @param PHPMailer $mail PHPMailer instance
+ *
* @return string
*/
- public function getMailSignature(): string
+ public function getMailSignature(PHPMailer $mail): string
{
global $routeparser;
$this->getMainPatterns() + $this->getSignaturePatterns()
);
$this
+ ->setMail($mail)
->setMain()
->setSocialReplacements();
public function cleanHtmlValue(string $value): string
{
$config = \HTMLPurifier_Config::createDefault();
- $config->set('Cache.SerializerPath', GALETTE_CACHE_DIR);
+ $cache_dir = rtrim(GALETTE_CACHE_DIR, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'htmlpurifier';
+ if (!file_exists($cache_dir)) {
+ mkdir($cache_dir, 0755, true);
+ }
+ $config->set('Cache.SerializerPath', $cache_dir);
$purifier = new \HTMLPurifier($config);
return $purifier->purify($value);
}
use ArrayObject;
use Galette\Events\GaletteEvent;
+use Galette\Features\HasEvent;
use Galette\Features\Socials;
use Throwable;
use Analog\Analog;
{
use Dynamics;
use Socials;
+ use HasEvent;
public const TABLE = 'adherents';
public const PK = 'id_adh';
}
}
+ $this
+ ->withAddEvent()
+ ->withEditEvent()
+ ->withoutDeleteEvent()
+ ->activateEvents();
+
if ($args == null || is_int($args)) {
if (is_int($args) && $args > 0) {
$this->load($args);
);
}
- $event = 'member.add';
+ $event = $this->getAddEventName();
} else {
$hist->add(_T("Fail to add new member."));
throw new \Exception(
$this->sname
);
}
- $event = 'member.edit';
+ $event = $this->getEditEventName();
}
//dynamic fields
$this->storeSocials($this->id);
//send event at the end of process, once all has been stored
- if ($event !== null) {
+ if ($event !== null && $this->areEventsEnabled()) {
$emitter->dispatch(new GaletteEvent($event, $this));
}
return true;
$this->loadParent();
return $this;
}
+
+ /**
+ * Get prefix for events
+ *
+ * @return string
+ */
+ protected function getEventsPrefix(): string
+ {
+ return 'member';
+ }
}
use ArrayObject;
use DateTime;
use Galette\Events\GaletteEvent;
+use Galette\Features\HasEvent;
use Throwable;
use Analog\Analog;
use Laminas\Db\Sql\Expression;
class Contribution
{
use Dynamics;
+ use HasEvent;
public const TABLE = 'cotisations';
public const PK = 'id_cotis';
global $preferences;
$this->_payment_type = (int)$preferences->pref_default_paymenttype;
+ $this
+ ->withAddEvent()
+ ->withEditEvent()
+ ->withoutDeleteEvent()
+ ->activateEvents();
+
/*
* Fields configuration. Each field is an array and must reflect:
* array(
_T("Contribution added"),
Adherent::getSName($this->zdb, $this->_member)
);
- $event = 'contribution.add';
+ $event = $this->getAddEventName();
} else {
$hist->add(_T("Fail to add new contribution."));
throw new \Exception(
);
}
- $event = 'contribution.edit';
+ $event = $this->getEditEventName();
}
//update deadline
if ($this->isFee()) {
$this->_orig_amount = $this->_amount;
//send event at the end of process, once all has been stored
- if ($event !== null) {
+ if ($event !== null && $this->areEventsEnabled()) {
$emitter->dispatch(new GaletteEvent($event, $this));
}
return false;
}
+
+ /**
+ * Get prefix for events
+ *
+ * @return string
+ */
+ protected function getEventsPrefix(): string
+ {
+ return 'contribution';
+ }
}
--- /dev/null
+<?php
+
+/**
+ * Has events trait
+ *
+ * PHP version 5
+ *
+ * Copyright © 2024 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Features
+ * @package Galette
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2024 The Galette Team
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link https://galette.eu
+ */
+
+namespace Galette\Features;
+
+/**
+ * Has events trait
+ *
+ * @category Features
+ * @name HasEvents
+ * @package Galette
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2024 The Galette Team
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link https://galette.eu
+ */
+
+trait HasEvent
+{
+ private bool $has_add_event = false;
+ private bool $has_edit_event = false;
+ private bool $has_delete_event = false;
+ protected bool $events_active = true;
+
+ /**
+ * Get prefix for events
+ *
+ * @return string
+ */
+ abstract protected function getEventsPrefix(): string;
+
+ /**
+ * Activate events
+ *
+ * @return self
+ */
+ public function activateEvents(): self
+ {
+ $this->events_active = true;
+ return $this;
+ }
+
+ /**
+ * Disable events
+ *
+ * @return self
+ */
+ public function disableEvents(): self
+ {
+ $this->events_active = false;
+ return $this;
+ }
+
+ /**
+ * Are events enabled
+ *
+ * @return bool
+ */
+ public function areEventsEnabled(): bool
+ {
+ return $this->events_active;
+ }
+
+ /**
+ * Activate add event
+ *
+ * @return self
+ */
+ public function withAddEvent(): self
+ {
+ $this->has_add_event = true;
+ return $this;
+ }
+
+ /**
+ * Disable add event
+ *
+ * @return self
+ */
+ public function withoutAddEvent(): self
+ {
+ $this->has_add_event = false;
+ return $this;
+ }
+
+ /**
+ * Get add event name
+ *
+ * @return ?string
+ */
+ public function getAddEventName(): ?string
+ {
+ if (!$this->hasAddEvent()) {
+ return null;
+ }
+ return sprintf(
+ '%1$s.add',
+ $this->getEventsPrefix()
+ );
+ }
+
+ /**
+ * Has add event
+ *
+ * @return bool
+ */
+ public function hasAddEvent(): bool
+ {
+ return $this->areEventsEnabled() && $this->has_add_event;
+ }
+
+ /**
+ * Activate edit event
+ *
+ * @return self
+ */
+ public function withEditEvent(): self
+ {
+ $this->has_edit_event = true;
+ return $this;
+ }
+
+ /**
+ * Disable edit event
+ *
+ * @return self
+ */
+ public function withoutEditEvent(): self
+ {
+ $this->has_edit_event = false;
+ return $this;
+ }
+
+ /**
+ * Get edit event name
+ *
+ * @return ?string
+ */
+ public function getEditEventName(): ?string
+ {
+ if (!$this->hasEditEvent()) {
+ return null;
+ }
+ return sprintf(
+ '%1$s.edit',
+ $this->getEventsPrefix()
+ );
+ }
+
+ /**
+ * Has edit event
+ *
+ * @return bool
+ */
+ public function hasEditEvent(): bool
+ {
+ return $this->areEventsEnabled() && $this->has_edit_event;
+ }
+
+ /**
+ * Activate add event
+ *
+ * @return self
+ */
+ public function withDeleteEvent(): self
+ {
+ $this->has_delete_event = true;
+ return $this;
+ }
+
+ /**
+ * Disable delete event
+ *
+ * @return self
+ */
+ public function withoutDeleteEvent(): self
+ {
+ $this->has_delete_event = false;
+ return $this;
+ }
+
+ /**
+ * Get edit event name
+ *
+ * @return ?string
+ */
+ public function getDeleteEventName(): ?string
+ {
+ if (!$this->hasDeleteEvent()) {
+ return null;
+ }
+ return sprintf(
+ '%1$s.delete',
+ $this->getEventsPrefix()
+ );
+ }
+
+ /**
+ * Has delete event
+ *
+ * @return bool
+ */
+ public function hasDeleteEvent(): bool
+ {
+ return $this->areEventsEnabled() && $this->has_delete_event;
+ }
+}
use Galette\DynamicFields\DynamicField;
use Analog\Analog;
use NumberFormatter;
+use PHPMailer\PHPMailer\PHPMailer;
use Slim\Routing\RouteParser;
/**
private $patterns = [];
private $replaces = [];
private $dynamic_patterns = [];
+ private ?PHPMailer $mail = null;
/**
* @var Db
return $c_patterns + $dynamic_patterns;
}
+ /**
+ * Set mail instance
+ *
+ * @param PHPMailer $mail PHPMailer instance
+ *
+ * @return self
+ */
+ public function setMail(PHPMailer $mail): self
+ {
+ $this->mail = $mail;
+ return $this;
+ }
+
/**
* Set main replacements
*
}
$logo = new Logo();
-
+ if ($this->mail !== null) {
+ $logo_content = $this->preferences->getURL() . $this->routeparser->urlFor('logo');
+ } else {
+ $logo_content = '@' . base64_encode(file_get_contents($logo->getPath()));
+ }
$logo_elt = sprintf(
- '<img src="%1$s" width="%2$s" height="%3$s" />',
- '@' . base64_encode(file_get_contents($logo->getPath())),
+ '<img src="%1$s" width="%2$s" height="%3$s" alt="" />',
+ $logo_content,
$logo->getOptimalWidth(),
$logo->getOptimalHeight()
);
$replaced
);
- return $replaced;
+ return trim($replaced);
}
/**
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
-use Slim\Routing\RouteContext;
+use Slim\Routing\RouteParser;
/**
* Galette's Slim middleware for Update and Maintenance
*/
protected $i18n;
+ /**
+ * @var RouteParser
+ */
+ protected RouteParser $routeParser;
+
/**
* Constructor
*
- * @param I18n $i18n I18n instance
- * @param callable|int $callback Callable or local constant
+ * @param I18n $i18n I18n instance
+ * @param RouteParser $routeParser Route parser
+ * @param callable|int $callback Callable or local constant
*/
- public function __construct(I18n $i18n, $callback = self::MAINTENANCE)
+ public function __construct(I18n $i18n, RouteParser $routeParser, callable|int $callback = self::MAINTENANCE)
{
$this->i18n = $i18n;
+ $this->routeParser = $routeParser;
if ($callback === self::MAINTENANCE) {
$this->callback = array($this, 'maintenancePage');
/**
* Renders the page
*
- * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
- * @param string $contents HTML page contents
+ * @param Request $request PSR7 request
+ * @param string $contents HTML page contents
*
* @return string
*/
private function renderPage(Request $request, $contents)
{
- $routeContext = RouteContext::fromRequest($request);
- $routeParser = $routeContext->getRouteParser();
- $path = $routeParser->urlFor('slash');
+ $path = $this->routeParser->urlFor('slash');
//add ending / if missing
if (
<head>
<title>" . _T("Galette needs update!") . "</title>
<meta charset=\"UTF-8\"/>
- <link rel=\"stylesheet\" type=\"text/css\" href=\"" . $theme_path . "../../assets/css/galette-main.bundle.min.css\"/>
<link rel=\"stylesheet\" type=\"text/css\" href=\"" . $theme_path . "ui/semantic.min.css\"/>
<link rel=\"shortcut icon\" href=\"" . $theme_path . "images/favicon.png\"/>
</head>
/**
* Displays maintenance page
*
- * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
+ * @param Request $request PSR7 request
*
* @return string
*/
/**
* Displays needs update page
*
- * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
+ * @param Request $request PSR7 request
*
* @return string
*/
<div class="field{% if field.isRequired() %} required{% endif %}{% if get_class(field) == 'Galette\\DynamicFields\\File' %} wide{% endif %}">
{{ _self.draw_field(field, field_data, disabled, loop.index, object, masschange) }}
{% if field.getInformation() %}
- <p class="exemple">{{ field.getInformation()|raw }}</p>
+ <div class="exemple">{{ field.getInformation()|raw }}</div>
{% endif %}
</div>
{% endfor %}
+++ /dev/null
-{% extends 'ajax.html.twig' %}
-
-{% block content %}
- {% include 'elements/messages_inline.html.twig' %}
-{% endblock %}
;
{# Let's see if there are success messages to show #}
-{% set successes = flash.getMessage('success_detected') %}
+{% set success = flash.getMessage('success_detected') ?? [] %}
+{% set success = success|merge(flash.getMessage('success') ?? []) %}
{% if success_detected is defined and success_detected is iterable %}
- {% for s in success_detected %}
- {% set successes = successes|merge([s]) %}
+ {% for entry in success_detected %}
+ {% set success = success|merge([entry]) %}
{% endfor %}
{% endif %}
-{% if successes is iterable and successes|length > 0 %}
- {% for success in successes %}
+{% if success is iterable and success|length > 0 %}
+ {% for entry in success %}
$('body')
.toast({
displayTime: 'auto',
- position: 'bottom right',
- message: '{{ success|e('js') }}',
+ minDisplayTime: 5000,
+ wordsPerMinute: 80,
+ showProgress: 'bottom',
+ closeIcon: true,
+ position: 'top attached',
+ message: '{{ entry|e('js') }}',
showIcon: 'check circle outline',
class: 'success'
})
{% endfor %}
{% endif %}
+{# Let's see if there are info messages to show #}
+{% set info = flash.getMessage('info_detected') ?? [] %}
+{% set info = info|merge(flash.getMessage('info') ?? []) %}
+{% if info_detected is defined and info_detected is iterable %}
+ {% for entry in info_detected %}
+ {% set info = info|merge([entry]) %}
+ {% endfor %}
+{% endif %}
+{% if info is iterable and info|length > 0 %}
+ {% for entry in info %}
+ {% set info_title = '' %}
+ {% set info_message = entry %}
+ {% if entry is iterable %}
+ {% set info_title = entry.title %}
+ {% set info_message = entry.message %}
+ {% endif %}
+ $('body')
+ .toast({
+ displayTime: 'auto',
+ minDisplayTime: 5000,
+ wordsPerMinute: 80,
+ showProgress: 'bottom',
+ closeIcon: true,
+ position: 'top attached',
+ title: '{{ info_title|e('js') }}',
+ message: '{{ info_message|e('js') }}',
+ showIcon: 'info',
+ class: 'info'
+ })
+ ;
+ {% endfor %}
+{% endif %}
+
+{# Let's see if there are loginfault messages to show #}
+{% set loginfaults = flash.getMessage('loginfault') %}
+{% if loginfault_detected is defined and loginfault_detected is iterable %}
+ {% for l in loginfault_detected %}
+ {% set loginfaults = loginfaults|merge([l]) %}
+ {% endfor %}
+{% endif %}
+
+{# Let's see if there are error messages to show #}
+{% set errors = flash.getMessage('error_detected') ?? [] %}
+{% set errors = errors|merge(flash.getMessage('error') ?? []) %}
+{% set errors = errors|merge(loginfaults ?? []) %}
+{% if error_detected is defined and error_detected is iterable %}
+ {% for e in error_detected %}
+ {% set errors = errors|merge([e]) %}
+ {% endfor %}
+{% endif %}
+{% if errors is iterable and errors|length > 0 %}
+ {% for error in errors %}
+ $('body')
+ .toast({
+ displayTime: 0,
+ closeIcon: true,
+ position: 'top attached',
+ message: '{{ error|e('js') }}',
+ showIcon: 'exclamation circle',
+ class: 'error'
+ })
+ ;
+ {% endfor %}
+{% endif %}
+
+{# Let's see if there are warning messages to show #}
+{% set warnings = flash.getMessage('warning_detected') ?? [] %}
+{% set warnings = warnings|merge(flash.getMessage('warning') ?? []) %}
+{% if warning_detected is defined and warning_detected is iterable %}
+ {% for w in warning_detected %}
+ {% set warnings = warnings|merge([w]) %}
+ {% endfor %}
+{% endif %}
+{% if warnings is iterable and warnings|length > 0 %}
+ {% for warning in warnings %}
+ $('body')
+ .toast({
+ displayTime: 0,
+ closeIcon: true,
+ position: 'top attached',
+ message: '{{ warning|e('js') }}',
+ showIcon: 'exclamation triangle',
+ class: 'warning'
+ })
+ ;
+ {% endfor %}
+{% endif %}
+
{# Renew telemetry #}
{% if renew_telemetry is defined and renew_telemetry %}
$('body')
* - modal_approve_text: modal's approve button's text.
* - modal_approve_icon: modal's approve button's icon.
* - modal_cancel_text: modal's cancel button's text.
+ * - modal_action_onshow: additionnal code to execute on modal's onShow event.
*
* @see loader.js.twig
* @see modal.js.twig
} %}
},
error: function() {
+ {# Use "only" keyword to prevent known but not explicitiely defined variables to be passed #}
{% include "elements/js/modal.js.twig" with {
modal_title_twig: _T("An error occurred :(")|e("js"),
modal_without_content: true,
modal_deny_only: true,
modal_cancel_text: _T("Close")|e("js"),
modal_classname: "redalert",
- } %}
+ } only %}
}
});
});
inline: false,
addTouchEvents: false,
});
+{% if modal_action_onshow is defined %}
+ {{ modal_action_onshow|raw }}
+{% endif %}
}
$.ajax({
url: '{{ url_for("ajaxMessages") }}',
method: "GET",
- success: function (message) {
- $('.main-content .message').remove();
- $('.main-content').prepend(message);
+ success: function (values) {
+ for (var type in values) {
+ var dtime = 0;
+ if (type == 'success') {
+ dtime = 'auto';
+ }
+ $('body')
+ .toast({
+ displayTime: dtime,
+ minDisplayTime: 5000,
+ wordsPerMinute: 80,
+ showProgress: 'bottom',
+ closeIcon: true,
+ position: 'top attached',
+ title: values[type]['title'],
+ message: values[type]['messages'].join('<br/>'),
+ showIcon: values[type]['icon'],
+ class: type
+ })
+ ;
+ }
}
});
}
<div class="menu">
<div class="item">
<div class="ui basic center aligned fitted segment">
+ {% if preferences.pref_website is not empty %}
+ <a href="{{ preferences.pref_website }}" target="_blank">
+ <img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon" title="{{ _T("Open '%s' in a new window")|replace({"%s": preferences.pref_website}) }}"/>
+ </a>
+ {% else %}
<img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
+ {% endif %}
<div class="ui block huge brand header">
{{ preferences.pref_nom }}
{% if preferences.pref_slogan %}<div class="sub tiny header">{{ __(preferences.pref_slogan) }}</div>{% endif %}
{% endif %}
{# Let's see if there are error messages to show #}
-{% set errors = flash.getMessage('error_detected') ?? []|merge(flash.getMessage('error') ?? [])|merge(loginfaults ?? []) %}
+{% set errors = flash.getMessage('error_detected') ?? [] %}
+{% set errors = errors|merge(flash.getMessage('error') ?? []) %}
+{% set errors = errors|merge(loginfaults ?? []) %}
{% if error_detected is defined and error_detected is iterable %}
{% for e in error_detected %}
{% set errors = errors|merge([e]) %}
{% endfor %}
{% endif %}
{% if errors is iterable and errors|length > 0 %}
- <div class="ui error icon message with-transition">
- <i class="times icon" aria-hidden="true"></i>
- <i class="window close outline icon" aria-hidden="true"></i>
- <div class="content">
- <div class="header">{{ _T("- ERROR -") }}</div>
- {% if errors|length > 1 %}
- <ul class="list">
- {% for error in errors %}
- <li>{{ error|raw }}</li>
- {% endfor %}
- </ul>
- {% else %}
- {% for error in errors %}
- <p>{{ error|raw }}</p>
- {% endfor %}
- {% endif %}
+ <noscript>
+ <div class="ui error icon message">
+ <i class="exclamation circle icon" aria-hidden="true"></i>
+ <div class="content">
+ <div class="header">{{ _T("- ERROR -") }}</div>
+ {% if errors|length > 1 %}
+ <ul class="list">
+ {% for error in errors %}
+ <li>{{ error|raw }}</li>
+ {% endfor %}
+ </ul>
+ {% else %}
+ {% for error in errors %}
+ <p>{{ error|raw }}</p>
+ {% endfor %}
+ {% endif %}
+ </div>
</div>
- </div>
+ </noscript>
{% endif %}
{# Let's see if there are warning messages to show #}
-{% set warnings = flash.getMessage('warning_detected') ?? []|merge(flash.getMessage('warning') ?? []) %}
+{% set warnings = flash.getMessage('warning_detected') ?? [] %}
+{% set warnings = warnings|merge(flash.getMessage('warning') ?? []) %}
{% if warning_detected is defined and warning_detected is iterable %}
{% for w in warning_detected %}
{% set warnings = warnings|merge([w]) %}
{% endfor %}
{% endif %}
{% if warnings is iterable and warnings|length > 0 %}
- <div class="ui warning icon message with-transition">
- <i class="exclamation triangle icon" aria-hidden="true"></i>
- <i class="window close outline icon" aria-hidden="true"></i>
- <div class="content">
- <div class="header">{{ _T("- WARNING -") }}</div>
- {% if warnings|length > 1 %}
- <ul class="list">
- {% for warning in warnings %}
- <li>{{ warning|raw }}</li>
- {% endfor %}
- </ul>
- {% else %}
- {% for warning in warnings %}
- <p>{{ warning|raw }}</p>
- {% endfor %}
- {% endif %}
+ <noscript>
+ <div class="ui warning icon message">
+ <i class="exclamation triangle icon" aria-hidden="true"></i>
+ <div class="content">
+ <div class="header">{{ _T("- WARNING -") }}</div>
+ {% if warnings|length > 1 %}
+ <ul class="list">
+ {% for warning in warnings %}
+ <li>{{ warning|raw }}</li>
+ {% endfor %}
+ </ul>
+ {% else %}
+ {% for warning in warnings %}
+ <p>{{ warning|raw }}</p>
+ {% endfor %}
+ {% endif %}
+ </div>
</div>
- </div>
+ </noscript>
{% endif %}
{# Let's see if there are success messages to show #}
-{% set successs = flash.getMessage('success_detected') ?? []|merge(flash.getMessage('success') ?? []) %}
+{% set success = flash.getMessage('success_detected') ?? [] %}
+{% set success = success|merge(flash.getMessage('success') ?? []) %}
{% if success_detected is defined and success_detected is iterable %}
- {% for s in success_detected %}
- {% set successs = successs|merge([s]) %}
+ {% for entry in success_detected %}
+ {% set success = success|merge([entry]) %}
{% endfor %}
{% endif %}
-{% if successs is iterable and successs|length > 0 %}
+{% if success is iterable and success|length > 0 %}
<noscript>
<div class="ui success icon message">
<i class="check circle outline icon" aria-hidden="true"></i>
<i class="window close outline icon" aria-hidden="true"></i>
<div class="content">
- {% if successs|length > 1 %}
+ {% if success|length > 1 %}
<ul class="list">
- {% for success in successs %}
- <li>{{ success|raw }}</li>
+ {% for entry in success %}
+ <li>{{ entry|raw }}</li>
{% endfor %}
</ul>
{% else %}
- {% for success in successs %}
- <p>{{ success|raw }}</p>
+ {% for entry in success %}
+ <p>{{ entry|raw }}</p>
{% endfor %}
{% endif %}
</div>
</noscript>
{% endif %}
+{# Let's see if there are info messages to show #}
+{% set info = flash.getMessage('info_detected') ?? [] %}
+{% set info = info|merge(flash.getMessage('info') ?? []) %}
+{% if info_detected is defined and info_detected is iterable %}
+ {% for i in info_detected %}
+ {% set info = info|merge([i]) %}
+ {% endfor %}
+{% endif %}
+{% if info is iterable and info|length > 0 %}
+ <noscript>
+ <div class="ui info icon message">
+ <i class="info icon" aria-hidden="true"></i>
+ <div class="content">
+ {% if info|length > 1 %}
+ <ul class="list">
+ {% for i in info %}
+ <li>{{ i|raw }}</li>
+ {% endfor %}
+ </ul>
+ {% else %}
+ {% for entry in info %}
+ {% set info_title = '' %}
+ {% set info_message = entry %}
+ {% if entry is iterable %}
+ {% set info_title = entry.title %}
+ <p class="header">{{ info_title }}</p>
+ {% set info_message = entry.message %}
+ {% endif %}
+ <p>{{ entry|raw }}</p>
+ {% endfor %}
+ {% endif %}
+ </div>
+ </div>
+ </noscript>
+{% endif %}
+
{# Renew telemetry #}
{% if renew_telemetry is defined and renew_telemetry %}
{% include "modals/telemetry.html.twig" with {part: "dialog"} %}
{% if not login.getCompactMenu() %}
<div class="ui basic center aligned fitted segment">
+ {% if preferences.pref_website is not empty %}
+ <a href="{{ preferences.pref_website }}" target="_blank">
+ <img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon" title="{{ _T("Open '%s' in a new window")|replace({"%s": preferences.pref_website}) }}"/>
+ </a>
+ {% else %}
<img src="{{ url_for('logo') }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
+ {% endif %}
<div class="ui block huge brand header">
{{ preferences.pref_nom }}
{% if preferences.pref_slogan %}<div class="sub tiny header">{{ __(preferences.pref_slogan) }}</div>{% endif %}
<script type="text/javascript" src="{{ base_path() }}/assets/js/formatting.js"></script>
<script type="text/javascript" src="{{ base_path() }}/assets/js/summernote.min.js"></script>
<script type="text/javascript" src="{{ base_path() }}/assets/js/lang/summernote-{{ i18n.getID()|replace({'_': '-'}) }}.min.js"></script>
- <script language="javascript">
- function activateMailingEditor() {
- if(!$('#mailing_html').attr('checked')){
- $('#mailing_html').attr('checked', true);
- }
-
- $('input#html_editor_active').attr('value', '1');
- $('#activate_editor').remove();
- $('#summernote_toggler').html('<a class="ui blue tertiary button" href="javascript:deactivateMailingEditor();" id="deactivate_editor">{{ _T("Deactivate HTML editor") }}</a>');
-
- $('#mailing_corps').summernote({
- lang: '{{ i18n.getID()|replace({'_': '-'}) }}',
- disableDragAndDrop: true,
- height: 240,
- toolbar: [
+ <script type="text/javascript">
+ function activateHtmlEditor(elt, basic) {
+ if (basic === true) {
+ var _toolbar = [
+ ['font', ['bold', 'italic', 'strikethrough', 'clear']],
+ ['para', ['ul', 'ol']],
+ ['insert', ['link']],
+ ['view', ['codeview']]
+ ];
+ } else {
+ var _toolbar = [
['style', ['style']],
['font', ['bold', 'italic', 'strikethrough', 'clear']],
['para', ['ul', 'ol', 'paragraph']],
['insert', ['link', 'picture']],
- ['view', ['codeview', 'help']]
- ],
+ ['view', ['codeview']]
+ ];
+ }
+ elt.summernote({
+ lang: '{{ i18n.getID()|replace({'_': '-'}) }}',
+ disableDragAndDrop: true,
+ height: 240,
+ toolbar: _toolbar,
styleTags: [
'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
],
}
}
});
- $('#mailing_corps').summernote('focus');
+ elt.summernote('focus');
+ }
+
+ function deactivateHtmlEditor(elt) {
+ elt.summernote('destroy');
+ }
+
+ function activateMailingEditor() {
+ if(!$('#mailing_html').attr('checked')){
+ $('#mailing_html').attr('checked', true);
+ }
+
+ $('input#html_editor_active').attr('value', '1');
+ $('#activate_editor').remove();
+ $('#summernote_toggler').html('<a class="ui blue tertiary button" href="javascript:deactivateMailingEditor();" id="deactivate_editor">{{ _T("Deactivate HTML editor") }}</a>');
+
+ activateHtmlEditor($('#mailing_corps'));
}
function deactivateMailingEditor() {
- $('#mailing_corps').summernote('destroy');
+ deactivateHtmlEditor($('#mailing_corps'));
$('#deactivate_editor').remove();
$('#summernote_toggler').html('<a class="ui blue tertiary button" href="javascript:activateMailingEditor();" id="activate_editor">{{ _T("Activate HTML editor") }}</a>');
}
<section class="content{% if contentcls is defined %} {{ contentcls }}{% endif %}{% if login.getCompactMenu() %} extended{% endif %}">
{% if not login.isLogged() %}
<div class="ui basic center aligned fitted segment">
+ {% if preferences.pref_website is not empty %}
+ <a href="{{ preferences.pref_website }}" target="_blank">
+ <img src="{{ url_for("logo") }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon" title="{{ _T("Open '%s' in a new window")|replace({"%s": preferences.pref_website}) }}"/>
+ </a>
+ {% else %}
<img src="{{ url_for("logo") }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="icon"/>
+ {% endif %}
<div class="ui large header">
{{ preferences.pref_nom }}
<div class="sub header">{% if preferences.pref_slogan %}{{ __(preferences.pref_slogan) }}{% endif %}</div>
{% block javascripts %}
<script>
- $('#field_information').summernote({
- lang: '{{ i18n.getID()|replace({'_': '-'}) }}',
- height: 240,
- toolbar: [
- ['style', ['style']],
- ['font', ['bold', 'italic', 'strikethrough', 'clear']],
- ['para', ['ul', 'ol', 'paragraph']],
- ['insert', ['link', 'picture']],
- ['view', ['codeview', 'help']]
- ],
- styleTags: [
- 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
- ]
+ $(function() {
+ activateHtmlEditor($('#field_information'), true);
});
- $('#field_information').summernote('focus');
-
</script>
{% endblock %}
modal_title_twig: _T("Edit field")|e("js"),
modal_class: "tiny",
modal_content_class: "scrolling",
- modal_onapprove: modal_onapprove
+ modal_onapprove: modal_onapprove,
+ modal_action_onshow: "activateHtmlEditor($('#field_information'), true);"
} %}
}
_editDynField();
$.ajax({
url: '{{ url_for('ajaxMessages') }}',
method: "GET",
- success: function (message) {
- $('#asso_name').after(message);
+ success: function (values) {
+ for (var type in values) {
+ var dtime = 0;
+ if (type == 'success') {
+ dtime = 'auto';
+ }
+ $('body')
+ .toast({
+ displayTime: dtime,
+ minDisplayTime: 5000,
+ wordsPerMinute: 80,
+ showProgress: 'bottom',
+ closeIcon: true,
+ position: 'top attached',
+ title: values[type]['title'],
+ message: values[type]['messages'].join('<br/>'),
+ showIcon: values[type]['icon'],
+ class: type
+ })
+ ;
+ }
}
});
}
$.ajax({
url: '{{ url_for('ajaxMessages') }}',
method: "GET",
- success: function(message) {
- var message_inline = new DOMParser().parseFromString(message, 'text/html');
- var message_content = message_inline.body.querySelectorAll('div.content');
- $('body').toast({
- position: 'bottom right',
- message: message_content,
- showIcon: 'check circle outline',
- class: 'success'
- });
+ success: function (values) {
+ for (var type in values) {
+ var dtime = 0;
+ if (type == 'success') {
+ dtime = 'auto';
+ }
+ $('body')
+ .toast({
+ displayTime: dtime,
+ minDisplayTime: 5000,
+ wordsPerMinute: 80,
+ showProgress: 'bottom',
+ closeIcon: true,
+ position: 'top attached',
+ title: values[type]['title'],
+ message: values[type]['messages'].join('<br/>'),
+ showIcon: values[type]['icon'],
+ class: type
+ })
+ ;
+ }
}
});
},
<section class="{% if login.isLogged() %}content{% else %}vertically centered{% endif %}">
{% if not login.isLogged() %}
<div class="ui basic center aligned fitted segment">
+ {% if preferences.pref_website is not empty %}
+ <a href="{{ preferences.pref_website }}" target="_blank">
+ <img src="{{ url_for("logo") }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="logo" title="{{ _T("Open '%s' in a new window")|replace({"%s": preferences.pref_website}) }}"/>
+ </a>
+ {% else %}
<img src="{{ url_for("logo") }}" width="{{ logo.getOptimalWidth() }}" height="{{ logo.getOptimalHeight() }}" alt="{{ preferences.pref_nom }}" class="logo"/>
+ {% endif %}
<div class="ui large header">
{{ preferences.pref_nom }}
<div class="sub header">{% if preferences.pref_slogan %}{{ __(preferences.pref_slogan) }}{% endif %}</div>
"permission": "644",
"autoInstall": true,
"rtl": "both",
- "components": ["reset", "site", "button", "container", "divider", "header", "icon", "input", "label", "list", "loader", "segment", "step", "form", "grid", "menu", "message", "table", "card", "item", "accordion", "checkbox", "dimmer", "dropdown", "popup", "sidebar", "tab", "transition", "text", "calendar", "toast", "modal", "api", "search", "emoji"],
+ "components": ["reset", "site", "button", "container", "divider", "header", "icon", "input", "label", "list", "loader", "segment", "step", "form", "grid", "menu", "message", "table", "card", "item", "accordion", "checkbox", "dimmer", "dropdown", "popup", "sidebar", "tab", "transition", "text", "calendar", "progress", "toast", "modal", "api", "search", "emoji"],
"version": "2.9.3"
}
\ No newline at end of file
namespace Galette\Core\test\units;
+use PHPMailer\PHPMailer\PHPMailer;
use PHPUnit\Framework\TestCase;
/**
*/
public function testGetMailSignature()
{
- $this->assertSame("\r\n-- \r\nGalette\r\n\r\n", $this->preferences->getMailSignature());
+ $mail = new PHPMailer();
+ $this->assertSame("\r\n-- \r\nGalette", $this->preferences->getMailSignature($mail));
$this->preferences->pref_website = 'https://galette.eu';
- $this->assertSame("\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu", $this->preferences->getMailSignature());
+ $this->assertSame(
+ "\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu",
+ $this->preferences->getMailSignature($mail)
+ );
//with legacy values
- $this->preferences->pref_mailsign = "NAME}\r\n\r\n{WEBSITE}\r\n{GOOGLEPLUS}\r\n{FACEBOOK}\r\n{TWITTER}\r\n{LINKEDIN}\r\n{VIADEO}";
- $this->assertSame("\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu", $this->preferences->getMailSignature());
+ $this->preferences->pref_mail_sign = "{NAME}\r\n\r\n{WEBSITE}\r\n{FACEBOOK}\r\n{TWITTER}\r\n{LINKEDIN}\r\n{VIADEO}";
+ $this->assertSame(
+ "\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu",
+ $this->preferences->getMailSignature($mail)
+ );
$social = new \Galette\Entity\Social($this->zdb);
$this->assertTrue(
);
$this->preferences->pref_mail_sign = "{ASSO_NAME}\r\n\r\n{ASSO_WEBSITE} - {ASSO_SOCIAL_MASTODON}";
- $this->assertSame("\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu - https://framapiaf.org/@galette", $this->preferences->getMailSignature());
+ $this->assertSame(
+ "\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu - https://framapiaf.org/@galette",
+ $this->preferences->getMailSignature($mail)
+ );
$social = new \Galette\Entity\Social($this->zdb);
$this->assertTrue(
2,
\Galette\Entity\Social::getListForMember(null, \Galette\Entity\Social::MASTODON)
);
- $this->assertSame("\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu - https://framapiaf.org/@galette, Galette mastodon URL - the return", $this->preferences->getMailSignature());
+ $this->assertSame(
+ "\r\n-- \r\nGalette\r\n\r\nhttps://galette.eu - https://framapiaf.org/@galette, Galette mastodon URL - the return",
+ $this->preferences->getMailSignature($mail)
+ );
}
/**
$legend['socials']['patterns']['asso_social_mynewtype']
);
}
+
+ /**
+ * Test website URL
+ *
+ * @return void
+ */
+ public function testWebsiteURL(): void
+ {
+ $preferences = [];
+ foreach ($this->preferences->getDefaults() as $key => $value) {
+ $preferences[$key] = $value;
+ }
+
+ $post = array_merge($preferences, ['pref_website' => 'https://galette.eu']);
+ $this->assertTrue(
+ $this->preferences->check($post, $this->login),
+ print_r($this->preferences->getErrors(), true)
+ );
+
+ $post = array_merge($preferences, ['pref_website' => 'galette.eu']);
+ $this->assertFalse(
+ $this->preferences->check($post, $this->login),
+ print_r($this->preferences->getErrors(), true)
+ );
+ $this->assertSame(['- Invalid website URL.'], $this->preferences->getErrors());
+ }
}
);
$this->assertMatchesRegularExpression(
- '/<td id="pdf_logo"><img src="@.+" width="129" height="60" \/><\/td>/',
+ '/<td id="pdf_logo"><img src="@.+" width="129" height="60" alt="" \/><\/td>/',
$model->hheader
);
--- /dev/null
+<?php
+
+/**
+ * HasEvents tests
+ *
+ * PHP version 5
+ *
+ * Copyright © 2024 The Galette Team
+ *
+ * This file is part of Galette (http://galette.tuxfamily.org).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category Features
+ * @package GaletteTests
+ *
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2024 The Galette Team
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link https://galette.eu
+ */
+
+namespace Galette\Entity\test\units;
+
+use Galette\GaletteTestCase;
+
+/**
+ * HasEvent tests class
+ *
+ * @category Features
+ * @name HasEvents
+ * @package GaletteTests
+ * @author Johan Cwiklinski <johan@x-tnd.be>
+ * @copyright 2024 The Galette Team
+ * @license https://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
+ * @link https://galette.eu
+ */
+class HasEvent extends GaletteTestCase
+{
+ protected int $seed = 20240223092214;
+
+ /**
+ * Test HasEvent capacities
+ *
+ * @return void
+ */
+ public function testCapacities(): void
+ {
+ $this->adh = new \Galette\Entity\Adherent($this->zdb);
+
+ //per default, add and edit events are active on contributions
+ $contrib = new \Galette\Entity\Contribution($this->zdb, $this->login);
+ $this->assertTrue($contrib->areEventsEnabled());
+ $this->assertTrue($contrib->hasAddEvent());
+ $this->assertTrue($contrib->hasEditEvent());
+ $this->assertFalse($contrib->hasDeleteEvent());
+ $this->assertEquals('contribution.add', $contrib->getAddEventName());
+ $this->assertEquals('contribution.edit', $contrib->getEditEventName());
+ $this->assertNull($contrib->getDeleteEventName());
+
+ //per default, add and edit events are active on members
+ $this->assertTrue($this->adh->areEventsEnabled());
+ $this->assertTrue($this->adh->hasAddEvent());
+ $this->assertTrue($this->adh->hasEditEvent());
+ $this->assertFalse($this->adh->hasDeleteEvent());
+ $this->assertEquals('member.add', $this->adh->getAddEventName());
+ $this->assertEquals('member.edit', $this->adh->getEditEventName());
+ $this->assertNull($this->adh->getDeleteEventName());
+
+ //disable add event
+ $this->adh->withoutAddEvent();
+ $this->assertFalse($this->adh->hasAddEvent());
+ $this->assertNull($this->adh->getAddEventName());
+ $this->assertTrue($this->adh->hasEditEvent());
+ //enable add event
+ $this->adh->withAddEvent();
+ $this->assertTrue($this->adh->hasAddEvent());
+
+ //disable edit event
+ $this->adh->withoutEditEvent();
+ $this->assertTrue($this->adh->hasAddEvent());
+ $this->assertFalse($this->adh->hasEditEvent());
+ $this->assertNull($this->adh->getEditEventName());
+ //enable edit event
+ $this->adh->withEditEvent();
+ $this->assertTrue($this->adh->hasEditEvent());
+
+ //enable delete event
+ $this->adh->withDeleteEvent();
+ $this->assertTrue($this->adh->hasDeleteEvent());
+ $this->assertEquals('member.delete', $this->adh->getDeleteEventName());
+ //disable delete event
+ $this->adh->withoutDeleteEvent();
+ $this->assertFalse($this->adh->hasDeleteEvent());
+
+ // disable all events
+ $this->adh->disableEvents();
+ $this->assertFalse($this->adh->areEventsEnabled());
+ $this->assertFalse($this->adh->hasAddEvent());
+ $this->assertFalse($this->adh->hasEditEvent());
+ $this->assertFalse($this->adh->hasDeleteEvent());
+ $this->assertNull($this->adh->getAddEventName());
+ $this->assertNull($this->adh->getEditEventName());
+ $this->assertNull($this->adh->getDeleteEventName());
+
+ //reactivate events
+ $this->adh->activateEvents();
+ $this->assertTrue($this->adh->areEventsEnabled());
+ $this->assertTrue($this->adh->hasAddEvent());
+ $this->assertTrue($this->adh->hasEditEvent());
+ $this->assertFalse($this->adh->hasDeleteEvent());
+ }
+}
define('GALETTE_TPL_SUBDIR', 'templates/default/');
define('GALETTE_THEME', 'themes/default/');
define('GALETTE_DATA_PATH', GALETTE_TESTS_PATH . '/tests-data/');
+define('GALETTE_CACHE_DIR', GALETTE_DATA_PATH . 'cache/');
if (is_dir(GALETTE_DATA_PATH)) {
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
@pluginsActiveBackground : #d9f7d8;
@pluginsInactiveBackground : #ffead8;
+
+@infoColor: @blue;
+@warningColor: #e35a00;