Current oav website

This commit is contained in:
Charlie Root
2023-03-20 12:18:38 +01:00
commit a096ce07cf
3270 changed files with 261778 additions and 0 deletions

View File

@ -0,0 +1,601 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
$core->auth->user_prefs->addWorkspace('interface');
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />
<meta name="GOOGLEBOT" content="NOSNIPPET" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bibliothèque de styles - Dotclear - 2.7</title>
<link rel="icon" type="image/png" href="images/favicon96-login.png" />
<?php
$js = [];
if ($core->auth->user_prefs->interface->darkmode) {
echo dcPage::cssLoad('style/default-dark.css');
} else {
echo dcPage::cssLoad('style/default.css');
}
if ($core->auth->user_prefs->interface->htmlfontsize) {
$js['htmlFontSize'] = $core->auth->user_prefs->interface->htmlfontsize;
}
// Set some JSON data
echo dcUtils::jsJson('dotclear_init', $js);
?>
<script src="js/jquery/jquery.js"></script>
<script src="js/jquery/jquery-ui.custom.js"></script>
<script src="js/jquery/jquery.ui.touch-punch.js"></script>
<script src="js/jquery/jquery.pageTabs.js"></script>
<script src="js/jquery/jquery.biscuit.js"></script>
<script src="js/prepend.js"></script>
<script src="js/common.js"></script>
<script src="js/prelude.js"></script>
<script src="js/_charte.js"></script>
</head>
<body id="dotclear-admin" class="no-js guideline<?php $core->auth->user_prefs->interface->dynfontsize ? ' responsive-font' : '';?>">
<ul id="prelude">
<li><a href="#content">Aller au contenu</a></li>
<li><a href="#main-menu">Aller au menu</a></li>
<li><a href="#qx">Aller à la recherche</a></li>
</ul>
<div id="header">
<h1><a href="./index.php"><span class="hidden">Dotclear</span></a></h1>
<div id="top-info-blog">
<p>Bibliothèque de styles - Dotclear - 2.6+</p>
</div>
<ul id="top-info-user"><li>Octobre 2013</li></ul>
</div><!-- /header -->
<div id="wrapper" class="clearfix">
<div class="hidden-if-no-js collapser-box"><button type="button" id="collapser" class="void-btn">
<img class="collapse-mm visually-hidden" src="images/collapser-hide.png" alt="Cacher le menu" />
<img class="expand-mm visually-hidden" src="images/collapser-show.png" alt="Montrer le menu" />
</button></div>
<div id="main">
<div id="content" class="clearfix">
<h2>Typographie</h2>
<h3 id="texte">Textes</h3>
<p>La font-size de base est à 1.2rem (la valeur <code>1rem</code> correspond à 10px). Si vous utilisez l'unité <code>rem</code> pensez à faire précéder la déclaration par son équivalent
en pixels pour rester compatible avec Internet Explorer. L'interlignage courant est à 1.5.</p>
<p>La liste suivante est de class <code>"nice"</code>. Elle est semblable aux listes ordinaires mais avec des puces carrées.</p>
<ul class="nice">
<li>Les textes courants sont en Arial, Helvetica ou sans-serif. </li>
<li>Le code adopte la fonte Andale Mono, Courier New ou monospace.</li>
<li>Les liens ont des aspects différents au focus et au survol. Il faut conserver cette distinction, nécessaire à l'accessibilité et l'ergonomie.</li>
</ul>
<h3 id="titres">Titre h3</h3>
<p>Le titre de niveau h1 est réservé au titre du site-admin. Le titre de niveau h2 est réservé au breadcrumb/titre de la page courante. On utilise les titres de niveau h3 en premier niveau de titre à l'intérieur des pages, comme sur la page Import/Export.</p>
<p>Il ne faut pas choisir un niveau de titre en fonction de son aspect mais respecter une hiérarchie cohérente. On peut obtenir visuellement l'aspect d'un titre h3 en donnant à l'élément la class <code>"as_h3"</code>.</p>
<h4>Titre de niveau h4</h4>
<p>On peut obtenir visuellement l'aspect d'un titre h4 en donnant à l'élément la class <code>"as_h4"</code>.</p>
<h5>Titre de niveau h5</h5>
<p>Le titre de niveau h5 est assez peu employé mais son style est prévu. Dans une admin de base, on utilise les niveaux
h5 pour certains éléments du sidebar du billet, mais un style particulier leur est alors appliqué pour ressembler aux autres
items de ce sidebar.</p>
<div class="fieldset">
<h4>Titres des encadrés</h4>
<p>Les titres de boîte encadrées (div de class <code>"fieldset"</code>, comme ici) se présentent comme ci-dessus.</p>
<p>On peut utiliser, quel que soit le niveau hx de cet intertitre la class <code>"pretty-title"</code> pour obtenir l'effet ci-dessus.</p>
</div>
<h4 class="smart-title">Autre variante</h4>
<p>On dispose également d'une class <code>"smart-title"</code> pour obtenir une présentation comme celle du titre de ce paragraphe.</p>
<h2>Layouts</h2>
<h3 id="onglets">Onglets</h3>
<p>Les descriptions des constructions en multi-colonnes ci-dessous présentent un exemple de répartition en onglets.</p>
<p>Chacun de ces onglets doit être défini à l'aide d'une <code>&lt;div class="multi-part"&gt;</code>. Ils seront alors automatiquement présentés sous forme d'onglets.</p>
<h3 id="multi-colonnage">Multi-colonnage</h3>
<div id="one-box" class="multi-part" title="One-box">
<h4>Boîtes distribuées horizontalement</h4>
<div class="one-box">
<div class="box">
<p><span class="step">1</span> Toutes les boîtes de class <code>"box"</code> placées à l'intérieur d'une boîte de class <code>"one-box"</code> se distribuent horizontalement (imaginez que chaque boîte est un mot dans un paragraphe). Si les largeurs de ces boîtes ne sont pas spécifiquement définies dans la CSS, elle s'ajustent à leur contenu.</p>
</div>
<div class="box">
<p><span class="step">2</span> Voici une petite boîte.</p>
</div>
<div class="box">
<p><span class="step">3</span> Une autre petite boîte.</p>
</div>
<div class="box">
<p><span class="step">4</span> Par défaut les « lignes » de boîtes <code>"box"</code> sont justifiées au sein de la boîte <code>"one-box"</code> et l'espacement se répartit entre elles.</p>
</div>
<div class="box">
<p><span class="step">5</span> Si vous souhaitez un autre alignement des boîtes entre elles vous pouvez ajouter les class :</p>
<ul class="nice clear">
<li><code>"txt-left"</code>,</li>
<li><code>"txt-right"</code></li>
<li>ou <code>"txt-center"</code></li>
</ul>
<p>à la class <code>"one-box"</code>.</p>
</div>
<div class="box">
<p><span class="step">6</span> Le cadre placé ici autour de chaque boîte ne fait pas partie des styles par défaut.</p>
</div>
</div>
</div>
<div id="two-boxes" class="multi-part" title="Two-boxes">
<h4>Boîtes distribuées deux par deux</h4>
<div>
<div class="two-boxes odd">
<p><span class="step">1</span> Les boîtes de class <code>"two-boxes"</code> ont une règle CSS <code>display:inline-block;</code>. Elles se rangent alternativement à gauche et à droite. Pour plus de clarté, les blocs sont ici numérotés avec leur ordre dans le flux.</p>
</div><!--
--><div class="two-boxes even">
<p><span class="step">2</span> On peut assortir une boîte des class <code>"odd"</code> (nothing left) et <code>"even"</code> pour que les marges se placent correctement.</p>
</div><!--
--><div class="two-boxes odd">
<p><span class="step">3</span> Attention, il faut soit ne pas retourner à la ligne entre la fermeture d'une boîte <code>"two-boxes"</code> et l'ouverture de la suivante soit adopter la méthode de commentaire vide mise en place ici et expliquée chez <a href="https://www.alsacreations.com/astuce/lire/1432-display-inline-block-espaces-indesirables.html">Alsacréations</a> («&nbsp;Méthode 2&nbsp;»).</p>
</div><!--
--><div class="two-boxes even">
<div class="two-boxes odd">
<p><span class="step">4</span> On peut bien sûr imbriquer des boîtes de class <code>"two-boxes"</code>
au sein d'une boîte <code>"two-boxes" afin qu'elles…</code>…</p>
</div><div class="two-boxes even">
<p><span class="step">4 bis</span>… se distribuent horizontalement comme dans une boîte <code>"one-box"</code>.</p>
</div>
</div>
</div>
</div>
<div id="three-boxes" class="multi-part" title="Three-boxes">
<h4>Boîtes distribuées trois par trois</h4>
<div>
<div class="three-boxes">
<div class="box">
<p>Sur le même principe que les « two-boxes » on peut utiliser des boîtes de class <code>"three-boxes"</code> pour répartir les contenus sur trois colonnes de 30% chacune (le reste est occupé par les marges).</p>
</div>
</div><!--
--><div class="three-boxes">
<div class="box">
<p>Comme pour les "two-boxes" il faut soit ne pas laisser d'espace ou de retour à la ligne entre les boîtes, soit adopter la méthode recommandée plus haut.</p>
</div>
</div><!--
--><div class="three-boxes">
<div class="box">
<p>Dans les « two-boxes » comme dans les « three-boxes », on peut placer à l'intérieur plusieurs autres div de class="box" qui s'afficheront les unes à côté des autres ou l'une en dessous de l'autre selon la place dont elles disposent.</p>
</div>
</div>
</div>
</div>
<div id="two-cols-50-50" class="multi-part" title="Two-cols (50/50)">
<h4>Deux colonnes flottantes de largeurs égales</h4>
<div class="two-cols clearfix">
<div class="col">
<p>La div englobante porte la class <code>"two-cols"</code>, chacune de ses div porte la class <code>"col"</code>.
Sans autre précision les deux colonnes sont d'égale largeur.</p>
</div>
<div class="col">
<p>Attention : ces colonnes sont construites avec des flottants, il faut donc penser à mettre une class <code>clear</code> à l'élément suivant ou ajouter la class <code>"clearfix"</code> à la <code>div class="two-cols"</code>.</p>
</div>
</div>
</div>
<div id="two-cols-70-30" class="multi-part" title="Two-cols (70/30)">
<h4>Deux colonnes flottantes de largeurs inégales</h4>
<div class="two-cols clearfix">
<div class="col70">
<p><span class="step">col70</span> La div englobante porte la class <code>"two-cols"</code>.
Pour obtenir des colonnes inégales, on dispose des classes <code>"col70"</code> et <code>col30</code> à attribuer à l'une ou à l'autre de ses colonnes.</p>
</div>
<div class="col30">
<p><span class="step">col30</span> Penser à mettre une class <code>"clear"</code> à l'élément suivant ou ajouter la class <code>"clearfix"</code> <code>div class="two-cols"</code>.</p>
</div>
</div>
</div>
<div id="three-cols" class="multi-part" title="Three-cols (deprecated)">
<h4>Trois colonnes flottantes de largeurs égales</h4>
<p class="warning"><strong>Deprecated.</strong> Ces règles sont conservées dans la 2.6 par souci de rétro-compatibilité mais il est recommandé d'utiliser désormais le colonnage <a href="#three-boxes">three-boxes</a>.</p>
<div class="three-cols clearfix">
<div class="col">
<h5>Colonne 1</h5>
<p>La div englobante porte la class <code>"three-cols"</code>, chacune de ses div porte la class <code>"col"</code>. Les trois colonnes sont d'égale largeur.</p>
</div>
<div class="col">
<h5>Colonne 2</h5>
<p>Voici une deuxième colonne. N'oubliez pas d'ajouter la class <code>"clearfix"</code> à la class <code>"three-cols"</code> ou un élément de class <code>"clear"</code> après cet élément.</p>
</div>
<div class="col">
<h5>Colonne 3</h5>
<p>Voilà la troisième colonne.</p>
</div>
</div>
</div>
<hr />
<p><strong>Note :</strong> dans les exemples les valeurs et les numérotations sont placées dans un <code>span class="step"</code> (et ressortent donc dans un petit bloc à fond gris).</p>
<h2>Interactions</h2>
<h3 id="elements">Éléments de formulaire</h3>
<form class="two-cols clearfix" action="#">
<div class="col">
<p><label for="ex1">Label simple + input text :</label><input id="ex1" type="text" /></p>
<p class="form-note">p class="form-note".</p>
<p><label for="ex4" class="classic">Label class="classic" + input text :</label> <input id="ex4" type="text" /></p>
<p><label for="ex2" class="required"><abbr title="Champ obligatoire">*</abbr> Label class="required" :</label> <input id="ex2" type="text" required placeholder="exemple" /><span class="form-note">span class="form-note"</span></p>
<p><label for="ex11" class="bold">Label class="bold" :</label> <input id="ex11" type="text" /></p>
<p class="form-note">La class="bold" est bien sûr à écrire en minuscules.</p>
<p><label for="ex3">Input class="maximal" :</label> <input id="ex3" type="text" class="maximal" /></p>
</div>
<div class="col">
<p class="field"><label for="ex5">p.field label + input :</label><input id="ex5" type="text" /></p>
<p class="field"><label for="ex6">p.field label + select :</label>
<select id="ex6"><option value="opt2">Option 2</option><option selected="selected" value="opt2">Option 2</option></select>
</p>
<p><label class="classic" for="ex7"><input type="checkbox" checked="checked" id="ex7" value="1" name="ex7" />
Checkbox (label.classic)</label></p>
<p><label class="classic" for="ex8-1"><input type="radio" checked="checked" id="ex8-1" value="ex8-1" name="ex8-1" />
Bouton radio 1 (label.classic)</label></p>
<p><label class="classic" for="ex8-2"><input type="radio" id="ex8-2" value="ex8-2" name="ex8-2" />
Bouton radio 2 (label.classic)</label></p>
<p class="form-note">Les checkboxes et les boutons radio sont dans la balise &lt;label&gt;.</p>
<p><label class="classic" for="ex9"><input type="checkbox"
checked="checked" id="ex9" value="1" title="intitulé du champ" /></label> <label
for="ex10" class="classic">checkbox.classic + label class="classic" :</label> <input id="ex10" type="text" /></p>
</div>
</form>
<form action="#" class="clear">
<fieldset>
<legend>Légende de fieldset</legend>
<p>Attention: Les fieldsets ne doivent être utilisés que pour isoler un groupe de champs au sein d'un formulaire.</p>
</fieldset>
</form>
<h3 id="boutons">Boutons</h3>
<div class="clearfix">
<p><a class="button add">a.button.add</a> Se place en haut à droite (dans un p.top-add)</p>
<p><input type="button" value="Type button" /> <a href="#" class="button">a.button</a> <input type="reset" value="Type reset" /> <a href="#" class="button reset">a.reset</a></p>
<p><input type="submit" value="Type submit" /> <input type="submit" class="delete" value="Type submit class delete" /> <a href="#" class="button delete">a.button delete</a></p>
<p><input type="submit" value="Type submit class disabled" class="disabled" /></p>
</div>
<h3 id="messages">Messages</h3>
<h4 class="smart-title">Messages système</h4>
<p>Il existe quatre types de messages système auxquels correspondent des classes CSS : .error, .message, .success, .warning-msg. Ils s'affichent en haut de page, sous le titre/breadcrumb.</p>
<div class="message">
<p>Message simple. Le plus souvent horodaté dcPage::message</p>
</div>
<div class="success">
<p>Message de succès. Le plus souvent horodaté dcPage::success</p>
</div>
<div class="warning-msg">
<p>Message warning. Non horodaté dcPage::warning</p>
</div>
<div class="error">
<p>Message d'erreur. Non horodaté dcPage::error</p>
</div>
<p>La classe .static-msg peut être utilisée directement pour affichage en haut de page :</p>
<div class="static-msg">
<p>Comme le message simple mais sans effets de transition.</p>
</div>
<p>Un type de message réservé à Dotclear peut s'afficher en haut de la page :</p>
<div class="dc-update">
<h3>Dotclear 42 est disponible</h3>
<p><a class="button submit" href="#">Mettre à jour maintenant</a>
<a class="button" href="u#">Me le rappeler plus tard</a></p>
<p class="updt-info"><a href="#">Informations sur cette version</a></p>
</div>
<h4 class="smart-title">Messages contextuels</h4>
<p class="warn">Paragraphe de message d'alerte class warn ou warning.</p>
<p class="info">Paragraphe de message de class info.</p>
<p>Ces messages sont en display:inline-block. Le fond s'adapte à la longueur du message.</p>
<h2>Navigation</h2>
<h3 id="direct">Selecteur d'accès direct</h3>
<p>Sur des pages longues et denses comme les pages about:config ou about:preferences, on peut utiliser un sélecteur pour faciliter l'accès direct aux sections.</p>
<p class="anchor-nav">
<label class="classic" for="lp_nav">Aller à : </label>
<select id="lp_nav" name="lp_nav">
<option value="#l_accessibility">accessibility</option>
<option value="#l_dashboard">dashboard</option>
<option value="#l_dmhostingmonitor">dmhostingmonitor</option>
<option value="#l_dmpending">dmpending</option>
<option value="#l_favorites">favorites</option>
<option value="#l_filters">filters</option>
<option value="#l_interface">interface</option>
<option value="#l_lists">lists</option>
<option value="#l_toggles">toggles</option>
</select>
<input type="submit" id="lp_submit" value="Ok" style="display: none;" />
<input type="hidden" value="aboutConfig" name="p" />
</p>
<h3 id="prevnext">Navigation contextuelle</h3>
<p><a title="Titre du lien" href="https://fr.dotclear.org/blog" class="onblog_link outgoing">Lien vers le blog <img alt="" src="images/outgoing-link.svg" /></a></p>
<p class="nav_prevnext"><a title="Titre de l'élément précédente" href="post.php?id=4145">«&nbsp;Élément précédent</a> | <a title="Titre de l'élément suivant" href="#">Élément suivant&nbsp;»</a></p>
<h3 id="pseudo-tabs">Pseudo-onglets </h3>
<p>Les pseudo-onglets permettent d'ajouter des sous-pages qui sont des liens vers d'autres pages, par opposition aux onglets qui sont des sections internes à la page.</p>
<p>Les pseudo-onglets sont à positionner immédiatement après le breadcrumb (ici un hr simule le trait sous le breadcrumb).</p>
<p>Ces pseudo-onglets doivent être définis avec un <code>&lt;ul class="pseudo-tabs"&gt;</code> et des <code>&lt;li&gt;</code>.</p>
<hr style="margin-bottom: .75em;" />
<ul class="pseudo-tabs">
<li><a href="#">Page 1</a></li>
<li><a href="#">Autre faux onglet</a></li>
<li><a href="#" class="active">Onglet actif</a></li>
<li><a href="#">Liste 4</a></li>
</ul>
<h2 id="common">Tableaux</h2>
<p>Il existe deux mises en forme type de tableaux selon que l'on cherche à faire un tableau ordinaire
ou un tableau dont on peut déplacer les lignes par glisser déposer (voir plus bas). Cependant certaines règles
sont communes à tout les tableaux.</p>
<h3>Règles communes</h3>
<h4>Largeur du tableau</h4>
<p>Sauf pour des tableaux particuliers (absents dans l'admin mais qui pourraient être nécessaires
à un plugin,les tableaux occupent toute la largeur de la page. Afin que les tableaux soient consultables
sur un mobile en navigant horizontalement, on englobe le tableau dans une <code>div class="table-outer"</code>,
qui servira de «&nbsp;conteneur&nbsp;».</p>
<h4>Accessibilité</h4>
<p>Les éléments caption, th, scope sont nécessaires à l'accessibilité. Ne les oubliez pas&nbsp;!&nbsp;».
On peut utiliser la <code>class="hidden"</code> sur l'élément <code>caption</code> (qui accueille
le titre du tableau) si vous ne souhaitez pas qu'il soit affiché sur la page.</p>
<h4>Les classes</h4>
<p>Des classes particulières peuvent être attribuées aux lignes :</p>
<ul>
<li><code>line</code> (systématique) : pour les traits horizontaux et le fond gris léger
au survol&nbsp;;</li>
<li><code>offline</code> : pour un noir estompé (gris quoi).</li>
</ul>
<p>Des classes particulières peuvent être appliquées aux cellules :</p>
<ul>
<li><code>nowrap</code> : pas de retour à la ligne dans la cellule, quelle que soit la
largeur de la page&nbsp;;</li>
<li><code>maximal</code> : la cellule prendra toute la largeur restante disponible&nbsp;;</li>
<li><code>count</code> : le contenu de la cellule sera aligné à droite avec un petit retrait.</li>
</ul>
<h3 id="courants">Tableau classique</h3>
<div class="table-outer">
<table>
<caption class="hidden">Liste des publications</caption>
<tr>
<th colspan="2" class="first">Titre</th>
<th scope="col">Date</th>
<th scope="col">Catégorie</th>
<th scope="col">Auteur</th>
<th scope="col">Commentaires</th>
<th scope="col">Rétroliens</th>
<th scope="col">État</th>
</tr>
<tr class="line">
<td class="nowrap"><input type="checkbox" name="name1" value="value1" /></td>
<td class="maximal" scope="row"><a href="#">Mon cher Franck</a></td>
<td class="nowrap count">06/08/2013 19:16</td>
<td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td><td class="nowrap count">4</td>
<td class="nowrap count">0</td><td class="nowrap status"><img alt="Publié" title="Publié" src="images/check-on.png" /> <img alt="Sélectionné" title="Sélectionné" src="images/selected.png" /> </td>
</tr>
<tr class="line offline">
<td class="nowrap"><input type="checkbox" name="name2" value="value2" /></td>
<td class="maximal" scope="row"><a href="#">Dotclear 2.3.0</a></td>
<td class="nowrap count">16/05/2011 22:29</td>
<td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td><td class="nowrap count">5</td>
<td class="nowrap count">0</td><td class="nowrap status"><img alt="Non publié" title="Non publié" src="images/check-off.png" /> <img alt="Sélectionné" title="Sélectionné" src="images/selected.png" /> </td>
</tr>
<tr class="line">
<td class="nowrap"><input type="checkbox" name="entries[]" value="2148" /></td>
<td class="maximal" scope="row"><a href="#">Causons opéra au Tamm Bara</a></td>
<td class="nowrap count">24/11/2009 23:10</td>
<td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td>
<td class="nowrap count">4</td><td class="nowrap count">1</td>
<td class="nowrap status"><img alt="Publié" title="Publié" src="images/check-on.png" /> </td>
</tr>
<tr class="line">
<td class="nowrap"><input type="checkbox" name="entries[]" value="2136" /></td>
<td class="maximal" scope="row"><a href="#">Souffler six bougies</a></td>
<td class="nowrap count">14/08/2009 00:00</td><td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td>
<td class="nowrap count">4</td><td class="nowrap count">2</td><td class="nowrap status"><img alt="Publié" title="Publié" src="images/check-on.png" /> </td>
</tr>
<tr class="line">
<td class="nowrap"><input type="checkbox" name="entries[]" value="2129" /></td>
<td class="maximal" scope="row"><a href="#">Dotclear et grenadine, troisième édition</a></td>
<td class="nowrap count">15/06/2009 07:39</td>
<td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td>
<td class="nowrap count">9</td>
<td class="nowrap count">1</td>
<td class="nowrap status"><img alt="Publié" title="Publié" src="images/check-on.png" /> </td>
</tr>
<tr class="line">
<td class="nowrap"><input type="checkbox" name="entries[]" value="2111" /></td>
<td class="maximal" scope="row"><a href="#">L'abc dotclear est né</a></td>
<td class="nowrap count">19/03/2009 10:31</td><td class="nowrap"><a href="#">Les aventures du clafoutis</a></td>
<td class="nowrap">kozlika</td>
<td class="nowrap count">1</td><td class="nowrap count">0</td>
<td class="nowrap status"><img alt="Publié" title="Publié" src="images/check-on.png" /> </td>
</tr>
</table>
</div>
<h3 id="dragable">Tableau avec ordonnancement</h3>
<p>Les tableaux permettant l'ordonnancement doivent offrir la possibilité d'effectuer le classement grâce à
des inputs placés en début de ligne pour que le classement soit possible même lorsque cette fonctionnalité est
désactivée (via les préférences utilisateurs, voire une désactivation complète du javascript dans le navigateur).</p>
<div class="table-outer">
<table class="maximal dragable">
<thead>
<tr>
<th colspan="3">Titre</th>
<th>Date</th>
<th>Auteur</th>
<th>Commentaires</th>
<th>Rétroliens</th>
<th>État</th>
</tr>
</thead>
<tbody id="pageslist">
<tr class="line" id="p10899">
<td class="nowrap handle minimal">
<input type="text" size="2" name="order[10899]" maxlength="3" value="1" class="position" title="position de Mentions légales" />
</td>
<td class="nowrap">
<input type="checkbox" name="entries[]" value="10899" title="Sélectionner cette page" />
</td>
<td class="maximal"><a href="#">Mentions légales</a>
</td>
<td class="nowrap">17/12/2008 07:35</td>
<td class="nowrap">franck</td>
<td class="nowrap">0</td>
<td class="nowrap">0</td>
<td class="nowrap status">
<img alt="Publié" title="Publié" src="images/check-on.png" />
</td>
</tr>
<tr class="line" id="p10937">
<td class="nowrap handle minimal">
<input type="text" size="2" name="order[10937]" maxlength="3" value="2" class="position" title="position de Page active et cachée" />
</td>
<td class="nowrap">
<input type="checkbox" name="entries[]" value="10937" title="Sélectionner cette page" />
</td>
<td class="maximal"><a href="#">Page active et cachée</a>
</td>
<td class="nowrap">26/10/2012 11:08</td>
<td class="nowrap">admin</td>
<td class="nowrap">0</td>
<td class="nowrap">0</td>
<td class="nowrap status">
<img alt="Publié" title="Publié" src="images/check-on.png" />
<img alt="Masqué" title="Masqué" src="images/hidden.png" />
</td>
</tr>
<tr class="line offline" id="p11047">
<td class="nowrap handle minimal">
<input type="text" size="2" name="order[11047]" maxlength="3" value="3" class="position" title="position de Page révisionnable" />
</td>
<td class="nowrap">
<input type="checkbox" name="entries[]" value="11047" title="Sélectionner cette page" />
</td>
<td class="maximal"><a href="#">Page révisionnable</a>
</td>
<td class="nowrap">14/12/2012 13:26</td>
<td class="nowrap">admin</td>
<td class="nowrap">0</td>
<td class="nowrap">0</td>
<td class="nowrap status">
<img alt="En attente" title="En attente" src="images/check-wrn.png" />
</td>
</tr>
<tr class="line offline" id="p10939">
<td class="nowrap handle minimal">
<input type="text" size="2" name="order[10939]" maxlength="3" value="4" class="position" title="position de Programme" />
</td>
<td class="nowrap">
<input type="checkbox" name="entries[]" value="10939" title="Sélectionner cette page" />
</td>
<td class="maximal"><a href="#">Programme</a>
</td>
<td class="nowrap">26/10/2020 11:23</td>
<td class="nowrap">admin</td>
<td class="nowrap">0</td>
<td class="nowrap">0</td>
<td class="nowrap status">
<img alt="Programmé" title="Programmé" src="images/scheduled.png" />
</td>
</tr>
<tr class="line offline" id="p10940">
<td class="nowrap handle minimal">
<input type="text" size="2" name="order[10940]" maxlength="3" value="5" class="position" title="position de Protégée" />
</td>
<td class="nowrap">
<input type="checkbox" name="entries[]" value="10940" title="Sélectionner cette page" />
</td>
<td class="maximal"><a href="#">Protégée</a>
</td>
<td class="nowrap">26/10/2012 11:23</td>
<td class="nowrap">admin</td>
<td class="nowrap">0</td>
<td class="nowrap">0</td>
<td class="nowrap status">
<img alt="En attente" title="En attente" src="images/check-wrn.png" />
<img alt="Protégé" title="Protégé" src="images/locker.png" />
</td>
</tr>
</tbody>
</table>
</div>
<h2 id="iconset">Icônes</h2>
<p>Les icônes utilisées dans l'administration sont présentes en deux formats&nbsp; 64*64px pour les grandes
(qui sont affichées sur le tableau de bord si la page correspondante est choisie en favori par l'utilisateur) et
16*16px pour les petits formats.</p>
<p>La plupart sont dérivées de la fonte d'icônes <a href="https://www.elegantthemes.com/blog/resources/elegant-icon-font">Elegant Font</a>. Les autres sont des images vectorielles réalisées
par la DC Team. Nous les avons nommées <em>Traviata</em>. La palette de couleurs utilisée est la suivante&nbsp;:</p>
<p class="txt-center"><img src="images/palette-traviata.png" alt="palette des couleurs utilisées pour les icônes" /></p>
<p class="txt-center">Bleu&nbsp;: #137bbb - Vert&nbsp;: #9ac123 - Rouge&nbsp;: #c44d58 - Bleu ciel&nbsp;: #a2cbe9 - Gris clair&nbsp;: #ececec -
Gris moyen&nbsp;: #b2b2b2 - Gris foncé&nbsp;: #676e78.</p>
</div><!-- /content -->
</div><!-- /main -->
<div id="main-menu">
<ul><li class="pretty-title">Typographie
<ul>
<li><a href="#texte">Texte</a></li>
<li><a href="#titres">Titres hx</a></li>
</ul>
</li>
<li class="pretty-title">Layouts
<ul>
<li><a href="#onglets">Onglets</a></li>
<li><a href="#multi-colonnage">Multi-colonnage</a></li>
</ul>
</li>
<li class="pretty-title">Interactions
<ul>
<li><a href="#elements">Éléments de formulaire</a></li>
<li><a href="#boutons">Boutons</a></li>
<li><a href="#messages">Messages</a></li>
</ul>
</li>
<li class="pretty-title">Navigation
<ul>
<li><a href="#direct">Accès direct</a></li>
<li><a href="#prevnext">Précédent, suivant</a></li>
<li><a href="#pseudo-tabs">Pseudo-onglets</a></li>
</ul>
</li>
<li class="pretty-title">Tableaux
<ul>
<li><a href="#commons">Règles communes</a></li>
<li><a href="#courants">Tableaux courants</a></li>
<li><a href="#dragables">Tableaux ordonnancés</a></li>
</ul>
</li>
</ul>
<div class="info vertical-separator">
<p>Cette page vise à présenter les règles graphiques et conventions utilisées dans les pages de l'administration
d'une installation Dotclear, à l'usage des contributeurs et développeurs d'extensions. Elle en est elle-même
une illustration. L'observation de son code source peut donc servir de complément aux descriptions.</p>
</div>
</div><!-- /main-menu -->
<div id="footer">
<a href="https://dotclear.org/" title="Merci de manger des clafoutis."><img src="style/dc_logos/w-dotclear90.png" alt="Merci d'utiliser Dotclear 2.6-dev." /></a>
</div><!-- /footer -->
<!--
.
,;:'`'::
__||
_____/LLLL\_
\__________"|
~^~^~^~^~^~^~^~^~^~
-->
</div><!-- /wrapper -->
</body>
</html>

410
dotclear._no/admin/auth.php Normal file
View File

@ -0,0 +1,410 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
# If we have a session cookie, go to index.php
if (isset($_SESSION['sess_user_id'])) {
$core->adminurl->redirect('admin.home');
}
# Loading locales for detected language
# That's a tricky hack but it works ;)
$dlang = http::getAcceptLanguage();
$dlang = ($dlang == '' ? 'en' : $dlang);
if ($dlang != 'en' && preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $dlang)) {
l10n::lang($dlang);
l10n::set(dirname(__FILE__) . '/../locales/' . $dlang . '/main');
}
if (defined('DC_ADMIN_URL')) {
$page_url = DC_ADMIN_URL . $core->adminurl->get('admin.auth');
} else {
$page_url = http::getHost() . $_SERVER['REQUEST_URI'];
}
$change_pwd = $core->auth->allowPassChange() && isset($_POST['new_pwd']) && isset($_POST['new_pwd_c']) && isset($_POST['login_data']);
$login_data = !empty($_POST['login_data']) ? html::escapeHTML($_POST['login_data']) : null;
$recover = $core->auth->allowPassChange() && !empty($_REQUEST['recover']);
$safe_mode = !empty($_REQUEST['safe_mode']);
$akey = $core->auth->allowPassChange() && !empty($_GET['akey']) ? $_GET['akey'] : null;
$user_id = $user_pwd = $user_key = $user_email = null;
$err = $msg = null;
# Auto upgrade
if (empty($_GET) && empty($_POST)) {
require dirname(__FILE__) . '/../inc/dbschema/upgrade.php';
try {
if (($changes = dcUpgrade::dotclearUpgrade($core)) !== false) {
$msg = __('Dotclear has been upgraded.') . '<!-- ' . $changes . ' -->';
}
} catch (Exception $e) {
$err = $e->getMessage();
}
}
# If we have POST login informations, go throug auth process
if (!empty($_POST['user_id']) && !empty($_POST['user_pwd'])) {
$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
$user_pwd = !empty($_POST['user_pwd']) ? $_POST['user_pwd'] : null;
}
# If we have COOKIE login informations, go throug auth process
elseif (isset($_COOKIE['dc_admin']) && strlen($_COOKIE['dc_admin']) == 104) {
# If we have a remember cookie, go through auth process with user_key
$user_id = substr($_COOKIE['dc_admin'], 40);
$user_id = @unpack('a32', @pack('H*', $user_id));
if (is_array($user_id)) {
$user_id = trim($user_id[1]);
$user_key = substr($_COOKIE['dc_admin'], 0, 40);
$user_pwd = null;
} else {
$user_id = null;
}
}
# Recover password
if ($recover && !empty($_POST['user_id']) && !empty($_POST['user_email'])) {
$user_id = !empty($_POST['user_id']) ? $_POST['user_id'] : null;
$user_email = !empty($_POST['user_email']) ? html::escapeHTML($_POST['user_email']) : '';
try
{
$recover_key = $core->auth->setRecoverKey($user_id, $user_email);
$subject = mail::B64Header('Dotclear ' . __('Password reset'));
$message =
__('Someone has requested to reset the password for the following site and username.') . "\n\n" .
$page_url . "\n" . __('Username:') . ' ' . $user_id . "\n\n" .
__('To reset your password visit the following address, otherwise just ignore this email and nothing will happen.') . "\n" .
$page_url . '?akey=' . $recover_key;
$headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
mail::sendMail($user_email, $subject, $message, $headers);
$msg = sprintf(__('The e-mail was sent successfully to %s.'), $user_email);
} catch (Exception $e) {
$err = $e->getMessage();
}
}
# Send new password
elseif ($akey) {
try
{
$recover_res = $core->auth->recoverUserPassword($akey);
$subject = mb_encode_mimeheader('Dotclear ' . __('Your new password'), 'UTF-8', 'B');
$message =
__('Username:') . ' ' . $recover_res['user_id'] . "\n" .
__('Password:') . ' ' . $recover_res['new_pass'] . "\n\n" .
preg_replace('/\?(.*)$/', '', $page_url);
$headers[] = 'From: ' . (defined('DC_ADMIN_MAILFROM') && DC_ADMIN_MAILFROM ? DC_ADMIN_MAILFROM : 'dotclear@local');
$headers[] = 'Content-Type: text/plain; charset=UTF-8;';
mail::sendMail($recover_res['user_email'], $subject, $message, $headers);
$msg = __('Your new password is in your mailbox.');
} catch (Exception $e) {
$err = $e->getMessage();
}
}
# Change password and retry to log
elseif ($change_pwd) {
try
{
$tmp_data = explode('/', $_POST['login_data']);
if (count($tmp_data) != 3) {
throw new Exception();
}
$data = [
'user_id' => base64_decode($tmp_data[0]),
'cookie_admin' => $tmp_data[1],
'user_remember' => $tmp_data[2] == '1'
];
if ($data['user_id'] === false) {
throw new Exception();
}
# Check login informations
$check_user = false;
if (isset($data['cookie_admin']) && strlen($data['cookie_admin']) == 104) {
$user_id = substr($data['cookie_admin'], 40);
$user_id = @unpack('a32', @pack('H*', $user_id));
if (is_array($user_id)) {
$user_id = trim($data['user_id']);
$user_key = substr($data['cookie_admin'], 0, 40);
$check_user = $core->auth->checkUser($user_id, null, $user_key) === true;
} else {
$user_id = trim($user_id);
}
}
if (!$core->auth->allowPassChange() || !$check_user) {
$change_pwd = false;
throw new Exception();
}
if ($_POST['new_pwd'] != $_POST['new_pwd_c']) {
throw new Exception(__("Passwords don't match"));
}
if ($core->auth->checkUser($user_id, $_POST['new_pwd']) === true) {
throw new Exception(__("You didn't change your password."));
}
$cur = $core->con->openCursor($core->prefix . 'user');
$cur->user_change_pwd = 0;
$cur->user_pwd = $_POST['new_pwd'];
$core->updUser($core->auth->userID(), $cur);
$core->session->start();
$_SESSION['sess_user_id'] = $user_id;
$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
if ($data['user_remember']) {
setcookie('dc_admin', $data['cookie_admin'], strtotime('+15 days'), '', '', DC_ADMIN_SSL);
}
$core->adminurl->redirect('admin.home');
} catch (Exception $e) {
$err = $e->getMessage();
}
}
# Try to log
elseif ($user_id !== null && ($user_pwd !== null || $user_key !== null)) {
# We check the user
$check_user = $core->auth->checkUser($user_id, $user_pwd, $user_key, false) === true;
if ($check_user) {
$check_perms = $core->auth->findUserBlog() !== false;
} else {
$check_perms = false;
}
$cookie_admin = http::browserUID(DC_MASTER_KEY . $user_id .
$core->auth->cryptLegacy($user_id)) . bin2hex(pack('a32', $user_id));
if ($check_perms && $core->auth->mustChangePassword()) {
$login_data = join('/', [
base64_encode($user_id),
$cookie_admin,
empty($_POST['user_remember']) ? '0' : '1'
]);
if (!$core->auth->allowPassChange()) {
$err = __('You have to change your password before you can login.');
} else {
$err = __('In order to login, you have to change your password now.');
$change_pwd = true;
}
} elseif ($check_perms && !empty($_POST['safe_mode']) && !$core->auth->isSuperAdmin()) {
$err = __('Safe Mode can only be used for super administrators.');
} elseif ($check_perms) {
$core->session->start();
$_SESSION['sess_user_id'] = $user_id;
$_SESSION['sess_browser_uid'] = http::browserUID(DC_MASTER_KEY);
if (!empty($_POST['blog'])) {
$_SESSION['sess_blog_id'] = $_POST['blog'];
}
if (!empty($_POST['safe_mode']) && $core->auth->isSuperAdmin()) {
$_SESSION['sess_safe_mode'] = true;
}
if (!empty($_POST['user_remember'])) {
setcookie('dc_admin', $cookie_admin, strtotime('+15 days'), '', '', DC_ADMIN_SSL);
}
$core->adminurl->redirect('admin.home');
} else {
if (isset($_COOKIE['dc_admin'])) {
unset($_COOKIE['dc_admin']);
setcookie('dc_admin', false, -600, '', '', DC_ADMIN_SSL);
}
if ($check_user) {
$err = __('Insufficient permissions');
} else {
$err = __('Wrong username or password');
}
}
}
if (isset($_GET['user'])) {
$user_id = $_GET['user'];
}
header('Content-Type: text/html; charset=UTF-8');
// Prevents Clickjacking as far as possible
header('X-Frame-Options: SAMEORIGIN'); // FF 3.6.9+ Chrome 4.1+ IE 8+ Safari 4+ Opera 10.5+
?>
<!DOCTYPE html>
<html lang="<?php echo $dlang; ?>">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Language" content="<?php echo $dlang; ?>" />
<meta name="ROBOTS" content="NOARCHIVE,NOINDEX,NOFOLLOW" />
<meta name="GOOGLEBOT" content="NOSNIPPET" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></title>
<link rel="icon" type="image/png" href="images/favicon96-logout.png" />
<link rel="shortcut icon" href="../favicon.ico" type="image/x-icon" />
<?php
echo dcPage::jsCommon();
?>
<link rel="stylesheet" href="style/default.css" type="text/css" media="screen" />
<?php
# --BEHAVIOR-- loginPageHTMLHead
$core->callBehavior('loginPageHTMLHead');
echo dcPage::jsLoad('js/_auth.js');
?>
</head>
<body id="dotclear-admin" class="auth">
<form action="<?php echo $core->adminurl->get('admin.auth'); ?>" method="post" id="login-screen">
<h1 role="banner"><?php echo html::escapeHTML(DC_VENDOR_NAME); ?></h1>
<?php
if ($err) {
echo '<div class="error" role="alert">' . $err . '</div>';
}
if ($msg) {
echo '<p class="success" role="alert">' . $msg . '</p>';
}
if ($akey) {
echo '<p><a href="' . $core->adminurl->get('admin.auth') . '">' . __('Back to login screen') . '</a></p>';
} elseif ($recover) {
echo
'<div class="fieldset" role="main"><h2>' . __('Request a new password') . '</h2>' .
'<p><label for="user_id">' . __('Username:') . '</label> ' .
form::field('user_id', 20, 32,
[
'default' => html::escapeHTML($user_id),
'autocomplete' => 'username'
]
) .
'</p>' .
'<p><label for="user_email">' . __('Email:') . '</label> ' .
form::email('user_email',
[
'default' => html::escapeHTML($user_email),
'autocomplete' => 'email'
]
) .
'</p>' .
'<p><input type="submit" value="' . __('recover') . '" />' .
form::hidden('recover', 1) . '</p>' .
'</div>' .
'<div id="issue">' .
'<p><a href="' . $core->adminurl->get('admin.auth') . '">' . __('Back to login screen') . '</a></p>' .
'</div>';
} elseif ($change_pwd) {
echo
'<div class="fieldset"><h2>' . __('Change your password') . '</h2>' .
'<p><label for="new_pwd">' . __('New password:') . '</label> ' .
form::password('new_pwd', 20, 255,
[
'autocomplete' => 'new-password'
]
) . '</p>' .
'<p><label for="new_pwd_c">' . __('Confirm password:') . '</label> ' .
form::password('new_pwd_c', 20, 255,
[
'autocomplete' => 'new-password'
]
) . '</p>' .
'</div>' .
'<p><input type="submit" value="' . __('change') . '" />' .
form::hidden('login_data', $login_data) . '</p>';
} else {
if (is_callable([$core->auth, 'authForm'])) {
echo $core->auth->authForm($user_id);
} else {
if ($safe_mode) {
echo '<div class="fieldset" role="main">';
echo '<h2>' . __('Safe mode login') . '</h2>';
echo
'<p class="form-note">' .
__('This mode allows you to login without activating any of your plugins. This may be useful to solve compatibility problems') . '&nbsp;</p>' .
'<p class="form-note">' . __('Disable or delete any plugin suspected to cause trouble, then log out and log back in normally.') .
'</p>';
} else {
echo '<div class="fieldset" role="main">';
}
echo
'<p><label for="user_id">' . __('Username:') . '</label> ' .
form::field('user_id', 20, 32,
[
'default' => html::escapeHTML($user_id),
'autocomplete' => 'username'
]
) . '</p>' .
'<p><label for="user_pwd">' . __('Password:') . '</label> ' .
form::password('user_pwd', 20, 255,
[
'autocomplete' => 'current-password'
]
) . '</p>' .
'<p>' .
form::checkbox('user_remember', 1) .
'<label for="user_remember" class="classic">' .
__('Remember my ID on this device') . '</label></p>' .
'<p><input type="submit" value="' . __('log in') . '" class="login" /></p>';
if (!empty($_REQUEST['blog'])) {
echo form::hidden('blog', html::escapeHTML($_REQUEST['blog']));
}
if ($safe_mode) {
echo
form::hidden('safe_mode', 1) .
'</div>';
} else {
echo '</div>';
}
echo
'<p id="cookie_help" class="error">' . __('You must accept cookies in order to use the private area.') . '</p>';
echo '<div id="issue">';
if ($safe_mode) {
echo
'<p><a href="' . $core->adminurl->get('admin.auth') . '" id="normal_mode_link">' . __('Get back to normal authentication') . '</a></p>';
} else {
echo '<p id="more"><strong>' . __('Connection issue?') . '</strong></p>';
if ($core->auth->allowPassChange()) {
echo '<p><a href="' . $core->adminurl->get('admin.auth', ['recover' => 1]) . '">' . __('I forgot my password') . '</a></p>';
}
echo '<p><a href="' . $core->adminurl->get('admin.auth', ['safe_mode' => 1]) . '" id="safe_mode_link">' . __('I want to log in in safe mode') . '</a></p>';
}
echo '</div>';
}
}
?>
</form>
</body>
</html>

113
dotclear._no/admin/blog.php Normal file
View File

@ -0,0 +1,113 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::checkSuper();
$blog_id = '';
$blog_url = '';
$blog_name = '';
$blog_desc = '';
# Create a blog
if (!isset($_POST['id']) && (isset($_POST['create']))) {
$cur = $core->con->openCursor($core->prefix . 'blog');
$blog_id = $cur->blog_id = $_POST['blog_id'];
$blog_url = $cur->blog_url = $_POST['blog_url'];
$blog_name = $cur->blog_name = $_POST['blog_name'];
$blog_desc = $cur->blog_desc = $_POST['blog_desc'];
try
{
# --BEHAVIOR-- adminBeforeBlogCreate
$core->callBehavior('adminBeforeBlogCreate', $cur, $blog_id);
$core->addBlog($cur);
# Default settings and override some
$core->blogDefaults($cur->blog_id);
$blog_settings = new dcSettings($core, $cur->blog_id);
$blog_settings->addNamespace('system');
$blog_settings->system->put('lang', $core->auth->getInfo('user_lang'));
$blog_settings->system->put('blog_timezone', $core->auth->getInfo('user_tz'));
if (substr($blog_url, -1) == '?') {
$blog_settings->system->put('url_scan', 'query_string');
} else {
$blog_settings->system->put('url_scan', 'path_info');
}
# --BEHAVIOR-- adminAfterBlogCreate
$core->callBehavior('adminAfterBlogCreate', $cur, $blog_id, $blog_settings);
dcPage::addSuccessNotice(sprintf(__('Blog "%s" successfully created'), html::escapeHTML($cur->blog_name)));
$core->adminurl->redirect("admin.blog", ['id' => $cur->blog_id]);
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
if (!empty($_REQUEST['id'])) {
$edit_blog_mode = true;
include dirname(__FILE__) . '/blog_pref.php';
} else {
dcPage::open(__('New blog'), dcPage::jsConfirmClose('blog-form'),
dcPage::breadcrumb(
[
__('System') => '',
__('Blogs') => $core->adminurl->get("admin.blogs"),
__('New blog') => ''
])
);
echo
'<form action="' . $core->adminurl->get("admin.blog") . '" method="post" id="blog-form">' .
'<div>' . $core->formNonce() . '</div>' .
'<p><label class="required" for="blog_id"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog ID:') . '</label> ' .
form::field('blog_id', 30, 32,
[
'default' => html::escapeHTML($blog_id),
'extra_html' => 'required placeholder="' . __('Blog ID') . '"'
]
) . '</p>' .
'<p class="form-note">' . __('At least 2 characters using letters, numbers or symbols.') . '</p> ';
echo
'<p><label class="required" for="blog_name"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog name:') . '</label> ' .
form::field('blog_name', 30, 255,
[
'default' => html::escapeHTML($blog_name),
'extra_html' => 'required placeholder="' . __('Blog name') . '" lang="' . $core->auth->getInfo('user_lang') . '" ' .
'spellcheck="true"'
]
) . '</p>' .
'<p><label class="required" for="blog_url"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog URL:') . '</label> ' .
form::url('blog_url',
[
'size' => 30,
'default' => html::escapeHTML($blog_url),
'extra_html' => 'required placeholder="' . __('Blog URL') . '"'
]
) . '</p>' .
'<p class="area"><label for="blog_desc">' . __('Blog description:') . '</label> ' .
form::textarea('blog_desc', 60, 5,
[
'default' => html::escapeHTML($blog_desc),
'extra_html' => 'lang="' . $core->auth->getInfo('user_lang') . '" spellcheck="true"'
]) . '</p>' .
'<p><input type="submit" accesskey="s" name="create" value="' . __('Create') . '" /></p>' .
'</form>';
dcPage::helpBlock('core_blog_new');
dcPage::close();
}

View File

@ -0,0 +1,74 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::checkSuper();
$blog_id = '';
$blog_name = '';
if (!empty($_POST['blog_id'])) {
try {
$rs = $core->getBlog($_POST['blog_id']);
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
if ($rs->isEmpty()) {
$core->error->add(__('No such blog ID'));
} else {
$blog_id = $rs->blog_id;
$blog_name = $rs->blog_name;
}
}
# Delete the blog
if (!$core->error->flag() && $blog_id && !empty($_POST['del'])) {
if (!$core->auth->checkPassword($_POST['pwd'])) {
$core->error->add(__('Password verification failed'));
} else {
try {
$core->delBlog($blog_id);
dcPage::addSuccessNotice(sprintf(__('Blog "%s" successfully deleted'), html::escapeHTML($blog_name)));
$core->adminurl->redirect("admin.blogs");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
}
dcPage::open(__('Delete a blog'), '',
dcPage::breadcrumb(
[
__('System') => '',
__('Blogs') => $core->adminurl->get("admin.blogs"),
__('Delete a blog') => ''
])
);
if (!$core->error->flag()) {
echo
'<div class="warning-msg"><p><strong>' . __('Warning') . '</strong></p>' .
'<p>' . sprintf(__('You are about to delete the blog %s. Every entry, comment and category will be deleted.'),
'<strong>' . $blog_id . ' (' . $blog_name . ')</strong>') . '</p></div>' .
'<p>' . __('Please give your password to confirm the blog deletion.') . '</p>';
echo
'<form action="' . $core->adminurl->get("admin.blog.del") . '" method="post">' .
'<div>' . $core->formNonce() . '</div>' .
'<p><label for="pwd">' . __('Your password:') . '</label> ' .
form::password('pwd', 20, 255, ['autocomplete' => 'current-password']) . '</p>' .
'<p><input type="submit" class="delete" name="del" value="' . __('Delete this blog') . '" />' .
form::hidden('blog_id', $blog_id) . '</p>' .
'</form>';
}
dcPage::close();

View File

@ -0,0 +1,907 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
$standalone = !isset($edit_blog_mode);
$blog_id = false;
if ($standalone) {
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('admin');
$blog_id = $core->blog->id;
$blog_status = $core->blog->status;
$blog_name = $core->blog->name;
$blog_desc = $core->blog->desc;
$blog_settings = $core->blog->settings;
$blog_url = $core->blog->url;
$action = $core->adminurl->get("admin.blog.pref");
$redir = $core->adminurl->get("admin.blog.pref");
} else {
dcPage::checkSuper();
try
{
if (empty($_REQUEST['id'])) {
throw new Exception(__('No given blog id.'));
}
$rs = $core->getBlog($_REQUEST['id']);
if (!$rs) {
throw new Exception(__('No such blog.'));
}
$blog_id = $rs->blog_id;
$blog_status = $rs->blog_status;
$blog_name = $rs->blog_name;
$blog_desc = $rs->blog_desc;
$blog_settings = new dcSettings($core, $blog_id);
$blog_url = $rs->blog_url;
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
$action = $core->adminurl->get("admin.blog");
$redir = $core->adminurl->get("admin.blog", ['id' => "%s"], '&', true);
}
# Language codes
$lang_combo = dcAdminCombos::getAdminLangsCombo();
# Status combo
$status_combo = dcAdminCombos::getBlogStatusescombo();
# Date format combo
$now = time();
$date_formats = $blog_settings->system->date_formats;
$time_formats = $blog_settings->system->time_formats;
$date_formats_combo = ['' => ''];
foreach ($date_formats as $format) {
$date_formats_combo[dt::str($format, $now)] = $format;
}
$time_formats_combo = ['' => ''];
foreach ($time_formats as $format) {
$time_formats_combo[dt::str($format, $now)] = $format;
}
# URL scan modes
$url_scan_combo = [
'PATH_INFO' => 'path_info',
'QUERY_STRING' => 'query_string'
];
# Post URL combo
$post_url_combo = [
__('year/month/day/title') => '{y}/{m}/{d}/{t}',
__('year/month/title') => '{y}/{m}/{t}',
__('year/title') => '{y}/{t}',
__('title') => '{t}',
__('post id/title') => '{id}/{t}',
__('post id') => '{id}'
];
if (!in_array($blog_settings->system->post_url_format, $post_url_combo)) {
$post_url_combo[html::escapeHTML($blog_settings->system->post_url_format)] = html::escapeHTML($blog_settings->system->post_url_format);
}
# Note title tag combo
$note_title_tag_combo = [
__('H4') => 0,
__('H3') => 1,
__('P') => 2
];
# Image title combo
$img_title_combo = [
__('(none)') => '',
__('Title') => 'Title ;; separator(, )',
__('Title, Date') => 'Title ;; Date(%b %Y) ;; separator(, )',
__('Title, Country, Date') => 'Title ;; Country ;; Date(%b %Y) ;; separator(, )',
__('Title, City, Country, Date') => 'Title ;; City ;; Country ;; Date(%b %Y) ;; separator(, )'
];
if (!in_array($blog_settings->system->media_img_title_pattern, $img_title_combo)) {
$img_title_combo[html::escapeHTML($blog_settings->system->media_img_title_pattern)] = html::escapeHTML($blog_settings->system->media_img_title_pattern);
}
# Image default size combo
$img_default_size_combo = [];
try {
$media = new dcMedia($core);
$img_default_size_combo[__('original')] = 'o';
foreach ($media->thumb_sizes as $code => $size) {
$img_default_size_combo[__($size[2])] = $code;
}
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
# Image default alignment combo
$img_default_alignment_combo = [
__('None') => 'none',
__('Left') => 'left',
__('Right') => 'right',
__('Center') => 'center'
];
# Image default legend and title combo
$img_default_legend_combo = [
__('Legend and title') => 'legend',
__('Title') => 'title',
__('None') => 'none'
];
# Robots policy options
$robots_policy_options = [
'INDEX,FOLLOW' => __("I would like search engines and archivers to index and archive my blog's content."),
'INDEX,FOLLOW,NOARCHIVE' => __("I would like search engines and archivers to index but not archive my blog's content."),
'NOINDEX,NOFOLLOW,NOARCHIVE' => __("I would like to prevent search engines and archivers from indexing or archiving my blog's content.")
];
# jQuery available versions
$jquery_root = dirname(__FILE__) . '/../inc/js/jquery';
$jquery_versions_combo = [__('Default') . ' (' . DC_DEFAULT_JQUERY . ')' => ''];
if (is_dir($jquery_root) && is_readable($jquery_root)) {
if (($d = @dir($jquery_root)) !== false) {
while (($entry = $d->read()) !== false) {
if ($entry != '.' && $entry != '..' && substr($entry, 0, 1) != '.' && is_dir($jquery_root . '/' . $entry)) {
if ($entry != DC_DEFAULT_JQUERY) {
$jquery_versions_combo[$entry] = $entry;
}
}
}
}
}
# Update a blog
if ($blog_id && !empty($_POST) && $core->auth->check('admin', $blog_id)) {
$cur = $core->con->openCursor($core->prefix . 'blog');
$cur->blog_id = $_POST['blog_id'];
$cur->blog_url = preg_replace('/\?+$/', '?', $_POST['blog_url']);
$cur->blog_name = $_POST['blog_name'];
$cur->blog_desc = $_POST['blog_desc'];
if ($core->auth->isSuperAdmin() && in_array($_POST['blog_status'], $status_combo)) {
$cur->blog_status = (int) $_POST['blog_status'];
}
$media_img_t_size = (integer) $_POST['media_img_t_size'];
if ($media_img_t_size < 0) {$media_img_t_size = 100;}
$media_img_s_size = (integer) $_POST['media_img_s_size'];
if ($media_img_s_size < 0) {$media_img_s_size = 240;}
$media_img_m_size = (integer) $_POST['media_img_m_size'];
if ($media_img_m_size < 0) {$media_img_m_size = 448;}
$media_video_width = (integer) $_POST['media_video_width'];
if ($media_video_width < 0) {$media_video_width = 400;}
$media_video_height = (integer) $_POST['media_video_height'];
if ($media_video_height < 0) {$media_video_height = 300;}
$nb_post_for_home = abs((integer) $_POST['nb_post_for_home']);
if ($nb_post_for_home < 1) {$nb_post_for_home = 1;}
$nb_post_per_page = abs((integer) $_POST['nb_post_per_page']);
if ($nb_post_per_page < 1) {$nb_post_per_page = 1;}
$nb_post_per_feed = abs((integer) $_POST['nb_post_per_feed']);
if ($nb_post_per_feed < 1) {$nb_post_per_feed = 1;}
$nb_comment_per_feed = abs((integer) $_POST['nb_comment_per_feed']);
if ($nb_comment_per_feed < 1) {$nb_comment_per_feed = 1;}
try
{
if ($cur->blog_id != null && $cur->blog_id != $blog_id) {
$rs = $core->getBlog($cur->blog_id);
if ($rs) {
throw new Exception(__('This blog ID is already used.'));
}
}
# --BEHAVIOR-- adminBeforeBlogUpdate
$core->callBehavior('adminBeforeBlogUpdate', $cur, $blog_id);
if (!preg_match('/^[a-z]{2}(-[a-z]{2})?$/', $_POST['lang'])) {
throw new Exception(__('Invalid language code'));
}
$core->updBlog($blog_id, $cur);
# --BEHAVIOR-- adminAfterBlogUpdate
$core->callBehavior('adminAfterBlogUpdate', $cur, $blog_id);
if ($cur->blog_id != null && $cur->blog_id != $blog_id) {
if ($blog_id == $core->blog->id) {
$core->setBlog($cur->blog_id);
$_SESSION['sess_blog_id'] = $cur->blog_id;
$blog_settings = $core->blog->settings;
} else {
$blog_settings = new dcSettings($core, $cur->blog_id);
}
$blog_id = $cur->blog_id;
}
$blog_settings->addNameSpace('system');
$blog_settings->system->put('editor', $_POST['editor']);
$blog_settings->system->put('copyright_notice', $_POST['copyright_notice']);
$blog_settings->system->put('post_url_format', $_POST['post_url_format']);
$blog_settings->system->put('lang', $_POST['lang']);
$blog_settings->system->put('blog_timezone', $_POST['blog_timezone']);
$blog_settings->system->put('date_format', $_POST['date_format']);
$blog_settings->system->put('time_format', $_POST['time_format']);
$blog_settings->system->put('comments_ttl', abs((integer) $_POST['comments_ttl']));
$blog_settings->system->put('trackbacks_ttl', abs((integer) $_POST['trackbacks_ttl']));
$blog_settings->system->put('allow_comments', !empty($_POST['allow_comments']));
$blog_settings->system->put('allow_trackbacks', !empty($_POST['allow_trackbacks']));
$blog_settings->system->put('comments_pub', empty($_POST['comments_pub']));
$blog_settings->system->put('trackbacks_pub', empty($_POST['trackbacks_pub']));
$blog_settings->system->put('comments_nofollow', !empty($_POST['comments_nofollow']));
$blog_settings->system->put('wiki_comments', !empty($_POST['wiki_comments']));
$blog_settings->system->put('comment_preview_optional', !empty($_POST['comment_preview_optional']));
$blog_settings->system->put('enable_xmlrpc', !empty($_POST['enable_xmlrpc']));
$blog_settings->system->put('note_title_tag', $_POST['note_title_tag']);
$blog_settings->system->put('nb_post_for_home', $nb_post_for_home);
$blog_settings->system->put('nb_post_per_page', $nb_post_per_page);
$blog_settings->system->put('use_smilies', !empty($_POST['use_smilies']));
$blog_settings->system->put('no_search', !empty($_POST['no_search']));
$blog_settings->system->put('inc_subcats', !empty($_POST['inc_subcats']));
$blog_settings->system->put('media_img_t_size', $media_img_t_size);
$blog_settings->system->put('media_img_s_size', $media_img_s_size);
$blog_settings->system->put('media_img_m_size', $media_img_m_size);
$blog_settings->system->put('media_video_width', $media_video_width);
$blog_settings->system->put('media_video_height', $media_video_height);
$blog_settings->system->put('media_img_title_pattern', $_POST['media_img_title_pattern']);
$blog_settings->system->put('media_img_use_dto_first', !empty($_POST['media_img_use_dto_first']));
$blog_settings->system->put('media_img_no_date_alone', !empty($_POST['media_img_no_date_alone']));
$blog_settings->system->put('media_img_default_size', $_POST['media_img_default_size']);
$blog_settings->system->put('media_img_default_alignment', $_POST['media_img_default_alignment']);
$blog_settings->system->put('media_img_default_link', !empty($_POST['media_img_default_link']));
$blog_settings->system->put('media_img_default_legend', $_POST['media_img_default_legend']);
$blog_settings->system->put('nb_post_per_feed', $nb_post_per_feed);
$blog_settings->system->put('nb_comment_per_feed', $nb_comment_per_feed);
$blog_settings->system->put('short_feed_items', !empty($_POST['short_feed_items']));
if (isset($_POST['robots_policy'])) {
$blog_settings->system->put('robots_policy', $_POST['robots_policy']);
}
$blog_settings->system->put('jquery_needed', !empty($_POST['jquery_needed']));
$blog_settings->system->put('jquery_version', $_POST['jquery_version']);
$blog_settings->system->put('prevents_clickjacking', !empty($_POST['prevents_clickjacking']));
$blog_settings->system->put('static_home', !empty($_POST['static_home']));
$blog_settings->system->put('static_home_url', $_POST['static_home_url']);
# --BEHAVIOR-- adminBeforeBlogSettingsUpdate
$core->callBehavior('adminBeforeBlogSettingsUpdate', $blog_settings);
if ($core->auth->isSuperAdmin() && in_array($_POST['url_scan'], $url_scan_combo)) {
$blog_settings->system->put('url_scan', $_POST['url_scan']);
}
dcPage::addSuccessNotice(__('Blog has been successfully updated.'));
http::redirect(sprintf($redir, $blog_id));
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
// Display
if ($standalone) {
$breadcrumb = dcPage::breadcrumb(
[
html::escapeHTML($blog_name) => '',
__('Blog settings') => ''
]
);
} else {
$breadcrumb = dcPage::breadcrumb(
[
__('System') => '',
__('Blogs') => $core->adminurl->get("admin.blogs"),
__('Blog settings') . ' : ' . html::escapeHTML($blog_name) => ''
]);
}
$desc_editor = $core->auth->getOption('editor');
$rte_flag = true;
$rte_flags = @$core->auth->user_prefs->interface->rte_flags;
if (is_array($rte_flags) && in_array('blog_descr', $rte_flags)) {
$rte_flag = $rte_flags['blog_descr'];
}
dcPage::open(__('Blog settings'),
dcPage::jsJson('blog_pref', [
'warning_path_info' => __('Warning: except for special configurations, it is generally advised to have a trailing "/" in your blog URL in PATH_INFO mode.'),
'warning_query_string' => __('Warning: except for special configurations, it is generally advised to have a trailing "?" in your blog URL in QUERY_STRING mode.')
]) .
dcPage::jsConfirmClose('blog-form') .
($rte_flag ? $core->callBehavior('adminPostEditor', $desc_editor['xhtml'], 'blog_desc', ['#blog_desc'], 'xhtml') : '') .
dcPage::jsLoad('js/_blog_pref.js') .
# --BEHAVIOR-- adminBlogPreferencesHeaders
$core->callBehavior('adminBlogPreferencesHeaders') .
dcPage::jsPageTabs(),
$breadcrumb
);
if ($blog_id) {
if (!empty($_GET['add'])) {
dcPage::success(__('Blog has been successfully created.'));
}
if (!empty($_GET['upd'])) {
dcPage::success(__('Blog has been successfully updated.'));
}
echo
'<div class="multi-part" id="params" title="' . __('Parameters') . '">' .
'<div id="standard-pref"><h3>' . __('Blog parameters') . '</h3>' .
'<form action="' . $action . '" method="post" id="blog-form">';
echo
'<div class="fieldset"><h4>' . __('Blog details') . '</h4>' .
$core->formNonce();
echo
'<p><label for="blog_name" class="required"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog name:') . '</label>' .
form::field('blog_name', 30, 255,
[
'default' => html::escapeHTML($blog_name),
'extra_html' => 'required placeholder="' . __('Blog name') . ' lang="' . $blog_settings->system->lang .
'" spellcheck="true"'
]
) . '</p>';
echo
'<p class="area"><label for="blog_desc">' . __('Blog description:') . '</label>' .
form::textarea('blog_desc', 60, 5,
[
'default' => html::escapeHTML($blog_desc),
'extra_html' => 'lang="' . $blog_settings->system->lang . '" spellcheck="true"'
]) . '</p>';
if ($core->auth->isSuperAdmin()) {
echo
'<p><label for="blog_status">' . __('Blog status:') . '</label>' .
form::combo('blog_status', $status_combo, $blog_status) . '</p>';
} else {
/*
Only super admins can change the blog ID and URL, but we need to pass
their values to the POST request via hidden html input values so as
to allow admins to update other settings.
Otherwise dcCore::getBlogCursor() throws an exception.
*/
echo
form::hidden('blog_id', html::escapeHTML($blog_id)) .
form::hidden('blog_url', html::escapeHTML($blog_url));
}
echo '</div>';
echo
'<div class="fieldset"><h4>' . __('Blog configuration') . '</h4>' .
'<p><label for="editor">' . __('Blog editor name:') . '</label>' .
form::field('editor', 30, 255, html::escapeHTML($blog_settings->system->editor)) .
'</p>' .
'<p><label for="lang">' . __('Default language:') . '</label>' .
form::combo('lang', $lang_combo, $blog_settings->system->lang, 'l10n') .
'</p>' .
'<p><label for="blog_timezone">' . __('Blog timezone:') . '</label>' .
form::combo('blog_timezone', dt::getZones(true, true), html::escapeHTML($blog_settings->system->blog_timezone)) .
'</p>' .
'<p><label for="copyright_notice">' . __('Copyright notice:') . '</label>' .
form::field('copyright_notice', 30, 255,
[
'default' => html::escapeHTML($blog_settings->system->copyright_notice),
'extra_html' => 'lang="' . $blog_settings->system->lang . '" spellcheck="true"'
]) .
'</p>' .
'</div>';
echo
'<div class="fieldset"><h4>' . __('Comments and trackbacks') . '</h4>' .
'<div class="two-cols">' .
'<div class="col">' .
'<p><label for="allow_comments" class="classic">' .
form::checkbox('allow_comments', '1', $blog_settings->system->allow_comments) .
__('Accept comments') . '</label></p>' .
'<p><label for="comments_pub" class="classic">' .
form::checkbox('comments_pub', '1', !$blog_settings->system->comments_pub) .
__('Moderate comments') . '</label></p>' .
'<p><label for="comments_ttl" class="classic">' . sprintf(__('Leave comments open for %s days') . '.',
form::number('comments_ttl', [
'min' => 0,
'max' => 999,
'default' => $blog_settings->system->comments_ttl]
)) .
'</label></p>' .
'<p class="form-note">' . __('No limit: leave blank.') . '</p>' .
'<p><label for="wiki_comments" class="classic">' .
form::checkbox('wiki_comments', '1', $blog_settings->system->wiki_comments) .
__('Wiki syntax for comments') . '</label></p>' .
'<p><label for="comment_preview_optional" class="classic">' .
form::checkbox('comment_preview_optional', '1', $blog_settings->system->comment_preview_optional) .
__('Preview of comment before submit is not mandatory') . '</label></p>' .
'</div>' .
'<div class="col">' .
'<p><label for="allow_trackbacks" class="classic">' .
form::checkbox('allow_trackbacks', '1', $blog_settings->system->allow_trackbacks) .
__('Accept trackbacks') . '</label></p>' .
'<p><label for="trackbacks_pub" class="classic">' .
form::checkbox('trackbacks_pub', '1', !$blog_settings->system->trackbacks_pub) .
__('Moderate trackbacks') . '</label></p>' .
'<p><label for="trackbacks_ttl" class="classic">' . sprintf(__('Leave trackbacks open for %s days') . '.',
form::number('trackbacks_ttl', [
'min' => 0,
'max' => 999,
'default' => $blog_settings->system->trackbacks_ttl]
)) .
'</label></p>' .
'<p class="form-note">' . __('No limit: leave blank.') . '</p>' .
'<p><label for="comments_nofollow" class="classic">' .
form::checkbox('comments_nofollow', '1', $blog_settings->system->comments_nofollow) .
__('Add "nofollow" relation on comments and trackbacks links') . '</label></p>' .
'</div>' .
'<br class="clear" />' . //Opera sucks
'</div>' .
'<br class="clear" />' . //Opera sucks
'</div>';
echo
'<div class="fieldset"><h4>' . __('Blog presentation') . '</h4>' .
'<div class="two-cols">' .
'<div class="col">' .
'<p><label for="date_format">' . __('Date format:') . '</label> ' .
form::field('date_format', 30, 255, html::escapeHTML($blog_settings->system->date_format)) .
form::combo('date_format_select', $date_formats_combo, ['extra_html' => 'title="' . __('Pattern of date') . '"']) .
'</p>' .
'<p class="chosen form-note">' . __('Sample:') . ' ' . dt::str(html::escapeHTML($blog_settings->system->date_format)) . '</p>' .
'<p><label for="time_format">' . __('Time format:') . '</label>' .
form::field('time_format', 30, 255, html::escapeHTML($blog_settings->system->time_format)) .
form::combo('time_format_select', $time_formats_combo, ['extra_html' => 'title="' . __('Pattern of time') . '"']) .
'</p>' .
'<p class="chosen form-note">' . __('Sample:') . ' ' . dt::str(html::escapeHTML($blog_settings->system->time_format)) . '</p>' .
'<p><label for="use_smilies" class="classic">' .
form::checkbox('use_smilies', '1', $blog_settings->system->use_smilies) .
__('Display smilies on entries and comments') . '</label></p>' .
'<p><label for="no_search" class="classic">' .
form::checkbox('no_search', '1', $blog_settings->system->no_search) .
__('Disable internal search system') . '</label></p>' .
'</div>' .
'<div class="col">' .
'<p><label for="nb_post_for_home" class="classic">' . sprintf(__('Display %s entries on first page'),
form::number('nb_post_for_home', [
'min' => 1,
'max' => 999,
'default' => $blog_settings->system->nb_post_for_home]
)) .
'</label></p>' .
'<p><label for="nb_post_per_page" class="classic">' . sprintf(__('Display %s entries per page'),
form::number('nb_post_per_page', [
'min' => 1,
'max' => 999,
'default' => $blog_settings->system->nb_post_per_page]
)) .
'</label></p>' .
'<p><label for="nb_post_per_feed" class="classic">' . sprintf(__('Display %s entries per feed'),
form::number('nb_post_per_feed', [
'min' => 1,
'max' => 999,
'default' => $blog_settings->system->nb_post_per_feed]
)) .
'</label></p>' .
'<p><label for="nb_comment_per_feed" class="classic">' . sprintf(__('Display %s comments per feed'),
form::number('nb_comment_per_feed', [
'min' => 1,
'max' => 999,
'default' => $blog_settings->system->nb_comment_per_feed]
)) .
'</label></p>' .
'<p><label for="short_feed_items" class="classic">' .
form::checkbox('short_feed_items', '1', $blog_settings->system->short_feed_items) .
__('Truncate feeds') . '</label></p>' .
'<p><label for="inc_subcats" class="classic">' .
form::checkbox('inc_subcats', '1', $blog_settings->system->inc_subcats) .
__('Include sub-categories in category page and category posts feed') . '</label></p>' .
'</div>' .
'</div>' .
'<br class="clear" />' . //Opera sucks
'<hr />' .
'<p><label for="static_home" class="classic">' .
form::checkbox('static_home', '1', $blog_settings->system->static_home) .
__('Display an entry as static home page') . '</label></p>' .
'<p><label for="static_home_url" class="classic">' . __('Entry URL (its content will be used for the static home page):') . '</label> ' .
form::field('static_home_url', 30, 255, html::escapeHTML($blog_settings->system->static_home_url)) .
' <button type="button" id="static_home_url_selector">' . __('Choose an entry') . '</button>' .
'</p>' .
'<p class="form-note">' . __('Leave empty to use the default presentation.') . '</p> ' .
'</div>';
echo
'<div class="fieldset"><h4 id="medias-settings">' . __('Media and images') . '</h4>' .
'<p class="form-note warning">' .
__('Please note that if you change current settings bellow, they will now apply to all new images in the media manager.') .
' ' . __('Be carefull if you share it with other blogs in your installation.') . '<br />' .
__('Set -1 to use the default size, set 0 to ignore this thumbnail size (images only).') . '</p>' .
'<div class="two-cols">' .
'<div class="col">' .
'<h5>' . __('Generated image sizes (max dimension in pixels)') . '</h5>' .
'<p class="field"><label for="media_img_t_size">' . __('Thumbnail') . '</label> ' .
form::number('media_img_t_size', [
'min' => -1,
'max' => 999,
'default' => $blog_settings->system->media_img_t_size
]) .
'</p>' .
'<p class="field"><label for="media_img_s_size">' . __('Small') . '</label> ' .
form::number('media_img_s_size', [
'min' => -1,
'max' => 999,
'default' => $blog_settings->system->media_img_s_size
]) .
'</p>' .
'<p class="field"><label for="media_img_m_size">' . __('Medium') . '</label> ' .
form::number('media_img_m_size', [
'min' => -1,
'max' => 999,
'default' => $blog_settings->system->media_img_m_size
]) .
'</p>' .
'<h5>' . __('Default size of the inserted video (in pixels)') . '</h5>' .
'<p class="field"><label for="media_video_width">' . __('Width') . '</label> ' .
form::number('media_video_width', [
'min' => -1,
'max' => 999,
'default' => $blog_settings->system->media_video_width
]) .
'</p>' .
'<p class="field"><label for="media_video_height">' . __('Height') . '</label> ' .
form::number('media_video_height', [
'min' => -1,
'max' => 999,
'default' => $blog_settings->system->media_video_height
]) .
'</p>' .
'</div>' .
'<div class="col">' .
'<h5>' . __('Default image insertion attributes') . '</h5>' .
'<p class="vertical-separator"><label for="media_img_title_pattern">' . __('Inserted image title') . '</label>' .
form::combo('media_img_title_pattern', $img_title_combo, html::escapeHTML($blog_settings->system->media_img_title_pattern)) . '</p>' .
'<p><label for="media_img_use_dto_first" class="classic">' .
form::checkbox('media_img_use_dto_first', '1', $blog_settings->system->media_img_use_dto_first) .
__('Use original media date if possible') . '</label></p>' .
'<p><label for="media_img_no_date_alone" class="classic">' .
form::checkbox('media_img_no_date_alone', '1', $blog_settings->system->media_img_no_date_alone) .
__('Do not display date if alone in title') . '</label></p>' .
'<p class="form-note info">' . __('It is retrieved from the picture\'s metadata.') . '</p>' .
'<p class="field vertical-separator"><label for="media_img_default_size">' . __('Size of inserted image:') . '</label>' .
form::combo('media_img_default_size', $img_default_size_combo,
(html::escapeHTML($blog_settings->system->media_img_default_size) != '' ? html::escapeHTML($blog_settings->system->media_img_default_size) : 'm')) .
'</p>' .
'<p class="field"><label for="media_img_default_alignment">' . __('Image alignment:') . '</label>' .
form::combo('media_img_default_alignment', $img_default_alignment_combo, html::escapeHTML($blog_settings->system->media_img_default_alignment)) .
'</p>' .
'<p><label for="media_img_default_link">' .
form::checkbox('media_img_default_link', '1', $blog_settings->system->media_img_default_link) .
__('Insert a link to the original image') . '</label></p>' .
'<p class="field"><label for="media_img_default_legend">' . __('Image legend and title:') . '</label>' .
form::combo('media_img_default_legend', $img_default_legend_combo, html::escapeHTML($blog_settings->system->media_img_default_legend)) .
'</p>' .
'</div>' .
'</div>' .
'<br class="clear" />' . //Opera sucks
'</div>' .
'</div>';
echo '<div id="advanced-pref"><h3>' . __('Advanced parameters') . '</h3>';
if ($core->auth->isSuperAdmin()) {
echo '<div class="fieldset"><h4>' . __('Blog details') . '</h4>';
echo
'<p><label for="blog_id" class="required"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog ID:') . '</label>' .
form::field('blog_id', 30, 32, html::escapeHTML($blog_id), '', '', false, 'required placeholder="' . __('Blog ID') . '"') . '</p>' .
'<p class="form-note">' . __('At least 2 characters using letters, numbers or symbols.') . '</p> ' .
'<p class="form-note warn">' . __('Please note that changing your blog ID may require changes in your public index.php file.') . '</p>';
echo
'<p><label for="blog_url" class="required"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Blog URL:') . '</label>' .
form::url('blog_url', [
'size' => 50,
'max' => 255,
'default' => html::escapeHTML($blog_url),
'extra_html' => 'required placeholder="' . __('Blog URL') . '"'
]) .
'</p>' .
'<p><label for="url_scan">' . __('URL scan method:') . '</label>' .
form::combo('url_scan', $url_scan_combo, $blog_settings->system->url_scan) . '</p>';
try
{
# Test URL of blog by testing it's ATOM feed
$file = $blog_url . $core->url->getURLFor('feed', 'atom');
$path = '';
$status = '404';
$content = '';
$client = netHttp::initClient($file, $path);
if ($client !== false) {
$client->setTimeout(4);
$client->setUserAgent($_SERVER['HTTP_USER_AGENT']);
$client->get($path);
$status = $client->getStatus();
$content = $client->getContent();
}
if ($status != '200') {
// Might be 404 (URL not found), 670 (blog not online), ...
echo
'<p class="form-note warn">' .
sprintf(__('The URL of blog or the URL scan method might not be well set (<code>%s</code> return a <strong>%s</strong> status).'),
html::escapeHTML($file), $status) .
'</p>';
} else {
if (substr($content, 0, 6) != '<?xml ') {
// Not well formed XML feed
echo
'<p class="form-note warn">' .
sprintf(__('The URL of blog or the URL scan method might not be well set (<code>%s</code> does not return an ATOM feed).'),
html::escapeHTML($file)) .
'</p>';
}
}
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
echo '</div>';
}
echo
'<div class="fieldset"><h4>' . __('Blog configuration') . '</h4>' .
'<p><label for="post_url_format">' . __('New post URL format:') . '</label>' .
form::combo('post_url_format', $post_url_combo, html::escapeHTML($blog_settings->system->post_url_format)) .
'</p>' .
'<p class="chosen form-note">' . __('Sample:') . ' ' . $core->blog->getPostURL('', date('Y-m-d H:i:00', $now), __('Dotclear'), 42) . '</p>' .
'</p>' .
'<p><label for="note_title_tag">' . __('HTML tag for the title of the notes on the blog:') . '</label>' .
form::combo('note_title_tag', $note_title_tag_combo, $blog_settings->system->note_title_tag) .
'</p>' .
'<p><label for="enable_xmlrpc" class="classic">' .
form::checkbox('enable_xmlrpc', '1', $blog_settings->system->enable_xmlrpc) .
__('Enable XML/RPC interface') . '</label>' . '</p>' .
'<p class="form-note info">' . __('XML/RPC interface allows you to edit your blog with an external client.') . '</p>';
if ($blog_settings->system->enable_xmlrpc) {
echo
'<p>' . __('XML/RPC interface is active. You should set the following parameters on your XML/RPC client:') . '</p>' .
'<ul>' .
'<li>' . __('Server URL:') . ' <strong><code>' .
sprintf(DC_XMLRPC_URL, $core->blog->url, $core->blog->id) .
'</code></strong></li>' .
'<li>' . __('Blogging system:') . ' <strong><code>Movable Type</code></strong></li>' .
'<li>' . __('User name:') . ' <strong><code>' . $core->auth->userID() . '</code></strong></li>' .
'<li>' . __('Password:') . ' <strong><code>&lt;' . __('your password') . '&gt;</code></strong></li>' .
'<li>' . __('Blog ID:') . ' <strong><code>1</code></strong></li>' .
'</ul>';
}
echo
'</div>';
// Search engines policies
echo '<div class="fieldset"><h4>' . __('Search engines robots policy') . '</h4>';
$i = 0;
foreach ($robots_policy_options as $k => $v) {
echo '<p><label for="robots_policy-' . $i . '" class="classic">' .
form::radio(['robots_policy', 'robots_policy-' . $i], $k, $blog_settings->system->robots_policy == $k) . ' ' . $v . '</label></p>';
$i++;
}
echo '</div>';
echo '<div class="fieldset"><h4>' . __('jQuery javascript library') . '</h4>' .
'<p><label for="jquery_needed" class="classic">' .
form::checkbox('jquery_needed', '1', $blog_settings->system->jquery_needed) .
__('Load the jQuery library') . '</label></p>' .
'<p><label for="jquery_version" class="classic">' . __('jQuery version to be loaded for this blog:') . '</label>' . ' ' .
form::combo('jquery_version', $jquery_versions_combo, $blog_settings->system->jquery_version) .
'</p>' .
'<br class="clear" />' . //Opera sucks
'</div>';
echo '<div class="fieldset"><h4>' . __('Blog security') . '</h4>' .
'<p><label for="prevents_clickjacking" class="classic">' .
form::checkbox('prevents_clickjacking', '1', $blog_settings->system->prevents_clickjacking) .
__('Protect the blog from Clickjacking (see <a href="https://en.wikipedia.org/wiki/Clickjacking">Wikipedia</a>)') . '</label></p>' .
'<br class="clear" />' . //Opera sucks
'</div>';
echo '</div>'; // End advanced
echo '<div id="plugins-pref"><h3>' . __('Plugins parameters') . '</h3>';
# --BEHAVIOR-- adminBlogPreferencesForm
$core->callBehavior('adminBlogPreferencesForm', $core, $blog_settings);
echo '</div>'; // End 3rd party, aka plugins
echo
'<p><input type="submit" accesskey="s" value="' . __('Save') . '" />' .
(!$standalone ? form::hidden('id', $blog_id) : '') .
'</p>' .
'</form>';
if ($core->auth->isSuperAdmin() && $blog_id != $core->blog->id) {
echo
'<form action="' . $core->adminurl->get("admin.blog.del") . '" method="post">' .
'<p><input type="submit" class="delete" value="' . __('Delete this blog') . '" />' .
form::hidden(['blog_id'], $blog_id) .
$core->formNonce() . '</p>' .
'</form>';
} else {
if ($blog_id == $core->blog->id) {
echo '<p class="message">' . __('The current blog cannot be deleted.') . '</p>';
} else {
echo '<p class="message">' . __('Only superadmin can delete a blog.') . '</p>';
}
}
echo '</div>';
#
# Users on the blog (with permissions)
$blog_users = $core->getBlogPermissions($blog_id, $core->auth->isSuperAdmin());
$perm_types = $core->auth->getPermissionsTypes();
echo
'<div class="multi-part" id="users" title="' . __('Users') . '">' .
'<h3 class="out-of-screen-if-js">' . __('Users on this blog') . '</h3>';
if (empty($blog_users)) {
echo '<p>' . __('No users') . '</p>';
} else {
if ($core->auth->isSuperAdmin()) {
$user_url_p = '<a href="' . $core->adminurl->get("admin.user", ['id' => '%1$s'], '&amp;', true) . '">%1$s</a>';
} else {
$user_url_p = '%1$s';
}
# Sort users list on user_id key
dcUtils::lexicalKeySort($blog_users);
$post_type = $core->getPostTypes();
$current_blog_id = $core->blog->id;
if ($blog_id != $core->blog->id) {
$core->setBlog($blog_id);
}
echo '<div>';
foreach ($blog_users as $k => $v) {
if (count($v['p']) > 0) {
echo
'<div class="user-perm' . ($v['super'] ? ' user_super' : '') . '">' .
'<h4>' . sprintf($user_url_p, html::escapeHTML($k)) .
' (' . html::escapeHTML(dcUtils::getUserCN(
$k, $v['name'], $v['firstname'], $v['displayname']
)) . ')</h4>';
if ($core->auth->isSuperAdmin()) {
echo
'<p>' . __('Email:') . ' ' .
($v['email'] != '' ? '<a href="mailto:' . $v['email'] . '">' . $v['email'] . '</a>' : __('(none)')) .
'</p>';
}
echo
'<h5>' . __('Publications on this blog:') . '</h5>' .
'<ul>';
foreach ($post_type as $type => $pt_info) {
$params = [
'post_type' => $type,
'user_id' => $k
];
echo '<li>' . sprintf(__('%1$s: %2$s'), __($pt_info['label']), $core->blog->getPosts($params, true)->f(0)) . '</li>';
}
echo
'</ul>';
echo
'<h5>' . __('Permissions:') . '</h5>' .
'<ul>';
if ($v['super']) {
echo '<li class="user_super">' . __('Super administrator') . '<br />' .
'<span class="form-note">' . __('All rights on all blogs.') . '</span></li>';
} else {
foreach ($v['p'] as $p => $V) {
if (isset($perm_types[$p])) {
echo '<li ' . ($p == 'admin' ? 'class="user_admin"' : '') . '>' . __($perm_types[$p]);
} else {
echo '<li>' . sprintf(__('[%s] (unreferenced permission)'), $p);
}
if ($p == 'admin') {
echo '<br /><span class="form-note">' . __('All rights on this blog.') . '</span>';
}
echo '</li>';
}
}
echo
'</ul>';
if (!$v['super'] && $core->auth->isSuperAdmin()) {
echo
'<form action="' . $core->adminurl->get('admin.user.actions') . '" method="post">' .
'<p class="change-user-perm"><input type="submit" class="reset" value="' . __('Change permissions') . '" />' .
form::hidden(['redir'], $core->adminurl->get("admin.blog.pref", ['id' => $k], '&')) .
form::hidden(['action'], 'perms') .
form::hidden(['users[]'], $k) .
form::hidden(['blogs[]'], $blog_id) .
$core->formNonce() .
'</p>' .
'</form>';
}
echo '</div>';
}
}
echo '</div>';
if ($current_blog_id != $core->blog->id) {
$core->setBlog($current_blog_id);
}
}
echo '</div>';
}
dcPage::helpBlock('core_blog_pref');
dcPage::close();

View File

@ -0,0 +1,250 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('admin');
# -- Loading themes --
$core->themes = new dcThemes($core);
$core->themes->loadModules($core->blog->themes_path, null);
# -- Page helper --
$list = new adminThemesList(
$core->themes,
$core->blog->themes_path,
$core->blog->settings->system->store_theme_url,
!empty($_GET['nocache'])
);
adminThemesList::$distributed_modules = explode(',', DC_DISTRIB_THEMES);
# -- Theme screenshot --
if (!empty($_GET['shot']) && $list->modules->moduleExists($_GET['shot'])) {
$f = path::real(empty($_GET['src']) ?
$core->blog->themes_path . '/' . $_GET['shot'] . '/screenshot.jpg' :
$core->blog->themes_path . '/' . $_GET['shot'] . '/' . path::clean($_GET['src'])
);
if (!file_exists($f)) {
$f = dirname(__FILE__) . '/images/noscreenshot.png';
}
http::cache(array_merge([$f], get_included_files()));
header('Content-Type: ' . files::getMimeType($f));
header('Content-Length: ' . filesize($f));
readfile($f);
exit;
}
# -- Display module configuration page --
if ($list->setConfiguration($core->blog->settings->system->theme)) {
# Get content before page headers
include $list->includeConfiguration();
# Gather content
$list->getConfiguration();
# Display page
dcPage::open(__('Blog appearance'),
dcPage::jsPageTabs() .
# --BEHAVIOR-- themesToolsHeaders
$core->callBehavior('themesToolsHeaders', $core, true),
dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
__('Blog appearance') => $list->getURL('', false),
'<span class="page-title">' . __('Theme configuration') . '</span>' => ''
])
);
# Display previously gathered content
$list->displayConfiguration();
dcPage::helpBlock('core_blog_theme_conf');
dcPage::close();
# Stop reading code here
return;
}
# -- Execute actions --
try {
$list->doActions();
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
# -- Page header --
dcPage::open(__('Themes management'),
dcPage::jsLoad('js/_blog_theme.js') .
dcPage::jsPageTabs() .
# --BEHAVIOR-- themesToolsHeaders
$core->callBehavior('themesToolsHeaders', $core, false),
dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
'<span class="page-title">' . __('Blog appearance') . '</span>' => ''
])
);
# -- Display modules lists --
if ($core->auth->isSuperAdmin()) {
if (!$core->error->flag()) {
if (!empty($_GET['nocache'])) {
dcPage::success(__('Manual checking of themes update done successfully.'));
}
}
# Updated modules from repo
$modules = $list->store->get(true);
if (!empty($modules)) {
echo
'<div class="multi-part" id="update" title="' . html::escapeHTML(__('Update themes')) . '">' .
'<h3>' . html::escapeHTML(__('Update themes')) . '</h3>' .
'<p>' . sprintf(
__('There is one theme to update available from repository.', 'There are %s themes to update available from repository.', count($modules)),
count($modules)
) . '</p>';
$list
->setList('theme-update')
->setTab('themes')
->setModules($modules)
->displayModules(
/*cols */['checkbox', 'name', 'sshot', 'desc', 'author', 'version', 'current_version', 'parent'],
/* actions */['update', 'delete']
);
echo
'<p class="info vertical-separator">' . sprintf(
__("Visit %s repository, the resources center for Dotclear."),
'<a href="https://themes.dotaddict.org/galerie-dc2/">Dotaddict</a>'
) .
'</p>' .
'</div>';
} else {
echo
'<form action="' . $list->getURL('', false) . '" method="get">' .
'<p><input type="hidden" name="nocache" value="1" />' .
'<input type="submit" value="' . __('Force checking update of themes') . '" /></p>' .
'</form>';
}
}
# Activated modules
$modules = $list->modules->getModules();
if (!empty($modules)) {
echo
'<div class="multi-part" id="themes" title="' . __('Installed themes') . '">' .
'<h3>' . __('Installed themes') . '</h3>' .
'<p class="more-info">' . __('You can configure and manage installed themes from this list.') . '</p>';
$list
->setList('theme-activate')
->setTab('themes')
->setModules($modules)
->displayModules(
/* cols */['sshot', 'distrib', 'name', 'config', 'desc', 'author', 'version', 'parent'],
/* actions */['select', 'behavior', 'deactivate', 'delete']
);
echo
'</div>';
}
# Deactivated modules
$modules = $list->modules->getDisabledModules();
if (!empty($modules)) {
echo
'<div class="multi-part" id="deactivate" title="' . __('Deactivated themes') . '">' .
'<h3>' . __('Deactivated themes') . '</h3>' .
'<p class="more-info">' . __('Deactivated themes are installed but not usable. You can activate them from here.') . '</p>';
$list
->setList('theme-deactivate')
->setTab('themes')
->setModules($modules)
->displayModules(
/* cols */['name', 'distrib'],
/* actions */['activate', 'delete']
);
echo
'</div>';
}
if ($core->auth->isSuperAdmin() && $list->isWritablePath()) {
# New modules from repo
$search = $list->getSearch();
$modules = $search ? $list->store->search($search) : $list->store->get();
if (!empty($search) || !empty($modules)) {
echo
'<div class="multi-part" id="new" title="' . __('Add themes') . '">' .
'<h3>' . __('Add themes from repository') . '</h3>';
// '<p>'.__('Search and install themes directly from repository.').'</p>';
$list
->setList('theme-new')
->setTab('new')
->setModules($modules)
->displaySearch()
->displayIndex()
->displayModules(
/* cols */['expander', 'sshot', 'name', 'score', 'config', 'desc', 'author', 'version', 'parent', 'details', 'support'],
/* actions */['install'],
/* nav limit */true
);
echo
'<p class="info vertical-separator">' . sprintf(
__("Visit %s repository, the resources center for Dotclear."),
'<a href="https://themes.dotaddict.org/galerie-dc2/">Dotaddict</a>'
) .
'</p>' .
'</div>';
}
# Add a new plugin
echo
'<div class="multi-part" id="addtheme" title="' . __('Install or upgrade manually') . '">' .
'<h3>' . __('Add themes from a package') . '</h3>' .
'<p class="more-info">' . __('You can install themes by uploading or downloading zip files.') . '</p>';
$list->displayManualForm();
echo
'</div>';
}
# --BEHAVIOR-- themesToolsTabs
$core->callBehavior('themesToolsTabs', $core);
# -- Notice for super admin --
if ($core->auth->isSuperAdmin() && !$list->isWritablePath()) {
echo
'<p class="warning">' . __('Some functions are disabled, please give write access to your themes directory to enable them.') . '</p>';
}
dcPage::helpBlock('core_blog_theme');
dcPage::close();

View File

@ -0,0 +1,185 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
# Filters
$status_combo = array_merge(
['-' => ''],
dcAdminCombos::getBlogStatusesCombo()
);
$sortby_combo = [
__('Last update') => 'blog_upddt',
__('Blog name') => 'UPPER(blog_name)',
__('Blog ID') => 'B.blog_id',
__('Status') => 'blog_status'
];
$order_combo = [
__('Descending') => 'desc',
__('Ascending') => 'asc'
];
# Actions
if ($core->auth->isSuperAdmin()) {
$blogs_actions_page = new dcBlogsActionsPage($core, $core->adminurl->get("admin.blogs"));
if ($blogs_actions_page->process()) {
return;
}
}
# Requests
$q = !empty($_GET['q']) ? $_GET['q'] : '';
$status = isset($_GET['status']) ? $_GET['status'] : '';
$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'blog_upddt';
$order = !empty($_GET['order']) ? $_GET['order'] : 'desc';
$show_filters = false;
$page = !empty($_GET['page']) ? max(1, (integer) $_GET['page']) : 1;
$nb_per_page = 30;
if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) {
if ($nb_per_page != (integer) $_GET['nb']) {
$show_filters = true;
}
$nb_per_page = (integer) $_GET['nb'];
}
# - Search filter
if ($q) {
$params['q'] = $q;
$show_filters = true;
}
# - Status filter
if ($status !== '' && in_array($status, $status_combo, true)) {
$params['blog_status'] = $status;
$show_filters = true;
} else {
$status = '';
}
# - Sortby and order filter
if ($sortby !== '' && in_array($sortby, $sortby_combo, true)) {
if ($order !== '' && in_array($order, $order_combo, true)) {
$params['order'] = $sortby . ' ' . $order;
} else {
$order = 'desc';
}
} else {
$sortby = 'blog_upddt';
$order = 'desc';
}
if ($sortby != 'blog_upddt' || $order != 'desc') {
$show_filters = true;
}
$params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
try {
$counter = $core->getBlogs($params, 1);
$rs = $core->getBlogs($params);
$nb_blog = $counter->f(0);
$rsStatic = $rs->toStatic();
if (($sortby != 'blog_upddt') && ($sortby != 'blog_status')) {
// Sort blog list using lexical order if necessary
$rsStatic->extend('rsExtUser');
$rsStatic = $rsStatic->toExtStatic();
$rsStatic->lexicalSort(($sortby == 'UPPER(blog_name)' ? 'blog_name' : 'blog_id'), $order);
}
$blog_list = new adminBlogList($core, $rs, $counter->f(0));
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
/* DISPLAY
-------------------------------------------------------- */
dcPage::open(__('List of blogs'),
dcPage::jsLoad('js/_blogs.js') . dcPage::jsFilterControl($show_filters),
dcPage::breadcrumb(
[
__('System') => '',
__('List of blogs') => ''
])
);
if (!$core->error->flag()) {
if ($core->auth->isSuperAdmin()) {
echo '<p class="top-add"><a class="button add" href="' . $core->adminurl->get("admin.blog") . '">' . __('Create a new blog') . '</a></p>';
}
echo
'<form action="' . $core->adminurl->get("admin.blogs") . '" method="get" id="filters-form">' .
'<h3 class="out-of-screen-if-js">' . __('Show filters and display options') . '</h3>' .
'<div class="table">' .
'<div class="cell">' .
'<h4>' . __('Filters') . '</h4>' .
'<p><label for="q" class="ib">' . __('Search:') . '</label> ' .
form::field('q', 20, 255, html::escapeHTML($q)) . '</p>' .
($core->auth->isSuperAdmin() ?
'<p><label for="status" class="ib">' . __('Status:') . '</label> ' .
form::combo('status', $status_combo, $status) . '</p>' : '') .
'</div>' .
'<div class="cell filters-options">' .
'<h4>' . __('Display options') . '</h4>' .
'<p><label for="sortby" class="ib">' . __('Order by:') . '</label> ' .
form::combo('sortby', $sortby_combo, html::escapeHTML($sortby)) . '</p>' .
'<p><label for="order" class="ib">' . __('Sort:') . '</label> ' .
form::combo('order', $order_combo, html::escapeHTML($order)) . '</p>' .
'<p><span class="label ib">' . __('Show') . '</span> <label for="nb" class="classic">' .
form::number('nb', 0, 999, $nb_per_page) . ' ' . __('blogs per page') . '</label></p>' .
'</div>' .
'</div>' .
'<p><input type="submit" value="' . __('Apply filters and display options') . '" />' .
'<br class="clear" /></p>' . //Opera sucks
'</form>';
# Show blogs
$blog_list->display($page, $nb_per_page,
($core->auth->isSuperAdmin() ?
'<form action="' . $core->adminurl->get("admin.blogs") . '" method="post" id="form-blogs">' : '') .
'%s' .
($core->auth->isSuperAdmin() ?
'<div class="two-cols clearfix">' .
'<p class="col checkboxes-helpers"></p>' .
'<p class="col right"><label for="action" class="classic">' . __('Selected blogs action:') . '</label> ' .
form::combo('action', $blogs_actions_page->getCombo(),
['class' => 'online', 'extra_html' => 'title="' . __('Actions') . '"']) .
$core->formNonce() .
'<input id="do-action" type="submit" value="' . __('ok') . '" /></p>' .
'</div>' .
'<p><label for="pwd" class="classic">' . __('Please give your password to confirm blog(s) deletion:') . '</label> ' .
form::password('pwd', 20, 255, ['autocomplete' => 'current-password']) . '</p>' .
form::hidden(['sortby'], $sortby) .
form::hidden(['order'], $order) .
form::hidden(['status'], $status) .
form::hidden(['page'], $page) .
form::hidden(['nb'], $nb_per_page) .
'</form>' : ''),
$show_filters
);
}
dcPage::helpBlock('core_blogs');
dcPage::close();

View File

@ -0,0 +1,219 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('categories');
# Remove a categories
if (!empty($_POST['delete'])) {
$keys = array_keys($_POST['delete']);
$cat_id = (int) $keys[0];
# Check if category to delete exists
$c = $core->blog->getCategory((integer) $cat_id);
if ($c->isEmpty()) {
dcPage::addErrorNotice(__('This category does not exist.'));
$core->adminurl->redirect("admin.categories");
}
$name = $c->cat_title;
unset($c);
try {
# Delete category
$core->blog->delCategory($cat_id);
dcPage::addSuccessNotice(sprintf(__('The category "%s" has been successfully deleted.'), html::escapeHTML($name)));
$core->adminurl->redirect("admin.categories");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
# move post into a category
if (!empty($_POST['mov']) && !empty($_POST['mov_cat'])) {
try {
# Check if category where to move posts exists
$keys = array_keys($_POST['mov']);
$cat_id = (int) $keys[0];
$mov_cat = (int) $_POST['mov_cat'][$cat_id];
$mov_cat = $mov_cat ?: null;
if ($mov_cat !== null) {
$c = $core->blog->getCategory($mov_cat);
if ($c->isEmpty()) {
throw new Exception(__('Category where to move entries does not exist'));
}
$name = $c->cat_title;
unset($c);
}
# Move posts
if ($mov_cat != $cat_id) {
$core->blog->changePostsCategory($cat_id, $mov_cat);
}
dcPage::addSuccessNotice(sprintf(__('The entries have been successfully moved to category "%s"'),
html::escapeHTML($name)));
$core->adminurl->redirect("admin.categories");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
# Update order
if (!empty($_POST['save_order']) && !empty($_POST['categories_order'])) {
$categories = json_decode($_POST['categories_order']);
foreach ($categories as $category) {
if (!empty($category->item_id) && !empty($category->left) && !empty($category->right)) {
$core->blog->updCategoryPosition($category->item_id, $category->left, $category->right);
}
}
dcPage::addSuccessNotice(__('Categories have been successfully reordered.'));
$core->adminurl->redirect("admin.categories");
}
# Reset order
if (!empty($_POST['reset'])) {
try
{
$core->blog->resetCategoriesOrder();
dcPage::addSuccessNotice(__('Categories order has been successfully reset.'));
$core->adminurl->redirect("admin.categories");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
/* Display
-------------------------------------------------------- */
$rs = $core->blog->getCategories();
$starting_script = "";
$core->auth->user_prefs->addWorkspace('accessibility');
if (!$core->auth->user_prefs->accessibility->nodragdrop
&& $core->auth->check('categories', $core->blog->id)
&& $rs->count() > 1) {
$starting_script .= dcPage::jsLoad('js/jquery/jquery-ui.custom.js');
$starting_script .= dcPage::jsLoad('js/jquery/jquery.ui.touch-punch.js');
$starting_script .= dcPage::jsLoad('js/jquery/jquery.mjs.nestedSortable.js');
}
$starting_script .= dcPage::jsConfirmClose('form-categories');
$starting_script .= dcPage::jsLoad('js/_categories.js');
dcPage::open(__('Categories'), $starting_script,
dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
__('Categories') => ''
])
);
if (!empty($_GET['del'])) {
dcPage::success(__('The category has been successfully removed.'));
}
if (!empty($_GET['reord'])) {
dcPage::success(__('Categories have been successfully reordered.'));
}
if (!empty($_GET['move'])) {
dcPage::success(__('Entries have been successfully moved to the category you choose.'));
}
$categories_combo = dcAdminCombos::getCategoriesCombo($rs);
echo
'<p class="top-add"><a class="button add" href="' . $core->adminurl->get("admin.category") . '">' . __('New category') . '</a></p>';
echo
'<div class="col">';
if ($rs->isEmpty()) {
echo '<p>' . __('No category so far.') . '</p>';
} else {
echo
'<form action="' . $core->adminurl->get("admin.categories") . '" method="post" id="form-categories">' .
'<div id="categories">';
$ref_level = $level = $rs->level - 1;
while ($rs->fetch()) {
$attr = 'id="cat_' . $rs->cat_id . '" class="cat-line clearfix"';
if ($rs->level > $level) {
echo str_repeat('<ul><li ' . $attr . '>', $rs->level - $level);
} elseif ($rs->level < $level) {
echo str_repeat('</li></ul>', -($rs->level - $level));
}
if ($rs->level <= $level) {
echo '</li><li ' . $attr . '>';
}
echo
'<p class="cat-title"><label class="classic" for="cat_' . $rs->cat_id . '"><a href="' .
$core->adminurl->get("admin.category", ['id' => $rs->cat_id]) . '">' . html::escapeHTML($rs->cat_title) .
'</a></label> </p>' .
'<p class="cat-nb-posts">(<a href="' . $core->adminurl->get("admin.posts", ['cat_id' => $rs->cat_id]) . '">' .
sprintf(($rs->nb_post > 1 ? __('%d entries') : __('%d entry')), $rs->nb_post) . '</a>' .
', ' . __('total:') . ' ' . $rs->nb_total . ')</p>' .
'<p class="cat-url">' . __('URL:') . ' <code>' . html::escapeHTML($rs->cat_url) . '</code></p>';
echo
'<p class="cat-buttons">';
if ($rs->nb_total > 0) {
// remove current category
echo
'<label for="mov_cat_' . $rs->cat_id . '">' . __('Move entries to') . '</label> ' .
form::combo(['mov_cat[' . $rs->cat_id . ']', 'mov_cat_' . $rs->cat_id], array_filter($categories_combo,
function ($cat) {return $cat->value != $GLOBALS['rs']->cat_id;}
), '', '') .
' <input type="submit" class="reset" name="mov[' . $rs->cat_id . ']" value="' . __('OK') . '"/>';
$attr_disabled = ' disabled="disabled"';
$input_class = 'disabled ';
} else {
$attr_disabled = '';
$input_class = '';
}
echo
' <input type="submit"' . $attr_disabled . ' class="' . $input_class . 'delete" name="delete[' . $rs->cat_id . ']" value="' . __('Delete category') . '"/>' .
'</p>';
$level = $rs->level;
}
if ($ref_level - $level < 0) {
echo str_repeat('</li></ul>', -($ref_level - $level));
}
echo
'</div>';
echo '<div class="clear">';
if ($core->auth->check('categories', $core->blog->id) && $rs->count() > 1) {
if (!$core->auth->user_prefs->accessibility->nodragdrop) {
echo '<p class="form-note hidden-if-no-js">' . __('To rearrange categories order, move items by drag and drop, then click on “Save categories order” button.') . '</p>';
}
echo
'<p><span class="hidden-if-no-js">' .
'<input type="hidden" id="categories_order" name="categories_order" value=""/>' .
'<input type="submit" name="save_order" id="save-set-order" value="' . __('Save categories order') . '" />' .
'</span> ';
} else {
echo '<p>';
}
echo
'<input type="submit" class="reset" name="reset" value="' . __('Reorder all categories on the top level') . '" />' .
$core->formNonce() . '</p>' .
'</div></form>';
}
echo '</div>';
dcPage::helpBlock('core_categories');
dcPage::close();

View File

@ -0,0 +1,263 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('categories');
$cat_id = '';
$cat_title = '';
$cat_url = '';
$cat_desc = '';
$cat_position = '';
$blog_settings = new dcSettings($core, $core->blog->id);
$blog_lang = $blog_settings->system->lang;
# Getting existing category
if (!empty($_REQUEST['id'])) {
try {
$rs = $core->blog->getCategory($_REQUEST['id']);
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
if (!$core->error->flag() && !$rs->isEmpty()) {
$cat_id = (integer) $rs->cat_id;
$cat_title = $rs->cat_title;
$cat_url = $rs->cat_url;
$cat_desc = $rs->cat_desc;
}
unset($rs);
# Getting hierarchy information
$parents = $core->blog->getCategoryParents($cat_id);
$rs = $core->blog->getCategoryParent($cat_id);
$cat_parent = $rs->isEmpty() ? 0 : (integer) $rs->cat_id;
unset($rs);
# Allowed parents list
$children = $core->blog->getCategories(['start' => $cat_id]);
$allowed_parents = [__('Top level') => 0];
$p = [];
while ($children->fetch()) {
$p[$children->cat_id] = 1;
}
$rs = $core->blog->getCategories();
while ($rs->fetch()) {
if (!isset($p[$rs->cat_id])) {
$allowed_parents[] = new formSelectOption(
str_repeat('&nbsp;&nbsp;', $rs->level - 1) . ($rs->level - 1 == 0 ? '' : '&bull; ') . html::escapeHTML($rs->cat_title),
$rs->cat_id
);
}
}
unset($rs);
# Allowed siblings list
$siblings = [];
$rs = $core->blog->getCategoryFirstChildren($cat_parent);
while ($rs->fetch()) {
if ($rs->cat_id != $cat_id) {
$siblings[html::escapeHTML($rs->cat_title)] = $rs->cat_id;
}
}
unset($rs);
}
# Changing parent
if ($cat_id && isset($_POST['cat_parent'])) {
$new_parent = (integer) $_POST['cat_parent'];
if ($cat_parent != $new_parent) {
try {
$core->blog->setCategoryParent($cat_id, $new_parent);
dcPage::addSuccessNotice(__('The category has been successfully moved'));
$core->adminurl->redirect("admin.categories");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
}
# Changing sibling
if ($cat_id && isset($_POST['cat_sibling'])) {
try {
$core->blog->setCategoryPosition($cat_id, (integer) $_POST['cat_sibling'], $_POST['cat_move']);
dcPage::addSuccessNotice(__('The category has been successfully moved'));
$core->adminurl->redirect("admin.categories");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
# Create or update a category
if (isset($_POST['cat_title'])) {
$cur = $core->con->openCursor($core->prefix . 'category');
$cur->cat_title = $cat_title = $_POST['cat_title'];
if (isset($_POST['cat_desc'])) {
$cur->cat_desc = $cat_desc = $_POST['cat_desc'];
}
if (isset($_POST['cat_url'])) {
$cur->cat_url = $cat_url = $_POST['cat_url'];
} else {
$cur->cat_url = $cat_url;
}
try
{
# Update category
if ($cat_id) {
# --BEHAVIOR-- adminBeforeCategoryUpdate
$core->callBehavior('adminBeforeCategoryUpdate', $cur, $cat_id);
$core->blog->updCategory($_POST['id'], $cur);
# --BEHAVIOR-- adminAfterCategoryUpdate
$core->callBehavior('adminAfterCategoryUpdate', $cur, $cat_id);
dcPage::addSuccessNotice(__('The category has been successfully updated.'));
$core->adminurl->redirect("admin.category", ['id' => $_POST['id']]);
}
# Create category
else {
# --BEHAVIOR-- adminBeforeCategoryCreate
$core->callBehavior('adminBeforeCategoryCreate', $cur);
$id = $core->blog->addCategory($cur, (integer) $_POST['new_cat_parent']);
# --BEHAVIOR-- adminAfterCategoryCreate
$core->callBehavior('adminAfterCategoryCreate', $cur, $id);
dcPage::addSuccessNotice(sprintf(__('The category "%s" has been successfully created.'),
html::escapeHTML($cur->cat_title)));
$core->adminurl->redirect("admin.categories");
}
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
$title = $cat_id ? html::escapeHTML($cat_title) : __('New category');
$elements = [
html::escapeHTML($core->blog->name) => '',
__('Categories') => $core->adminurl->get("admin.categories")
];
if ($cat_id) {
while ($parents->fetch()) {
$elements[html::escapeHTML($parents->cat_title)] = $core->adminurl->get("admin.category", ['id' => $parents->cat_id]);
}
}
$elements[$title] = '';
$category_editor = $core->auth->getOption('editor');
$rte_flag = true;
$rte_flags = @$core->auth->user_prefs->interface->rte_flags;
if (is_array($rte_flags) && in_array('cat_descr', $rte_flags)) {
$rte_flag = $rte_flags['cat_descr'];
}
dcPage::open($title,
dcPage::jsConfirmClose('category-form') .
dcPage::jsLoad('js/_category.js') .
($rte_flag ? $core->callBehavior('adminPostEditor', $category_editor['xhtml'], 'category', ['#cat_desc'], 'xhtml') : ''),
dcPage::breadcrumb($elements)
);
if (!empty($_GET['upd'])) {
dcPage::success(__('Category has been successfully updated.'));
}
echo
'<form action="' . $core->adminurl->get("admin.category") . '" method="post" id="category-form">' .
'<h3>' . __('Category information') . '</h3>' .
'<p><label class="required" for="cat_title"><abbr title="' . __('Required field') . '">*</abbr> ' . __('Name:') . '</label> ' .
form::field('cat_title', 40, 255, [
'default' => html::escapeHTML($cat_title),
'extra_html' => 'required placeholder="' . __('Name') . '" lang="' . $blog_lang . '" spellcheck="true"'
]) .
'</p>';
if (!$cat_id) {
$rs = $core->blog->getCategories();
echo
'<p><label for="new_cat_parent">' . __('Parent:') . ' ' .
'<select id="new_cat_parent" name="new_cat_parent" >' .
'<option value="0">' . __('(none)') . '</option>';
while ($rs->fetch()) {
echo '<option value="' . $rs->cat_id . '" ' . (!empty($_POST['new_cat_parent']) && $_POST['new_cat_parent'] == $rs->cat_id ? 'selected="selected"' : '') . '>' .
str_repeat('&nbsp;&nbsp;', $rs->level - 1) . ($rs->level - 1 == 0 ? '' : '&bull; ') . html::escapeHTML($rs->cat_title) . '</option>';
}
echo
'</select></label></p>';
unset($rs);
}
echo
'<div class="lockable">' .
'<p><label for="cat_url">' . __('URL:') . '</label> '
. form::field('cat_url', 40, 255, html::escapeHTML($cat_url)) .
'</p>' .
'<p class="form-note warn" id="note-cat-url">' .
__('Warning: If you set the URL manually, it may conflict with another category.') . '</p>' .
'</div>' .
'<p class="area"><label for="cat_desc">' . __('Description:') . '</label> ' .
form::textarea('cat_desc', 50, 8,
[
'default' => html::escapeHTML($cat_desc),
'extra_html' => 'lang="' . $blog_lang . '" spellcheck="true"'
]) .
'</p>' .
'<p><input type="submit" accesskey="s" value="' . __('Save') . '" />' .
($cat_id ? form::hidden('id', $cat_id) : '') .
$core->formNonce() .
'</p>' .
'</form>';
if ($cat_id) {
echo
'<h3 class="border-top">' . __('Move this category') . '</h3>' .
'<div class="two-cols">' .
'<div class="col">' .
'<form action="' . $core->adminurl->get("admin.category") . '" method="post" class="fieldset">' .
'<h4>' . __('Category parent') . '</h4>' .
'<p><label for="cat_parent" class="classic">' . __('Parent:') . '</label> ' .
form::combo('cat_parent', $allowed_parents, $cat_parent) . '</p>' .
'<p><input type="submit" accesskey="s" value="' . __('Save') . '" />' .
form::hidden(['id'], $cat_id) . $core->formNonce() . '</p>' .
'</form>' .
'</div>';
if (count($siblings) > 0) {
echo
'<div class="col">' .
'<form action="' . $core->adminurl->get("admin.category") . '" method="post" class="fieldset">' .
'<h4>' . __('Category sibling') . '</h4>' .
'<p><label class="classic" for="cat_sibling">' . __('Move current category') . '</label> ' .
form::combo('cat_move', [__('before') => 'before', __('after') => 'after'],
['extra_html' => 'title="' . __('position: ') . '"']) . ' ' .
form::combo('cat_sibling', $siblings) . '</p>' .
'<p><input type="submit" accesskey="s" value="' . __('Save') . '" />' .
form::hidden(['id'], $cat_id) . $core->formNonce() . '</p>' .
'</form>' .
'</div>';
}
echo '</div>';
}
dcPage::helpBlock('core_category');
dcPage::close();

View File

@ -0,0 +1,252 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
$comment_id = null;
$comment_dt = '';
$comment_author = '';
$comment_email = '';
$comment_site = '';
$comment_content = '';
$comment_ip = '';
$comment_status = '';
$comment_trackback = 0;
$comment_spam_status = '';
$comment_editor = $core->auth->getOption('editor');
# Status combo
$status_combo = dcAdminCombos::getCommentStatusescombo();
# Adding comment (comming from post form, comments tab)
if (!empty($_POST['add']) && !empty($_POST['post_id'])) {
try
{
$rs = $core->blog->getPosts(['post_id' => $_POST['post_id'], 'post_type' => '']);
if ($rs->isEmpty()) {
throw new Exception(__('Entry does not exist.'));
}
$cur = $core->con->openCursor($core->prefix . 'comment');
$cur->comment_author = $_POST['comment_author'];
$cur->comment_email = html::clean($_POST['comment_email']);
$cur->comment_site = html::clean($_POST['comment_site']);
$cur->comment_content = $core->HTMLfilter($_POST['comment_content']);
$cur->post_id = (integer) $_POST['post_id'];
# --BEHAVIOR-- adminBeforeCommentCreate
$core->callBehavior('adminBeforeCommentCreate', $cur);
$comment_id = $core->blog->addComment($cur);
# --BEHAVIOR-- adminAfterCommentCreate
$core->callBehavior('adminAfterCommentCreate', $cur, $comment_id);
dcPage::addSuccessNotice(__('Comment has been successfully created.'));
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
http::redirect($core->getPostAdminURL($rs->post_type, $rs->post_id, false) . '&co=1');
}
if (!empty($_REQUEST['id'])) {
$params['comment_id'] = $_REQUEST['id'];
try {
$rs = $core->blog->getComments($params);
if (!$rs->isEmpty()) {
$comment_id = $rs->comment_id;
$post_id = $rs->post_id;
$post_type = $rs->post_type;
$post_title = $rs->post_title;
$comment_dt = $rs->comment_dt;
$comment_author = $rs->comment_author;
$comment_email = $rs->comment_email;
$comment_site = $rs->comment_site;
$comment_content = $rs->comment_content;
$comment_ip = $rs->comment_ip;
$comment_status = $rs->comment_status;
$comment_trackback = (boolean) $rs->comment_trackback;
$comment_spam_status = $rs->comment_spam_status;
}
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
if (!$comment_id && !$core->error->flag()) {
$core->error->add(__('No comments'));
}
if (!$core->error->flag() && isset($rs)) {
$can_edit = $can_delete = $can_publish = $core->auth->check('contentadmin', $core->blog->id);
if (!$core->auth->check('contentadmin', $core->blog->id) && $core->auth->userID() == $rs->user_id) {
$can_edit = true;
if ($core->auth->check('delete', $core->blog->id)) {
$can_delete = true;
}
if ($core->auth->check('publish', $core->blog->id)) {
$can_publish = true;
}
}
# update comment
if (!empty($_POST['update']) && $can_edit) {
$cur = $core->con->openCursor($core->prefix . 'comment');
$cur->comment_author = $_POST['comment_author'];
$cur->comment_email = html::clean($_POST['comment_email']);
$cur->comment_site = html::clean($_POST['comment_site']);
$cur->comment_content = $core->HTMLfilter($_POST['comment_content']);
if (isset($_POST['comment_status'])) {
$cur->comment_status = (integer) $_POST['comment_status'];
}
try
{
# --BEHAVIOR-- adminBeforeCommentUpdate
$core->callBehavior('adminBeforeCommentUpdate', $cur, $comment_id);
$core->blog->updComment($comment_id, $cur);
# --BEHAVIOR-- adminAfterCommentUpdate
$core->callBehavior('adminAfterCommentUpdate', $cur, $comment_id);
dcPage::addSuccessNotice(__('Comment has been successfully updated.'));
$core->adminurl->redirect("admin.comment", ['id' => $comment_id]);
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
if (!empty($_POST['delete']) && $can_delete) {
try {
# --BEHAVIOR-- adminBeforeCommentDelete
$core->callBehavior('adminBeforeCommentDelete', $comment_id);
$core->blog->delComment($comment_id);
dcPage::addSuccessNotice(__('Comment has been successfully deleted.'));
http::redirect($core->getPostAdminURL($rs->post_type, $rs->post_id) . '&co=1', false);
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
if (!$can_edit) {
$core->error->add(__("You can't edit this comment."));
}
}
/* DISPLAY
-------------------------------------------------------- */
if ($comment_id) {
$breadcrumb = dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
html::escapeHTML($post_title) => $core->getPostAdminURL($post_type, $post_id) . '&amp;co=1#c' . $comment_id,
__('Edit comment') => ''
]);
} else {
$breadcrumb = dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
html::escapeHTML($post_title) => $core->getPostAdminURL($post_type, $post_id),
__('Edit comment') => ''
]);
}
dcPage::open(__('Edit comment'),
dcPage::jsConfirmClose('comment-form') .
dcPage::jsLoad('js/_comment.js') .
$core->callBehavior('adminPostEditor', $comment_editor['xhtml'], 'comment', ['#comment_content'], 'xhtml') .
# --BEHAVIOR-- adminCommentHeaders
$core->callBehavior('adminCommentHeaders'),
$breadcrumb
);
if ($comment_id) {
if (!empty($_GET['upd'])) {
dcPage::success(__('Comment has been successfully updated.'));
}
$comment_mailto = '';
if ($comment_email) {
$comment_mailto = '<a href="mailto:' . html::escapeHTML($comment_email)
. '?subject=' . rawurlencode(sprintf(__('Your comment on my blog %s'), $core->blog->name))
. '&amp;body='
. rawurlencode(sprintf(__("Hi!\n\nYou wrote a comment on:\n%s\n\n\n"), $rs->getPostURL()))
. '">' . __('Send an e-mail') . '</a>';
}
echo
'<form action="' . $core->adminurl->get("admin.comment") . '" method="post" id="comment-form">' .
'<div class="fieldset">' .
'<h3>' . __('Information collected') . '</h3>' .
'<p>' . __('IP address:') . ' ' .
'<a href="' . $core->adminurl->get("admin.comments", ['ip' => $comment_ip]) . '">' . $comment_ip . '</a></p>' .
'<p>' . __('Date:') . ' ' .
dt::dt2str(__('%Y-%m-%d %H:%M'), $comment_dt) . '</p>' .
'</div>' .
'<h3>' . __('Comment submitted') . '</h3>' .
'<p><label for="comment_author" class="required"><abbr title="' . __('Required field') . '">*</abbr>' . __('Author:') . '</label>' .
form::field('comment_author', 30, 255, [
'default' => html::escapeHTML($comment_author),
'extra_html' => 'required placeholder="' . __('Author') . '"'
]) .
'</p>' .
'<p><label for="comment_email">' . __('Email:') . '</label>' .
form::email('comment_email', 30, 255, html::escapeHTML($comment_email)) .
'<span>' . $comment_mailto . '</span>' .
'</p>' .
'<p><label for="comment_site">' . __('Web site:') . '</label>' .
form::url('comment_site', 30, 255, html::escapeHTML($comment_site)) .
'</p>' .
'<p><label for="comment_status">' . __('Status:') . '</label>' .
form::combo('comment_status', $status_combo,
['default' => $comment_status, 'disabled' => !$can_publish]) .
'</p>' .
# --BEHAVIOR-- adminAfterCommentDesc
$core->callBehavior('adminAfterCommentDesc', $rs) .
'<p class="area"><label for="comment_content">' . __('Comment:') . '</label> ' .
form::textarea('comment_content', 50, 10,
[
'default' => html::escapeHTML($comment_content),
'extra_html' => 'lang="' . $core->auth->getInfo('user_lang') . '" spellcheck="true"'
]) .
'</p>' .
'<p>' . form::hidden('id', $comment_id) .
$core->formNonce() .
'<input type="submit" accesskey="s" name="update" value="' . __('Save') . '" /> ';
if ($can_delete) {
echo '<input type="submit" class="delete" name="delete" value="' . __('Delete') . '" />';
}
echo
'</p>' .
'</form>';
}
dcPage::helpBlock('core_comments');
dcPage::close();

View File

@ -0,0 +1,302 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
if (!empty($_POST['delete_all_spam'])) {
try {
$core->blog->delJunkComments();
$_SESSION['comments_del_spam'] = true;
$core->adminurl->redirect("admin.comments");
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
}
# Creating filter combo boxes
# Filter form we'll put in html_block
$status_combo = array_merge(
['-' => ''],
dcAdminCombos::getCommentStatusescombo()
);
$type_combo = [
'-' => '',
__('Comment') => 'co',
__('Trackback') => 'tb'
];
$sortby_combo = [
__('Date') => 'comment_dt',
__('Entry title') => 'post_title',
__('Entry date') => 'post_dt',
__('Author') => 'comment_author',
__('Status') => 'comment_status'
];
$sortby_lex = [
// key in sorty_combo (see above) => field in SQL request
'post_title' => 'post_title',
'comment_author' => 'comment_author',
'comment_spam_filter' => 'comment_spam_filter'];
$order_combo = [
__('Descending') => 'desc',
__('Ascending') => 'asc'
];
/* Get comments
-------------------------------------------------------- */
$author = isset($_GET['author']) ? $_GET['author'] : '';
$status = isset($_GET['status']) ? $_GET['status'] : '';
$type = !empty($_GET['type']) ? $_GET['type'] : '';
$sortby = !empty($_GET['sortby']) ? $_GET['sortby'] : 'comment_dt';
$order = !empty($_GET['order']) ? $_GET['order'] : 'desc';
$ip = !empty($_GET['ip']) ? $_GET['ip'] : '';
$email = !empty($_GET['email']) ? $_GET['email'] : '';
$site = !empty($_GET['site']) ? $_GET['site'] : '';
$with_spam = $author || $status || $type || $sortby != 'comment_dt' || $order != 'desc' || $ip;
$show_filters = false;
$page = !empty($_GET['page']) ? max(1, (integer) $_GET['page']) : 1;
$nb_per_page = 30;
if (!empty($_GET['nb']) && (integer) $_GET['nb'] > 0) {
if ($nb_per_page != (integer) $_GET['nb']) {
$show_filters = true;
}
$nb_per_page = (integer) $_GET['nb'];
}
$params['limit'] = [(($page - 1) * $nb_per_page), $nb_per_page];
$params['no_content'] = true;
# Author filter
if ($author !== '') {
$params['q_author'] = $author;
$show_filters = true;
} else {
$author = '';
}
# - Type filter
if ($type == 'tb' || $type == 'co') {
$params['comment_trackback'] = ($type == 'tb');
$show_filters = true;
} else {
$type = '';
}
# - Status filter
if ($status !== '' && in_array($status, $status_combo)) {
$params['comment_status'] = $status;
$show_filters = true;
} elseif (!$with_spam) {
$params['comment_status_not'] = -2;
$status = '';
} else {
$status = '';
}
# - IP filter
if ($ip) {
$params['comment_ip'] = $ip;
$show_filters = true;
}
# - email filter
if ($email) {
$params['comment_email'] = $email;
$show_filters = true;
}
# - site filter
if ($site) {
$params['comment_site'] = $site;
$show_filters = true;
}
// Add some sort order if spams displayed
if ($with_spam || ($status == -2)) {
$sortby_combo[__('IP')] = 'comment_ip';
$sortby_combo[__('Spam filter')] = 'comment_spam_filter';
}
# Sortby and order filter
if ($sortby !== '' && in_array($sortby, $sortby_combo)) {
if (array_key_exists($sortby, $sortby_lex)) {
$params['order'] = $core->con->lexFields($sortby_lex[$sortby]);
} else {
$params['order'] = $sortby;
}
if ($order !== '' && in_array($order, $order_combo)) {
$params['order'] .= ' ' . $order;
} else {
$order = 'desc';
}
if ($sortby != 'comment_dt' || $order != 'desc') {
$show_filters = true;
}
} else {
$sortby = 'comment_dt';
$order = 'desc';
}
# Actions combo box
$combo_action = [];
$default = '';
if ($core->auth->check('delete,contentadmin', $core->blog->id) && $status == -2) {
$default = 'delete';
}
$comments_actions_page = new dcCommentsActionsPage($core, $core->adminurl->get("admin.comments"));
if ($comments_actions_page->process()) {
return;
}
/* Get comments
-------------------------------------------------------- */
try {
$comments = $core->blog->getComments($params);
$counter = $core->blog->getComments($params, true);
$comment_list = new adminCommentList($core, $comments, $counter->f(0));
} catch (Exception $e) {
$core->error->add($e->getMessage());
}
/* DISPLAY
-------------------------------------------------------- */
dcPage::open(__('Comments and trackbacks'),
dcPage::jsLoad('js/_comments.js') . dcPage::jsFilterControl($show_filters),
dcPage::breadcrumb(
[
html::escapeHTML($core->blog->name) => '',
__('Comments and trackbacks') => ''
])
);
if (!empty($_GET['upd'])) {
dcPage::success(__('Selected comments have been successfully updated.'));
} elseif (!empty($_GET['del'])) {
dcPage::success(__('Selected comments have been successfully deleted.'));
}
if (!$core->error->flag()) {
if (isset($_SESSION['comments_del_spam'])) {
dcPage::message(__('Spam comments have been successfully deleted.'));
unset($_SESSION['comments_del_spam']);
}
$spam_count = $core->blog->getComments(['comment_status' => -2], true)->f(0);
if ($spam_count > 0) {
echo
'<form action="' . $core->adminurl->get("admin.comments") . '" method="post" class="fieldset">';
if (!$with_spam || ($status != -2)) {
if ($spam_count == 1) {
echo '<p>' . sprintf(__('You have one spam comment.'), '<strong>' . $spam_count . '</strong>') . ' ' .
'<a href="' . $core->adminurl->get("admin.comments", ['status' => -2]) . '">' . __('Show it.') . '</a></p>';
} elseif ($spam_count > 1) {
echo '<p>' . sprintf(__('You have %s spam comments.'), '<strong>' . $spam_count . '</strong>') . ' ' .
'<a href="' . $core->adminurl->get("admin.comments", ['status' => -2]) . '">' . __('Show them.') . '</a></p>';
}
}
echo
'<p>' .
$core->formNonce() .
'<input name="delete_all_spam" class="delete" type="submit" value="' . __('Delete all spams') . '" /></p>';
# --BEHAVIOR-- adminCommentsSpamForm
$core->callBehavior('adminCommentsSpamForm', $core);
echo '</form>';
}
echo
'<form action="' . $core->adminurl->get("admin.comments") . '" method="get" id="filters-form">' .
'<h3 class="out-of-screen-if-js">' . __('Show filters and display options') . '</h3>' .
'<div class="table">' .
'<div class="cell">' .
'<h4>' . __('Filters') . '</h4>' .
'<p><label for="type" class="ib">' . __('Type:') . '</label> ' .
form::combo('type', $type_combo, $type) . '</p> ' .
'<p><label for="status" class="ib">' . __('Status:') . '</label> ' .
form::combo('status', $status_combo, $status) . '</p>' .
'</div>' .
'<div class="cell filters-sibling-cell">' .
'<p><label for="author" class="ib">' . __('Author:') . '</label> ' .
form::field('author', 20, 255, html::escapeHTML($author)) . '</p>' .
'<p><label for="ip" class="ib">' . __('IP address:') . '</label> ' .
form::field('ip', 20, 39, html::escapeHTML($ip)) . '</p>' .
'<p><label for="email" class="ib">' . __('Email:') . '</label> ' .
form::field('email', 20, 255, html::escapeHTML($email)) . '</p>' .
'<p><label for="site" class="ib">' . __('Web site:') . '</label> ' .
form::field('site', 20, 255, html::escapeHTML($site)) . '</p>' .
'</div>' .
'<div class="cell filters-options">' .
'<h4>' . __('Display options') . '</h4>' .
'<p><label for="sortby" class="ib">' . __('Order by:') . '</label> ' .
form::combo('sortby', $sortby_combo, $sortby) . '</p>' .
'<p><label for="order" class="ib">' . __('Sort:') . '</label> ' .
form::combo('order', $order_combo, $order) . '</p>' .
'<p><span class="label ib">' . __('Show') . '</span> <label for="nb" class="classic">' .
form::number('nb', 0, 999, $nb_per_page) . ' ' .
__('comments per page') . '</label></p>' .
'</div>' .
'</div>' .
'<p><input type="submit" value="' . __('Apply filters and display options') . '" />' .
'<br class="clear" /></p>' . //Opera sucks
'</form>';
# Show comments
$comment_list->display($page, $nb_per_page,
'<form action="' . $core->adminurl->get("admin.comments") . '" method="post" id="form-comments">' .
'%s' .
'<div class="two-cols">' .
'<p class="col checkboxes-helpers"></p>' .
'<p class="col right"><label for="action" class="classic">' . __('Selected comments action:') . '</label> ' .
form::combo('action', $comments_actions_page->getCombo(),
['default' => $default, 'extra_html' => 'title="' . __('Actions') . '"']) .
$core->formNonce() .
'<input id="do-action" type="submit" value="' . __('ok') . '" /></p>' .
form::hidden(['type'], $type) .
form::hidden(['sortby'], $sortby) .
form::hidden(['order'], $order) .
form::hidden(['author'], html::escapeHTML(preg_replace('/%/', '%%', $author))) .
form::hidden(['status'], $status) .
form::hidden(['ip'], preg_replace('/%/', '%%', $ip)) .
form::hidden(['page'], $page) .
form::hidden(['nb'], $nb_per_page) .
form::hidden(['email'], html::escapeHTML(preg_replace('/%/', '%%', $email))) .
form::hidden(['site'], html::escapeHTML(preg_replace('/%/', '%%', $site))) .
'</div>' .
'</form>',
$show_filters,
($with_spam || ($status == -2))
);
}
dcPage::helpBlock('core_comments');
dcPage::close();

View File

@ -0,0 +1,30 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*
* @deprecated It is only used for plugins compatibility
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
if (isset($_REQUEST['redir'])) {
$u = explode('?', $_REQUEST['redir']);
$uri = $u[0];
if (isset($u[1])) {
parse_str($u[1], $args);
}
$args['redir'] = $_REQUEST['redir'];
} else {
$uri = $core->adminurl->get("admin.comments");
$args = [];
}
$comments_actions_page = new dcCommentsActionsPage($core, $uri, $args);
$comments_actions_page->setEnableRedirSelection(false);
$comments_actions_page->process();

View File

@ -0,0 +1,106 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
// From: https://github.com/nico3333fr/CSP-useful
//
// Note: this script requires PHP ≥ 5.4.
// Inspired from https://mathiasbynens.be/notes/csp-reports
// Dareboost wants it? Not a problem.
header('X-Content-Type-Options: "nosniff"');
require dirname(__FILE__) . '/../inc/admin/prepend.php';
// Specify admin CSP log file if necessary
if (!defined('LOGFILE')) {
define('LOGFILE', path::real(DC_VAR) . '/csp/csp_report.json');
}
// Get the raw POST data
$data = file_get_contents('php://input');
// Only continue if its valid JSON that is not just `null`, `0`, `false` or an
// empty string, i.e. if it could be a CSP violation report.
if ($data = json_decode($data, true)) {
// get source-file and blocked-URI to perform some tests
$source_file = isset($data['csp-report']['source-file']) ? $data['csp-report']['source-file'] : '';
$line_number = isset($data['csp-report']['line-number']) ? $data['csp-report']['line-number'] : '';
$blocked_uri = isset($data['csp-report']['blocked-uri']) ? $data['csp-report']['blocked-uri'] : '';
$document_uri = isset($data['csp-report']['document-uri']) ? $data['csp-report']['document-uri'] : '';
$violated_directive = isset($data['csp-report']['violated-directive']) ? $data['csp-report']['violated-directive'] : '';
if (
// avoid false positives notifications coming from Chrome extensions (Wappalyzer, MuteTab, etc.)
// bug here https://code.google.com/p/chromium/issues/detail?id=524356
strpos($source_file, 'chrome-extension://') === false
// avoid false positives notifications coming from Safari extensions (diigo, evernote, etc.)
&& strpos($source_file, 'safari-extension://') === false
&& strpos($blocked_uri, 'safari-extension://') === false
// search engine extensions ?
&& strpos($source_file, 'se-extension://') === false
// added by browsers in webviews
&& strpos($blocked_uri, 'webviewprogressproxy://') === false
// Google Search App see for details https://github.com/nico3333fr/CSP-useful/commit/ecc8f9b0b379ae643bc754d2db33c8b47e185fd1
&& strpos($blocked_uri, 'gsa://onpageload') === false
) {
// Prepare report data (hash => info)
$hash = hash('md5', $blocked_uri . $document_uri . $source_file . $line_number . $violated_directive);
try {
// Check report dir (create it if necessary)
files::makeDir(dirname(LOGFILE), true);
// Check if report is not already stored in log file
$contents = '';
if (file_exists(LOGFILE)) {
$contents = file_get_contents(LOGFILE);
if ($contents && $contents != '') {
if (substr($contents, -1) == ',') {
// Remove final comma if present
$contents = substr($contents, 0, -1);
}
if ($contents != '') {
$list = json_decode('[' . $contents . ']', true);
if (is_array($list)) {
foreach ($list as $idx => $value) {
if (isset($value['hash']) && $value['hash'] == $hash) {
// Already stored, ignore
return;
}
}
}
}
}
}
// Add report to the file
if (!($fp = @fopen(LOGFILE, 'a'))) {
// Unable to open file, ignore
return;
}
// Prettify the JSON-formatted data
$violation = array_merge(['hash' => $hash], $data['csp-report']);
$output = json_encode($violation, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
// The file content will have to be enclosed in brackets [] before
// beeing decoded with json_decoded(<content>,true);
fprintf($fp, ($contents != '' ? ',' : '') . '%s', $output);
} catch (Exception $e) {
return;
}
}
}

View File

@ -0,0 +1,34 @@
<?php
# -- BEGIN LICENSE BLOCK ---------------------------------------
#
# This file is part of Dotclear 2.
#
# Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
# Licensed under the GPL version 2.0 license.
# See LICENSE file or
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
#
# -- END LICENSE BLOCK -----------------------------------------
# Delete users
if (!empty($_REQUEST['dispatch_action']))
{
if ($_REQUEST['dispatch_action'] == 'deleteuser')
{
if (!empty($_REQUEST['user_id'])) {
$delete_users = $_REQUEST['user_id'];
}
include dirname(__FILE__).'/users.php';
exit;
}
elseif ($_REQUEST['dispatch_action'] == 'setpermissions')
{
include dirname(__FILE__).'/permissions_blog.php';
exit;
}
}
echo '<p>What the hell are you doing here?</p>';
exit;
?>

View File

@ -0,0 +1,95 @@
<?php
/**
* @package Dotclear
* @subpackage Backend
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
require dirname(__FILE__) . '/../inc/admin/prepend.php';
dcPage::check('usage,contentadmin');
$helpPage = function (...$args) {
$ret = ['content' => '', 'title' => ''];
if (empty($args)) {
return $ret;
}
;
global $__resources;
if (empty($__resources['help'])) {
return $ret;
}
$content = '';
$title = '';
foreach ($args as $v) {
if (is_object($v) && isset($v->content)) {
$content .= $v->content;
continue;
}
if (!isset($__resources['help'][$v])) {
continue;
}
$f = $__resources['help'][$v];
if (!file_exists($f) || !is_readable($f)) {
continue;
}
$fc = file_get_contents($f);
if (preg_match('|<body[^>]*?>(.*?)</body>|ms', $fc, $matches)) {
$content .= $matches[1];
if (preg_match('|<title[^>]*?>(.*?)</title>|ms', $fc, $matches)) {
$title = $matches[1];
}
} else {
$content .= $fc;
}
}
if (trim($content) == '') {
return $ret;
}
$ret['content'] = $content;
if ($title != '') {
$ret['title'] = $title;
}
return $ret;
};
$help_page = !empty($_GET['page']) ? html::escapeHTML($_GET['page']) : 'index';
$content_array = $helpPage($help_page);
if (($content_array['content'] == '') || ($help_page == 'index')) {
$content_array = $helpPage('index');
}
if ($content_array['title'] != '') {
$breadcrumb = dcPage::breadcrumb(
[
__('Global help') => $core->adminurl->get("admin.help"),
$content_array['title'] => ''
]);
} else {
$breadcrumb = dcPage::breadcrumb(
[
__('Global help') => ''
]);
}
/* DISPLAY
-------------------------------------------------------- */
dcPage::open(__('Global help'),
dcPage::jsPageTabs('first-step'),
$breadcrumb
);
echo $content_array['content'];
// Prevents global help link display
$GLOBALS['__resources']['ctxhelp'] = true;
dcPage::close();

BIN
dotclear._no/admin/images/add.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><title>attachment</title><path d="M9.5 19.75a4.25 4.25 0 0 1-4.25-4.25v-6.5a2.75 2.75 0 0 1 5.5 0v6h-1.5v-6a1.25 1.25 0 0 0-2.5 0v6.5a2.75 2.75 0 0 0 5.5 0v-11.5a2.25 2.25 0 0 0-4.5 0v1h-1.5v-1a3.75 3.75 0 0 1 7.5 0v11.5a4.25 4.25 0 0 1-4.25 4.25z" fill="#ff0000"/></svg>

After

Width:  |  Height:  |  Size: 332 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

View File

@ -0,0 +1,11 @@
<svg
id="mask"
viewbox="0 0 16 16"
preserveAspectRatio="xMinYMin meet"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M7.9 7.9l2.1 7.5 1.7-2.6 3.2 3.2 1.1-1.1-3.3-3.2 2.7-1.6z">
</path>
<path d="M8 12h-7v-9h12v5.4l1 0.2v-6.6h-14v11h8.2z">
</path>
</svg>

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 820 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 925 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Some files were not shown because too many files have changed in this diff Show More