Si vous constatez la perte de tickets, de commentaires ou de projets, veuillez consulter la page https://status.framasoft.org/incident/626.

Commit 3aeb8300 authored by Thomas Citharel's avatar Thomas Citharel 🌲

Wiki stuff

Signed-off-by: Thomas Citharel's avatarThomas Citharel <tcit@tcit.fr>
parent 802a0297
......@@ -19,8 +19,12 @@
/node_modules/
/grav/
/doku/
/blog/users/*
!/blog/users/.gitkeep
/wiki/users/*
!/wiki/users/.gitkeep
data/db/framasites.sqlite
......@@ -48,9 +48,6 @@ domain:
site:
subdomain: Sub-domain
email: Email address
login: Login
password: Password
save: Create a website
delete:
form:
......@@ -65,12 +62,42 @@ site:
admin: Administration
add_user: Add an user
forgot_password: Forgot password
export: Export
export: Export website
delete: Delete website
management: Management
create:
blog: Create a blog
wiki: Create a wiki
created: Created with user %user% %time%
new:
subdomain:
title: Site subdomain
informations:
title: Website informations
site_name:
label: Website name
desc: Your website name
site_description:
label: Website description
desc: In a few sentences, your website description.
site_keywords:
label: Website keywords
desc: A few keywords associated to your website, separated with commas.
user:
title: Website administrator
email: Email address
login: Login
password: Password
locale: Language
admin: Admin
fullname: Fullname
info:
domain: If you want it, you will be able to buy a domain for this website later.
more: More informations
prefilled: The following fields are prefilled with your account informations, but you can of course change them.
subdomain_help: This name will be used as your website subdomain. It is limited to 25 characters.
different_password: We recommend to choose a different password for your websites and this administration panel.
optional: Optional
contact:
email: Email address
......
......@@ -48,9 +48,6 @@ domain:
site:
subdomain: Sous-domaine
email: Adresse email
login: Identifiant
password: Mot de passe
save: Créer un site
delete:
form:
......@@ -67,14 +64,14 @@ site:
forgot_password: Mot de passe oublié
export: Exporter
delete: Supprimer
more: Gestion du site
management: Gestion du site
create:
blog: Créer un blog
wiki: Créer un wiki
created: Créé avec l'utilisateur %user% il y a %time%
new:
subdomain:
title: Domaine du site
title: Sous-domaine du site
informations:
title: Informations du site
site_name:
......@@ -86,6 +83,21 @@ site:
site_keywords:
label: Mots-clés du site
desc: Quelques mots clés associés à votre site, séparés par des virgules
user:
title: Administrateur de votre site
email: Adresse email
login: Identifiant
password: Mot de passe
locale: Langue
admin: Admin
fullname: Nom complet
info:
domain: Si vous le désirez, vous pourrez acheter un domaine par la suite.
more: Plus d'informations
prefilled: Les valeurs ci-dessous sont préremplies avec les informations de votre compte, mais vous pouvez bien entendu les modifier.
subdomain_help: Ce nom sera utilisé comme sous-domaine de votre site. Il est limité à 25 caractères.
different_password: Nous vous recommandons de choisir un mot de passe différent pour vos sites et cet espace d'administration.
optional: Faculatif
contact:
email: Adresse email
......@@ -111,6 +123,12 @@ payment:
desc: Nous vous confirmons votre paiement de %amount%€ à l'ordre de Framasoft. Vous allez maintenant pouvoir renseigner les informations nécessaires à la réservation de votre domaine.
next.btn: Suivant
settings:
title: Paramètres
languages:
label: Langues
save: Sauvegarder
flashes:
site:
deleted: Le site %site% a été supprimé.
\ No newline at end of file
{% extends 'base.html.twig' %}
{% block content %}
{% set blog = app.user.hasBlog %}
{% set wiki = app.user.hasWiki %}
<div class="uk-flex uk-flex-center">
{% for site in sites %}
<div class="uk-card uk-card-default uk-width-1-2@m">
{% if blog %}
<div class="uk-card-header">
<div class="uk-grid-small uk-flex-middle" uk-grid>
<div class="uk-width-auto">
<i class="fa fa-globe" aria-hidden="true"></i>
</div>
<div class="uk-width-expand">
<div class="uk-card-badge uk-label">BLOG</div>
<h3 class="uk-card-title uk-margin-remove-bottom">{{ site_infos(blog).name }} <a href="https://{{ blog.subdomain }}.frama.site"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a></h3>
{% if blog.admin %}
<p class="uk-text-meta uk-margin-remove-top"><time datetime="2016-04-01T19:00">{{ 'site.created' | trans({'%user%': blog.admin.username, '%time%': blog.createdAt | time_diff}) }}</time></p>
<div class="uk-card-badge uk-label">{% spaceless %}
{% if site is blog %}
BLOG
{% elseif site is wiki %}
WIKI
{% endif %}
{% endspaceless %}</div>
<h3 class="uk-card-title uk-margin-remove-bottom">{{ site_infos(site).name }} <a href="https://{{ site.subdomain }}.frama.{% spaceless %}
{% if site is blog %}
blog
{% elseif site is wiki %}
wiki
{% endif %}
{% endspaceless %}"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a></h3>
{% if site.admin %}
<p class="uk-text-meta uk-margin-remove-top"><time datetime="2016-04-01T19:00">{{ 'site.created' | trans({'%user%': site.admin.username, '%time%': site.createdAt | time_diff}) }}</time></p>
{% endif %}
</div>
</div>
</div>
<div class="uk-card-body">
{{ site_infos(blog).description }}
{{ site_infos(site).description }}
</div>
{#<div class="uk-card-body">
{% if blog.domains | length > 0 %}
{% if site.domains | length > 0 %}
{{ 'site.domains.also_available' | trans }}
<ul class="uk-list uk-list-divider">
{% for domain in blog.domains %}
{% for domain in site.domains %}
<li><a href="https://{{ domain.domainName }}">{{ domain.domainName }} <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a></li>
{% endfor %}
</ul>
......@@ -35,33 +45,17 @@
<h3><a class="btn btn-primary" href="{{ path('domain') }}">{{ 'site.domains.management' | trans }}</a></h3>
</div>#}
<div class="uk-card-footer">
<a href="https://{{ blog.subdomain }}.frama.site/admin" class="uk-button uk-button-primary">{{ 'site.shortcuts.admin' | trans }}</a>
<a href="{{ path('site-view', {'site': blog.id}) }}" class="uk-button uk-button-default">{{ 'site.shortcuts.more' | trans }}</a>
</div>
{% else %}
<div class="uk-flex uk-flex-center uk-flex-middle">
<h3><a class="btn btn-primary" href="{{ path('new-blog') }}">{{ 'site.create.blog' | trans }}</a></h3>
</div>
{% endif %}
</div>
{% if wiki %}
<div class="uk-card uk-card-default uk-width-1-2@m">
<h1>{{ 'site.type.wiki' | trans }}</h1>
<h3><a href="https://{{ wiki.subdomain }}.frama.site">{{ wiki.subdomain }}.frama.site <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></a></h3>
<span>{{ 'site.created' | trans({'%user%': wiki.login, '%time%': wiki.createdAt | time_diff}) }}</span>
<div>
<a href="https://{{ wiki.subdomain }}.frama.site/admin" class="btn btn-success">{{ 'site.shortcuts.admin' | trans }}</a>
<a href="#" class="btn btn-info">{{ 'site.shortcuts.add_user' | trans }}</a>
<a href="#" class="btn btn-info">{{ 'site.shortcuts.forgot_password' | trans }}</a>
<a href="#" class="btn btn-warning">{{ 'site.shortcuts.export' | trans }}</a>
<a href="{{ path('delete-site', {'site': wiki.id}) }}" class="btn btn-danger">{{ 'site.shortcuts.delete' | trans }}</a>
<a href="https://{{ site.subdomain }}.frama.site/admin" class="uk-button uk-button-primary">{{ 'site.shortcuts.admin' | trans }}</a>
<a href="{{ path('site-view', {'site': site.id}) }}" class="uk-button uk-button-default">{{ 'site.shortcuts.management' | trans }}</a>
</div>
</div>
{% else %}
<div class="uk-card uk-card-default uk-card-body uk-width-1-2@m uk-margin-left">
<h3><a class="btn btn-primary" href="{{ path('new-site', {'type': 'wiki'}) }}">{{ 'site.create.wiki' | trans }}</a></h3>
</div>
{% endif %}
{% endfor %}
</div>
<hr>
<div class="uk-flex uk-flex-center uk-flex-middle">
<h3><a class="btn btn-primary" href="{{ path('new-wiki') }}">{{ 'site.create.wiki' | trans }}</a></h3>
</div>
<div class="uk-flex uk-flex-center uk-flex-middle">
<h3><a class="btn btn-primary" href="{{ path('new-blog') }}">{{ 'site.create.blog' | trans }}</a></h3>
</div>
{% endblock %}
{% extends 'base.html.twig' %}
{% block content %}
<div class="row">
{{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
{{ form_errors(form) }}
<h2>{{ 'settings.title' | trans }}</h2>
<div class="form-group">
{{ form_label(form.locale) }}
{{ form_errors(form.locale) }}
<div class="col-sm-10">
{{ form_widget(form.locale) }}
<small class="help-block">Ce nom sera utilisé comme sous-domaine de votre site. Il est limité à 25 caractères.</small>
</div>
</div>
{{ form_widget(form.save, {'attr': {'class': 'btn btn-primary'}}) }}
{{ form_rest(form) }}
</div>
{% endblock %}
\ No newline at end of file
......@@ -15,11 +15,11 @@
{{ form_widget(form.subdomain) }}
<div class="input-group-addon">.frama.site</div>
</div>
<small class="help-block">Ce nom sera utilisé comme sous-domaine de votre site. Il est limité à 25 caractères.</small>
<small class="help-block">{{ 'site.new.info.subdomain_help' | trans }}</small>
</div>
</div>
<div class="alert alert-info">
Si vous le désirez, vous pourrez acheter un domaine par la suite. <a href="{{ path('domain') }}"><b>Plus d'informations <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></b></a>
{{ 'site.new.info.domain' | trans }} <a href="{{ path('domain') }}"><b>{{ 'site.new.info.more' | trans }} <span class="glyphicon glyphicon-new-window" aria-hidden="true"></span></b></a>
</div>
<h2>{{ 'site.new.informations.title' | trans }}</h2>
......@@ -41,6 +41,7 @@
</div>
</div>
{% if blog %}
<div class="form-group">
{{ form_errors(form.siteKeywords) }}
{{ form_label(form.siteKeywords) }}
......@@ -49,10 +50,22 @@
<small class="help-block">{{ 'site.new.informations.site_keywords.desc' | trans }}</small>
</div>
</div>
{% endif %}
<h2>Module d'administration de votre site</h2>
{% if wiki %}
<div class="form-group">
{{ form_errors(form.policy) }}
{{ form_label(form.policy) }}
<div class="col-sm-10">
{{ form_widget(form.policy) }}
<small class="help-block">{{ 'site.new.info.optional' | trans }}</small>
</div>
</div>
{% endif %}
<h2>{{ 'site.new.user.title' | trans }}</h2>
<div class="alert alert-info">
Les valeurs ci-dessous sont préremplies avec les informations de votre compte, mais vous pouvez bien entendu les modifier.
{{ 'site.new.info.prefilled' | trans }}
</div>
<div class="form-group">
......@@ -77,11 +90,33 @@
<div class="col-sm-10">
<div class="input-group">
{{ form_widget(form.siteUsers.password) }}
<small class="help-block">Nous vous recommandons de choisir un mot de passe différent pour vos sites et cet espace d'administration.</small>
<small class="help-block">{{ 'site.new.info.different_password' | trans }}</small>
</div>
</div>
</div>
{% if wiki %}
<div class="form-group">
{{ form_errors(form.siteUsers.fullName) }}
{{ form_label(form.siteUsers.fullName) }}
<div class="col-sm-10">
{{ form_widget(form.siteUsers.fullName) }}
<small class="help-block">{{ 'site.new.info.optional' | trans }}</small>
</div>
</div>
{% endif %}
{% if blog %}
<div class="form-group">
{{ form_errors(form.siteUsers.locale) }}
{{ form_label(form.siteUsers.locale) }}
<div class="col-sm-10">
{{ form_widget(form.siteUsers.locale) }}
<small class="help-block">{{ 'site.new.info.optional' | trans }}</small>
</div>
</div>
{% endif %}
{{ form_widget(form.save, {'attr': {'class': 'btn btn-primary'}}) }}
{{ form_rest(form) }}
......
{% extends 'base.html.twig' %}
{% block content %}
{{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
{{ form_errors(form) }}
<div class="form-group">
{{ form_errors(form.email) }}
{{ form_label(form.email) }}
<div class="col-sm-10">
{{ form_widget(form.email) }}
</div>
</div>
<div class="form-group">
{{ form_errors(form.username) }}
{{ form_label(form.username) }}
<div class="col-sm-10">
{{ form_widget(form.username) }}
</div>
</div>
<div class="form-group">
{{ form_errors(form.password) }}
{{ form_label(form.password) }}
<div class="col-sm-10">
{{ form_widget(form.password) }}
<small class="help-block">{{ 'site.new.info.different_password' | trans }}</small>
</div>
</div>
<div class="form-group">
{{ form_errors(form.fullname) }}
{{ form_label(form.fullname) }}
<div class="col-sm-10">
{{ form_widget(form.fullname) }}
<small class="help-block">{{ 'site.new.info.optional' | trans }}</small>
</div>
</div>
<div class="form-group">
{{ form_errors(form.locale) }}
{{ form_label(form.locale) }}
<div class="col-sm-10">
{{ form_widget(form.locale) }}
<small class="help-block">{{ 'site.new.info.optional' | trans }}</small>
</div>
</div>
<div class="form-group">
{{ form_errors(form.admin) }}
{{ form_label(form.admin) }}
{{ form_widget(form.admin) }}
</div>
{{ form_rest(form) }}
{% endblock content %}
\ No newline at end of file
......@@ -13,17 +13,21 @@
<th>Username</th>
<th>Email</th>
<th>Admin</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for user in site_infos(site).users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{{ user.admin ? 'Y' : 'N' }}</td>
<td><a href="{{ path('delete-site-user', {'siteUser': user.username, 'site': site.id}) }}">Supprimer</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<a id="blog_add_user_button" href="#" class="btn btn-info">{{ 'site.shortcuts.add_user' | trans }}</a>
<a id="blog_add_user_button" href="{{ path('site-add-user', {'site': site.id}) }}" class="btn btn-info">{{ 'site.shortcuts.add_user' | trans }}</a>
<h3>Tools</h3>
<a href="https://{{ site.subdomain }}.frama.site/admin/forgot" class="btn btn-info">{{ 'site.shortcuts.forgot_password' | trans }}</a>
......
......@@ -9,12 +9,15 @@ parameters:
locale: fr
app:
languages:
fr: Français
en: English
blog:
path_accounts: '%kernel.project_dir%/blog/users'
path_grav: '%kernel.project_dir%/grav'
wiki:
path_accounts: '%kernel.project_dir%/wiki/users'
path_doku: '%kernel.project_dir%/dokuwiki'
path_doku: '%kernel.project_dir%/doku'
gandi:
mode: '%gandi_api_mode%'
key:
......
This diff is collapsed.
......@@ -7,6 +7,8 @@ use AppBundle\Entity\Blog\BlogUser;
use AppBundle\Exception\SubDomainFolderExistsException;
use Psr\Log\LoggerInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\Yaml\Yaml;
class AsyncAccountGrav extends AbstractMakeAccount implements AsyncAccountInterface
......@@ -142,16 +144,22 @@ class AsyncAccountGrav extends AbstractMakeAccount implements AsyncAccountInterf
public function makeAccount()
{
$adminStatus = null;
$admin = $this->siteUser->isAdmin();
if ($admin) {
$adminStatus = [
'login' => true,
'super' => false,
];
}
$userData = [
'hashed_password' => password_hash($this->siteUser->getPassword(), PASSWORD_DEFAULT),
'email' => $this->user->getEmail(),
'language' => $this->user->getLocale(),
'access' => [
'admin' => [
'login' => true,
'super' => false,
],
'admin' => $adminStatus,
'site' => [
'login' => true,
],
......@@ -161,22 +169,25 @@ class AsyncAccountGrav extends AbstractMakeAccount implements AsyncAccountInterf
'configuration_site' => false,
'configuration_media' => false,
'configuration_info' => false,
'settings' => true,
'settings' => $admin,
'pages' => true,
'maintenance' => true,
'maintenance' => $admin,
'statistics' => true,
'plugins' => true,
'themes' => true,
'users' => true,
'plugins' => $admin,
'themes' => $admin,
'users' => $admin,
],
'fullname' => $this->siteUser->getFullName(),
'fullname' => $this->siteUser->getUsername(),
'state' => 'enabled',
];
$yamlData = Yaml::dump($userData);
file_put_contents($this->accountsPath . '/' . $this->site->getSubdomain() . '/user/accounts/' . $this->siteUser->getUsername() . '.yaml', $yamlData);
}
public function delete()
/**
* @throws \Exception
*/
public function deleteSite()
{
$fs = new Filesystem();
$this->logger->info('Deleting site at subdomain ' . $this->site->getSubdomain() . '...');
......@@ -185,4 +196,33 @@ class AsyncAccountGrav extends AbstractMakeAccount implements AsyncAccountInterf
}
$fs->remove($this->accountsPath . '/' . $this->site->getSubdomain());
}
/**
* Deletes an user from a site
*/
public function deleteSiteUser()
{
$this->logger->debug($this->user->getUsername() . " tried to delete account " . $this->siteUser->getUsername() . " for website " . $this->site->getSubdomain());
$fs = new Filesystem();
$finder = new Finder();
if (!$fs->exists($this->accountsPath . '/' . $this->site->getSubdomain() . '/user/accounts' . $this->siteUser->getUsername() . '.yaml')) {
throw new \Exception("This user doesn't exist. Strange");
}
$files = $finder->files()->in($this->accountsPath . '/' . $this->site->getSubdomain() . '/user/accounts')->name('*.yaml');
foreach ($files as $file) {
/** @var SplFileInfo $file */
$userData = Yaml::parse($file->getContents());
$this->logger->debug("Testing if account " . $file->getRelativePathname() . " is admin");
/** There's at least one more admin, we can remove this user */
if (isset($userData['access']['admin']) && null !== $userData['access']['admin'] && $file->getRelativePathname() !== $this->siteUser->getUsername()) {
$this->logger->debug("The site user " . $file . " is an admin, so we can delete the site user " . $this->siteUser->getUsername());
$fs->remove($this->accountsPath . '/' . $this->site->getSubdomain() . '/user/accounts/' . $this->siteUser->getUsername() . '.yaml');
}
}
}
}
\ No newline at end of file
......@@ -20,7 +20,8 @@ class DefaultController extends Controller
/**
* @Route("/", name="homepage")
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
* @param SiteFactory $siteFactory
* @return Response
*/
public function indexAction(Request $request, SiteFactory $siteFactory): Response
{
......@@ -29,7 +30,7 @@ class DefaultController extends Controller
$sites = $this->getUser()->getSites();
foreach ($sites as $site) {
/** @var $site Site */
$site->setSiteUsers($siteFactory->getUsersForSite($site->getSubdomain()));
$site->setSiteUsers($siteFactory->getUsersForSite($site));
}
return $this->render('default/panel.html.twig', [
......
<?php
namespace AppBundle\Controller;
use AppBundle\Form\SettingsType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class SettingsController extends Controller
{
/**
* @Route("/settings", name="settings")
* @param Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request): Response
{
$em = $this->getDoctrine()->getManager();
// handle basic config detail (this form is defined as a service)
$configForm = $this->createForm(SettingsType::class, $this->getUser(), ['action' => $this->generateUrl('settings')]);
$configForm->handleRequest($request);
if ($configForm->isSubmitted() && $configForm->isValid()) {
$em->persist($this->getUser());
$em->flush();
$request->getSession()->set('_locale', $this->getUser()->getLocale());
$this->get('session')->getFlashBag()->add(
'notice',
'flashes.config.notice.config_saved'
);
return $this->redirect($this->generateUrl('settings'));
}
return $this->render('default/settings.html.twig', ['form' => $configForm->createView()]);
}
}
This diff is collapsed.
......@@ -26,6 +26,7 @@ class AppExtension extends Extension
$container->setParameter('app.payplug_api_mode', $config['payplug']['mode']);
$container->setParameter('app.payplug_api_key_test', $config['payplug']['key']['test']);
$container->setParameter('app.payplug_api_key_prod', $config['payplug']['key']['prod']);
$container->setParameter('app.languages', $config['languages']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
......
......@@ -14,6 +14,9 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->arrayNode('languages')
->prototype('scalar')->end()
->end()
->arrayNode('blog')
->children()
->scalarNode('path_accounts')
......
......@@ -24,6 +24,11 @@ class AbstractSiteUser
*/
protected $admin;
/**
* @var string
*/
protected $locale;
/**
* @return string
*/
......@@ -81,7 +86,7 @@ class AbstractSiteUser
/**
* @return bool
*/
public function isAdmin(): bool
public function isAdmin()
{
return $this->admin;
}
......@@ -96,5 +101,22 @@ class AbstractSiteUser
return $this;
}
/**
* @return string
*/
public function getLocale()
{
return $this->locale;
}
/**
* @param string $locale
* @return AbstractSiteUser
*/
public function setLocale(string $locale): AbstractSiteUser
{
$this->locale = $locale;
return $this;
}