Valider 5b9ae7da rédigé par cerdic's avatar cerdic
Parcourir les fichiers

feat: une classe Spip\Afficher\Minipublic utilisable facilement pour renvoyer...

feat: une classe Spip\Afficher\Minipublic utilisable facilement pour renvoyer des messages simples à l'utilisateur sans passer par le squelette. C'est une version modernisée et étendue de ce que l'on avait avec minipres()
```
$minipublic = new Spip\Afficher\Minipublic();
echo $minipublic->page("SPIP vous passe bien le bonjour !");
```
parent dc31ed8e
Chargement en cours
Chargement en cours
Chargement en cours
Chargement en cours
+281 −0
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
<?php

/***************************************************************************\
 *  SPIP, Système de publication pour l'internet                           *
 *                                                                         *
 *  Copyright © avec tendresse depuis 2001                                 *
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
 *                                                                         *
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
 * \***************************************************************************/

namespace Spip\Afficher;


/**
 * Présentation des pages simplifiées publiques pour envoyer un message à un utilisateur
 *
 * @package SPIP\Afficher\Minipublic
 **/
class Minipublic {

	public function __construct() {
		include_fichiers_fonctions();
		include_spip('inc/headers');
		include_spip('inc/texte'); //inclue inc/lang et inc/filtres
		include_spip('inc/filtres_images_mini');
	}

	/**
	 * Retourne le début d'une page HTML minimale
	 *
	 * Le contenu de CSS minimales (reset.css, clear.css, minipublic.css) est inséré
	 * dans une balise script inline (compactée si possible)
	 *
	 * @param array $options
	 *   string $lang : forcer la langue utilisateur
	 *   string $page_title : titre éventuel de la page (nom du site par défaut)
	 *   string $couleur_fond : pour la couleur dominante de la page (par défaut on reprend le réglage de la page de login)
	 *   bool $all_inline : inliner les CSS pour envoyer toute la page en 1 hit
	 *   string $doctype
	 *   string $charset
	 *   string $onload
	 *   array $css_files : ajouter des fichiers css
	 *   string $css : ajouter du CSS inline
	 *   string $head : contenu à ajouter à la fin <head> (pour inclusion de JS ou JS inline...)
	 * @return string
	 *    Code HTML
	 *
	 * @uses html_lang_attributes()
	 * @uses minifier() si le plugin compresseur est présent
	 * @uses url_absolue_css()
	 *
	 * @uses utiliser_langue_visiteur()
	 * @uses http_no_cache()
	 */
	protected function ouvreBody($options = []) {
		if (empty($options['lang'])) {
			// on se limite sur une langue de $GLOBALS['meta']['langues_multilingue'] car on est dans le public
			utiliser_langue_visiteur($GLOBALS['meta']['langues_multilingue']);
		} else {
			changer_langue($options['lang']);
		}
		http_no_cache();

		$page_title = (isset($options['page_title']) ? $options['page_title'] : $GLOBALS['meta']['nom_site']);
		$doctype = (isset($options['doctype']) ? $options['doctype'] : "<!DOCTYPE html>");
		$doctype = trim($doctype) . "\n";
		$charset = (isset($options['charset']) ? $options['charset'] : "utf-8");
		$all_inline = (isset($options['all_inline']) ? $options['all_inline'] : true);
		$onLoad = (isset($options['onLoad']) ? $options['onLoad'] : '');
		if ($onLoad) {
			$onLoad = ' onload="' . attribut_html($onLoad) . '"';
		}

		# envoyer le charset
		if (!headers_sent()) {
			header('Content-Type: text/html; charset=' . $charset);
		}

		$css = '';

		if (function_exists('couleur_hex_to_hsl')) {
			if (!empty($options['couleur_fond'])) {
				$couleur_fond = $options['couleur_fond'];
			} else {
				$couleur_fond = lire_config("couleur_login", "#db1762");
			}
			$h = couleur_hex_to_hsl($couleur_fond, "h");
			$s = couleur_hex_to_hsl($couleur_fond, "s");
			$l = couleur_hex_to_hsl($couleur_fond, "l");
		}

		$inline = ":root {"
			. "--minipublic-color-theme--h: $h;"
			. "--minipublic-color-theme--s: $s;"
			. "--minipublic-color-theme--l: $l;}";
		$vars = file_get_contents(find_in_theme('minipublic.vars.css'));
		$inline .= "\n" . trim($vars);
		if (function_exists('minifier')) {
			$inline = minifier($inline, 'css');
		}
		$files = [
			find_in_theme('reset.css'),
			find_in_theme('clear.css'),
			find_in_theme('minipublic.css'),
		];
		if (!empty($options['css_files'])) {
			foreach ($options['css_files'] as $css_file) {
				$files[] = $css_file;
			}
		}
		if ($all_inline) {
			// inliner les CSS (optimisation de la page minipublic qui passe en un seul hit a la demande)
			foreach ($files as $name) {
				$file = direction_css($name);
				if (function_exists('minifier')) {
					$file = minifier($file);
				} else {
					$file = url_absolue_css($file); // precaution
				}
				$css .= file_get_contents($file);
			}
			$css = "$inline\n$css";
			if (!empty($options['css'])) {
				$css .= "\n" . $options['css'];
			}
			$css = "<style type='text/css'>$css</style>";
		} else {
			$css = "<style type='text/css'>$inline</style>";
			foreach ($files as $name) {
				$file = timestamp(direction_css($name));
				$css .= "<link rel='stylesheet' href='" . attribut_html($file) . "' type='text/css' />\n";
			}
			if (!empty($options['css'])) {
				$css .= "<style type='text/css'>" . $options['css'] . "</style>";
			}
		}

		return $doctype .
			html_lang_attributes() .
			"<head>\n" .
			'<title>' .
			textebrut($page_title) .
			"</title>\n" .
			"<meta name=\"viewport\" content=\"width=device-width\" />\n" .
			$css .
			(empty($options['head']) ? "" : $options['head']) .
			"</head>\n" .
			"<body{$onLoad} class=\"minipublic\">\n" .
			"\t<div class=\"minipublic-bloc\">\n";
	}

	/**
	 * Ouvre le corps : affiche le header avec un éventuel titre + ouvre le div corps
	 * @param $options
	 * @return string
	 */
	protected function ouvreCorps($options = []) {
		$url_site = url_de_base();
		$header = "<header>\n" .
			"<h1><a href=\"" . attribut_html($url_site) . "\">" . interdire_scripts($GLOBALS['meta']['nom_site']) . "</a></h1>\n";

		$titre = (isset($options['titre']) ? $options['titre'] : '');
		if ($titre) {
			$header .= "<h2>" . interdire_scripts($titre) . "</h2>";
		}
		$header .= "</header>";

		return $header . "<div class='corps'>\n";
	}

	/**
	 * Ferme le corps : affiche le footer par défaut ou custom et ferme le div corps
	 * @param $options
	 * @return string
	 */
	protected function fermeCorps($options = []) {
		$url_site = url_de_base();

		if (isset($options['footer'])) {
			$footer = $options['footer'];
		} else {
			$footer = "<a href=\"" . attribut_html($url_site) . "\">" . _T('retour') . "</a>\n";
		}
		if (!empty($footer)) {
			$footer = "<footer>\n{$footer}</footer>";
		}

		return "</div>\n" . $footer;
	}


	/**
	 * Retourne la fin d'une page HTML minimale
	 *
	 * @return string Code HTML
	 */
	protected function fermeBody() {
		return "\n\t</div>\n</body>\n</html>";
	}


	/**
	 * Retourne une page HTML contenant, dans une présentation minimale,
	 * le contenu transmis dans `$corps`.
	 *
	 * Appelée pour afficher un message ou une demande de confirmation simple et rapide
	 *
	 * @param string $corps
	 *   Corps de la page
	 * @param array $options
	 * @return string
	 *   HTML de la page
	 * @see  ouvreBody()
	 * @see  ouvreCorps()
	 *   string $titre : Titre à l'affichage (différent de $page_title)
	 *   int $status : status de la page
	 *   string $footer : pied de la box en remplacement du bouton retour par défaut
	 * @uses ouvreBody()
	 * @uses ouvreCorps()
	 * @uses fermeCorps()
	 * @uses fermeBody()
	 *
	 */
	public function page($corps, $options = []) {

		// par securite
		if (!defined('_AJAX')) {
			define('_AJAX', false);
		}

		$status = (isset($options['status']) ? intval($options['status']) : 200);
		$status = ($status ?: 200);

		http_response_code($status);

		$html = $this->ouvreBody($options)
			. $this->ouvreCorps($options)
			. $corps
			. $this->fermeCorps($options)
			. $this->fermeBody();

		if (
			$GLOBALS['profondeur_url'] >= (_DIR_RESTREINT ? 1 : 2)
			and empty($options['all_inline'])
		) {
			define('_SET_HTML_BASE', true);
			include_spip('public/assembler');
			$GLOBALS['html'] = true;
			page_base_href($html);
		}
		return $html;
	}

	/**
	 * Fonction helper pour les erreurs
	 * @param ?string $message_erreur
	 * @param array $options
	 * @see page()
	 * @return string
	 *
	 */
	public function pageErreur($message_erreur = null, $options = []) {

		if (empty($message_erreur)) {
			if (empty($options['lang'])) {
				utiliser_langue_visiteur();
			} else {
				changer_langue($options['lang']);
			}
			$message_erreur = _T('info_acces_interdit');
		}
		$corps = "<div class='msg-alert error'>"
			. $message_erreur
			. "</div>";
		if (empty($options['status'])) {
			$options['status'] = 403;
		}
		return $this->page($corps, $options);
	}
}
+202 −0
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
/* Voir minipublic.vars.css pour la définition des variables */

h1 {font-size: 1.5em; line-height: 1.1; font-weight: bold}
h2 {font-size: 1.25em; line-height: 1}
b, strong { font-weight: bold}
i, em {font-style: italic}
a { text-decoration: underline; color:var(--minipublic-color-theme-dark);}
a:hover, a:focus, a:active {text-decoration: none; background: var(--minipublic-color-theme-dark); color:#fff;}

.btn,
input.submit,
input.reset,
button {
	position: relative;
	box-sizing: border-box;
	display: inline-flex;
	justify-content: center;
	align-items: center;
	text-align: center;
	vertical-align: middle;
	padding: var(--minipublic-btn-padding-y) var(--minipublic-btn-padding-x);
	margin-bottom: var(--minipublic-btn-margin);
	background-color: var(--minipublic-btn-color-main-bg);
	color: var(--minipublic-btn-color-main-text);
	border: 1px solid var(--minipublic-btn-color-main-border);
	border-bottom-color: var(--minipublic-btn-color-border-accent);
	border-radius: var(--minipublic-btn-border-radius);
	text-decoration: none;
	font-size: var(--minipublic-btn-font-size);
	font-family: inherit;
	font-weight: 400;
	line-height: var(--minipublic-line-height);
	user-select: none;
	transition: all 0.1s;
}
a.btn,
#wysiwyg a.btn, #wysiwyg a.btn:hover,
input.submit,
input.reset,
button {
	text-decoration: none;
	cursor: pointer;
}

/* Survol */
.btn:hover,
.btn:focus,
input.submit:hover,
input.submit:focus,
input.reset:hover,
input.reset:focus,
button:hover {
	background-color: var(--minipublic-btn-color-main-hover-bg);
	border-color: var(--minipublic-btn-color-main-hover-border);
	border-bottom-color: var(--minipublic-btn-color-border-accent);
	color: var(--minipublic-btn-color-main-hover-text);
	text-decoration: none;
	transition: all 0.2s;
}
/* Focus : outline */
.btn:focus,
input.submit:focus,
input.reset:focus,
button:focus {
	box-shadow: 0 0 0 0.2rem var(--minipublic-btn-color-focus);
}
/* Actif */
.btn:active,
input.submit:active,
input.reset:active,
button:active {
	background-color: var(--minipublic-btn-color-main-active-bg);
	border-color: var(--minipublic-btn-color-main-active-border);
	border-bottom-color: var(--minipublic-btn-color-border-accent);
	color: var(--minipublic-btn-color-main-active-text);
}


/* Alertes */
.msg-alert,
.msg-alert * {
box-sizing: border-box;
}
.msg-alert,
.notice, .error, .success,
.formulaire_spip .reponse_formulaire {
	position: relative;
	padding: var(--minipublic-alert-spacing-y) var(--minipublic-alert-spacing-x);
	/* gouttière + taille icone + espacement arbitraire avec le texte */
	padding-left: calc(var(--minipublic-alert-spacing-x) + var(--minipublic-alert-iconsize) + 0.75rem);
	margin: calc(var(--minipublic-margin-bottom) * 1.5) 0; /* Idem boîtes */
	background-repeat: no-repeat;
	/* Aligner icône au niveau de la 1ère ligne de texte */
	background-position: left var(--minipublic-alert-spacing-x) top calc(var(--minipublic-alert-spacing-y) - ((var(--minipublic-alert-iconsize) - var(--minipublic-line-height)) / 2));
	background-size: var(--minipublic-alert-iconsize);
	font-weight: normal;
	border-radius: var(--minipublic-border-radius);
	box-shadow: inset 0 0 1.5em hsla(0, 0%, 0%, 0.02);
	background-color: var(--minipublic-color-gray-lighter);
	border-left: 0.5rem solid var(--minipublic-color-gray-light);
}
.msg-alert a {
	color: var(--minipublic-color-black);
	text-decoration: underline;
}

/* Code */
.msg-alert tt,
.msg-alert code {
	color: var(--minipublic-color-black);
}

/* Notice */
.msg-alert.notice,
.notice {
	color:            hsl(var(--minipublic-color-notice--h), var(--minipublic-color-notice--s), 18%);
	background-color: hsl(var(--minipublic-color-notice--h), 90%, 88%);
	border-color:     hsl(var(--minipublic-color-notice--h), 100%, 48%);
}

/* Erreur */
.msg-alert.error,
.formulaire_spip .reponse_formulaire_erreur,
.error {
	color:            hsl(var(--minipublic-color-error--h), var(--minipublic-color-error--s), 18%);
	background-color: hsl(var(--minipublic-color-error--h), 60%, 95%);
	border-color:     hsl(var(--minipublic-color-error--h), var(--minipublic-color-error--s), 50%);
}

/* Succès */
.msg-alert.success,
.formulaire_spip .reponse_formulaire_ok,
.success {
	color:            hsl(var(--minipublic-color-success--h), var(--minipublic-color-success--s), 15%);
	background-color: hsl(var(--minipublic-color-success--h), 55%, 90%);
	border-color:     hsl(var(--minipublic-color-success--h), var(--minipublic-color-success--s), 45%);
}

/* Information */
.msg-alert.info,
.information {
	color:            hsl(var(--minipublic-color-info--h), var(--minipublic-color-info--s), 25%);
	background-color: hsl(var(--minipublic-color-info--h), 45%, 93%);
	border-color:     hsl(var(--minipublic-color-info--h), var(--minipublic-color-info--s), 60%);
}

.minipublic  {
	margin:0;
	font-size: 1em;
	color: #333;
	border:0;
	padding: 0;
	font-family: arial, helvetica, sans-serif;
	line-height: var(--minipublic-line-height);
	background-size: cover;
	background-position: center;
	background: var(--minipublic-color-theme);
	background: radial-gradient(circle, var(--minipublic-color-theme) 20%, var(--minipublic-color-theme-dark) 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 100vh;
}

.minipublic-bloc {
	border: 1px solid var(--minipublic-color-gray-light);
	border-radius: var(--minipublic-border-radius);
	margin: 5rem auto;
	width: 30em;
	max-width: 100vw;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	text-align: center;
	min-height: 20em;
}

.minipublic-bloc header {
	background: rgba(255,255,255,85%);
	padding: 1.5em;
	text-align: center;
	border-top-left-radius: var(--minipublic-border-radius);
	border-top-right-radius: var(--minipublic-border-radius);
}
.minipublic-bloc header h1+h2 {
	margin-top: 0.75rem;
}

.minipublic-bloc .corps {
	background: rgba(255,255,255,95%);
	padding: 1.5em;
	overflow: auto;
	flex-grow: 1;
}

.minipublic-bloc footer {
	background: rgba(255,255,255,85%);
	padding: 1.5em;
	text-align: center;
	border-bottom-left-radius: var(--minipublic-border-radius);
	border-bottom-right-radius: var(--minipublic-border-radius);
}
+66 −0
Numéro de ligne d'origine Numéro de ligne de diff Ligne de diff
:root {
	/*
	Définie par défaut par minipublic() d'après la config couleur_login
	Mais vous pouvez forcer une valeur ici
	--minipublic-color-theme--h: 337;
	--minipublic-color-theme--s: 81%;
	--minipublic-color-theme--l: 47%;
	 */
	--minipublic-color-theme--hs: var(--minipublic-color-theme--h), var(--minipublic-color-theme--s);

	--minipublic-color-theme: hsl(var(--minipublic-color-theme--hs), var(--minipublic-color-theme--l));
	--minipublic-color-theme-light: hsl(var(--minipublic-color-theme--hs), calc(var(--minipublic-color-theme--l) + 25%));
	--minipublic-color-theme-dark: hsl(var(--minipublic-color-theme--hs), calc(var(--minipublic-color-theme--l) - 25%));

	--minipublic-color-gray-lighter: rgba(255, 255, 255, 0.7);
	--minipublic-color-gray-light: rgba(255, 255, 255, 0.5);
	--minipublic-color-gray-dark: rgba(0, 0, 0, 0.5);
	--minipublic-color-black: #333;


	--minipublic-line-height: 1.5em;
	--minipublic-border-radius: 0.25rem;
	--minipublic-margin-bottom: 1.5em;

	/* couleurs partagées */
	--minipublic-btn-color-white:                #fff;
	--minipublic-btn-color-black:                #333;
	--minipublic-btn-color-border-accent:        hsla(0, 0%, 0%, 0.2);
	--minipublic-btn-color-focus:                hsla(var(--minipublic-color-theme--h), calc(var(--minipublic-color-theme--s) * 3), var(--minipublic-color-theme--l), 0.5);

	--minipublic-btn-color-main-bg:              hsl(var(--minipublic-color-theme--hs), calc(var(--minipublic-color-theme--l) * 0.9));
	--minipublic-btn-color-main-border:          var(--minipublic-btn-color-main-bg);
	--minipublic-btn-color-main-text:            var(--minipublic-btn-color-white);
	--minipublic-btn-color-main-hover-bg:        hsl(var(--minipublic-color-theme--hs), calc(var(--minipublic-color-theme--l) * 0.75));
	--minipublic-btn-color-main-hover-border:    var(--minipublic-btn-color-main-hover-bg);
	--minipublic-btn-color-main-hover-text:      var(--minipublic-btn-color-main-text);
	--minipublic-btn-color-main-active-bg:       hsl(var(--minipublic-color-theme--hs), calc(var(--minipublic-color-theme--l) * 0.6));
	--minipublic-btn-color-main-active-border:   var(--minipublic-btn-color-main-active-bg);
	--minipublic-btn-color-main-active-text:     var(--minipublic-btn-color-main-text);

	/* taille normale */
	--minipublic-btn-font-size:           1em;
	--minipublic-btn-padding-x:           1rem;
	--minipublic-btn-padding-y:           0.5rem;
	--minipublic-btn-margin:              0.25em;
	--minipublic-btn-border-radius:       0.25em;
	--minipublic-btn-gutter:              0.5em;

	--minipublic-color-success--h: 72;
	--minipublic-color-success--s: 66%;
	--minipublic-color-success--l: 62%;
	--minipublic-color-error--h: 356;
	--minipublic-color-error--s: 70%;
	--minipublic-color-error--l: 57%;
	--minipublic-color-notice--h: 47;
	--minipublic-color-notice--s: 100%;
	--minipublic-color-notice--l: 62%;
	--minipublic-color-info--h: 197;
	--minipublic-color-info--s: 56%;
	--minipublic-color-info--l: 27%;

	--minipublic-alert-spacing-x: 1.25rem;
	--minipublic-alert-spacing-y: 1.25rem;
	--minipublic-alert-iconsize: 0.0rem;
	--minipublic-alert-closesize: 1.25em;
}