Remove admin account

This commit is contained in:
julien committed 2026-11-02 22:00:14 +01:00
1 parent 4f28a59c7e
commit db316b2914
13 files changed
+372 -144

No files matched your search

+31 -10
View File
@@ -33,15 +33,24 @@ Créer ou mettre à jour la base de données :
bundle exec ruby bin/migrate bundle exec ruby bin/migrate
``` ```
Lors de la première installation, créer le compte du premier administrateur : Créer un ou plusieurs comptes de test :
```sh ```sh
bundle exec ruby bin/initialize-admin bundle exec ruby bin/initialize-account
``` ```
Cette commande est à exécuter une seule fois. Elle crée un compte vérifié et Cette commande crée un compte vérifié et sa fiche de membre.
lui attribue les droits techniques d'administration. Elle ne crée pas de Elle peut être exécutée plusieurs fois. Les comptes créés ne possèdent
demande d'adhésion. aucune adhésion ni responsabilité au sein du Noyau.
Pour tester l'espace du Noyau, désigner un ou plusieurs comptes de test :
```sh
bundle exec ruby bin/initialize-core
```
En développement, un seul membre suffit. Cette commande peut être
réexécutée pour ajouter d'autres membres au Noyau.
Démarrer le serveur de développement : Démarrer le serveur de développement :
@@ -60,16 +69,28 @@ bundle exec irb -r dotenv/load -r ./netig.rb
Docker Compose fournit les variables d'environnement via `env_file` et définit Docker Compose fournit les variables d'environnement via `env_file` et définit
`RACK_ENV=production`. `RACK_ENV=production`.
Exécuter les migrations dans le conteneur applicatif avant de démarrer la Exécuter les migrations dans le conteneur applicatif avant de démarrer
nouvelle version : la nouvelle version :
```sh ```sh
bundle exec ruby bin/migrate bundle exec ruby bin/migrate
``` ```
Lors de la première installation, créer le premier administrateur depuis le ### Initialisation du premier Noyau
conteneur applicatif :
Une fois l'inscription publique disponible, les membres fondateurs créent
leurs comptes depuis le site et vérifient leurs adresses électroniques.
Après leur désignation par l'assemblée constitutive, initialiser le Noyau
depuis le conteneur applicatif :
```sh ```sh
bundle exec ruby bin/initialize-admin bundle exec ruby bin/initialize-core
``` ```
Cette commande doit être exécutée une seule fois en production.
Elle exige au moins trois personnes physiques disposant d'un compte vérifié.
Elle enregistre leurs adhésions comme acceptées et leur attribue la qualité
de membre du Noyau.
La date de l'assemblée constitutive est demandée lors de l'exécution.
+28 -19
View File
@@ -1,6 +1,11 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require "dotenv/load" if ENV.fetch("RACK_ENV", "development") == "development" environment = ENV.fetch("RACK_ENV", "development")
require "dotenv/load" if environment == "development"
abort "Ce script est réservé au développement." unless
ENV.fetch("RACK_ENV", "development") == "development"
require "io/console" require "io/console"
require "rodauth" require "rodauth"
@@ -8,16 +13,17 @@ require_relative "../lib/database"
console = IO.console or abort "Terminal interactif requis." console = IO.console or abort "Terminal interactif requis."
abort "Un administrateur existe déjà." if
DB[:members].where(admin: true).count.positive?
console.print "Adresse électronique : " console.print "Adresse électronique : "
email = console.gets&.strip email = console.gets&.strip&.downcase
abort "Adresse électronique obligatoire." if email.to_s.empty? abort "Adresse électronique obligatoire." if email.to_s.empty?
abort "Ce compte existe déjà." if existing_account = DB[:accounts]
DB[:accounts].where(email: email).count.positive? .where(Sequel.function(:lower, :email) => email)
.count
.positive?
abort "Ce compte existe déjà." if existing_account
console.print "Prénom : " console.print "Prénom : "
first_name = console.gets&.strip first_name = console.gets&.strip
@@ -31,16 +37,9 @@ abort "Prénom et nom obligatoires." if
password = console.getpass("Mot de passe : ") password = console.getpass("Mot de passe : ")
confirmation = console.getpass("Confirmer le mot de passe : ") confirmation = console.getpass("Confirmer le mot de passe : ")
abort "Mot de passe obligatoire." if password.empty? abort "Mot de passe obligatoire." if password.to_s.empty?
abort "Les mots de passe ne correspondent pas." unless password == confirmation abort "Les mots de passe ne correspondent pas." unless
password == confirmation
console.puts
console.puts "Administrateur : #{first_name} #{last_name} (#{email})"
console.print "Confirmer la création ? [o/N] "
abort "Opération annulée." unless %w[o oui].include?(
console.gets&.strip&.downcase
)
verified_status_id = DB[:account_statuses] verified_status_id = DB[:account_statuses]
.where(name: "Verified") .where(name: "Verified")
@@ -48,6 +47,15 @@ verified_status_id = DB[:account_statuses]
abort "Statut Verified introuvable." unless verified_status_id abort "Statut Verified introuvable." unless verified_status_id
console.puts
console.puts "Compte : #{first_name} #{last_name} (#{email})"
console.puts
console.print "Confirmer la création ? [o/N] "
abort "Opération annulée." unless %w[o oui].include?(
console.gets&.strip&.downcase
)
auth = Rodauth.lib do auth = Rodauth.lib do
db DB db DB
enable :create_account enable :create_account
@@ -73,8 +81,9 @@ DB.transaction do
first_name: first_name, first_name: first_name,
last_name: last_name, last_name: last_name,
organization_name: nil, organization_name: nil,
admin: true core_member: false
) )
end end
puts "Premier administrateur créé." puts "Compte de développement créé."
+169
View File
@@ -0,0 +1,169 @@
#!/usr/bin/env ruby
environment = ENV.fetch("RACK_ENV", "development")
require "dotenv/load" if environment == "development"
abort "Environnement non pris en charge." unless
%w[development production].include?(
ENV.fetch("RACK_ENV", "development")
)
require "date"
require "io/console"
require_relative "../lib/database"
console = IO.console or abort "Terminal interactif requis."
if environment == "production" &&
DB[:members].where(core_member: true).count.positive?
abort "Un Noyau existe déjà."
end
verified_status_id = DB[:account_statuses]
.where(name: "Verified")
.get(:id)
abort "Statut Verified introuvable." unless verified_status_id
find_member = lambda do |email|
account = DB[:accounts]
.where(Sequel.function(:lower, :email) => email)
.first
abort "Compte introuvable : #{email}" unless account
abort "Adresse électronique non vérifiée : #{email}" unless
account[:status_id] == verified_status_id
member = DB[:members]
.where(account_id: account[:id])
.first
abort "Fiche membre introuvable : #{email}" unless member
abort "Une personne physique est requise : #{email}" unless
member[:kind] == "individual"
member
end
console.print "Date de l'assemblée (AAAA-MM-JJ) : "
date_input = console.gets&.strip
unless date_input&.match?(/\A\d{4}-\d{2}-\d{2}\z/)
abort "Format attendu : AAAA-MM-JJ."
end
begin
assembly_date = Date.iso8601(date_input)
rescue Date::Error
abort "Date invalide."
end
abort "La date ne peut pas être dans le futur." if
assembly_date > Date.today
assembly_datetime = Time.utc(
assembly_date.year,
assembly_date.month,
assembly_date.day
)
console.puts
console.puts "Saisissez les adresses électroniques des membres désignés."
console.puts "Une adresse par ligne. Entrée pour terminer."
console.puts
emails = []
loop do
console.print "Adresse électronique : "
email = console.gets&.strip&.downcase
break if email.to_s.empty?
abort "Adresse déjà saisie : #{email}" if emails.include?(email)
emails << email
end
minimum_members = environment == "production" ? 3 : 1
abort "Au moins #{minimum_members} membre(s) sont nécessaires." if
emails.length < minimum_members
members = emails.map do |email|
[email, find_member.call(email)]
end
console.puts
console.print "Adresse de la personne qui enregistre la décision : "
recorded_by_email = console.gets&.strip&.downcase
abort "Adresse électronique obligatoire." if
recorded_by_email.to_s.empty?
recorded_by = find_member.call(recorded_by_email)
console.puts
console.puts "Environnement : #{environment}"
console.puts "Date de l'assemblée : #{assembly_date}"
console.puts
console.puts "Membres du Noyau :"
members.each do |email, member|
name = "#{member[:first_name]} #{member[:last_name]}"
console.puts " #{name} (#{email})"
end
console.puts
console.puts "Décision enregistrée par : #{recorded_by_email}"
console.puts
console.print "Confirmer l'initialisation ? [o/N] "
abort "Opération annulée." unless %w[o oui].include?(
console.gets&.strip&.downcase
)
if environment == "production" &&
DB[:members].where(core_member: true).count.positive?
abort "Un Noyau existe déjà."
end
DB.transaction do
members.each do |_email, member|
membership = DB[:memberships]
.where(
member_id: member[:id],
status: %w[pending accepted],
ended_at: nil
)
.first
if membership && membership[:status] == "pending"
DB[:memberships]
.where(id: membership[:id])
.update(
status: "accepted",
decided_at: assembly_datetime,
recorded_by: recorded_by[:id]
)
elsif membership.nil?
DB[:memberships].insert(
member_id: member[:id],
status: "accepted",
requested_at: assembly_datetime,
decided_at: assembly_datetime,
recorded_by: recorded_by[:id]
)
end
DB[:members]
.where(id: member[:id])
.update(core_member: true)
end
end
puts "Noyau initialisé (#{members.length} membres)."
+4 -1
View File
@@ -12,7 +12,7 @@ Sequel.migration do
String :last_name String :last_name
String :organization_name String :organization_name
TrueClass :admin, null: false, default: false TrueClass :core_member, null: false, default: false
index :account_id, unique: true index :account_id, unique: true
@@ -28,6 +28,9 @@ Sequel.migration do
"AND last_name IS NULL AND organization_name IS NOT NULL " \ "AND last_name IS NULL AND organization_name IS NOT NULL " \
"AND trim(organization_name) <> '')" "AND trim(organization_name) <> '')"
) )
constraint :members_core_member_individual,
Sequel.lit("core_member = FALSE OR kind = 'individual'")
end end
end end
+67 -50
View File
@@ -1,9 +1,9 @@
# Informations légales et conditions d’utilisation # Informations légales et conditions d’utilisation
NETig est un projet associatif régi par la loi du 1er juillet 1901. NETig est un projet associatif régi par la loi du 1er juillet 1901.
L’association n’a pas encore été déclarée en préfecture et ne dispose donc L’association n’a pas encore été déclarée en préfecture et ne dispose
pas encore de la personnalité morale ni de la capacité juridique attachée donc pas encore de la personnalité morale ni de la capacité juridique
à une association déclarée. Cette information sera mise à jour après la attachée à une association déclarée. Cette information sera mise à jour après la
déclaration et sa publication au Journal officiel des associations et déclaration et sa publication au Journal officiel des associations et
fondations d’entreprises (JOAFE). fondations d’entreprises (JOAFE).
@@ -30,30 +30,29 @@ Finlande.
Les services NETig sont proposés sans facturation liée à leur utilisation. Les services NETig sont proposés sans facturation liée à leur utilisation.
Certains sont accessibles au public ; d’autres sont réservés aux membres Certains sont accessibles au public ; d’autres sont réservés aux membres
à jour de leur cotisation. de l’association.
Conformément aux statuts, toute demande d’adhésion est examinée par le Noyau. Toute personne physique ou morale peut demander à adhérer à l’association,
L’adhésion prend effet après acceptation de la demande et paiement de la à condition d’accepter ses statuts et ses principes. Le Noyau examine les
cotisation annuelle fixée par l’Assemblée générale. Elle dure un an et se demandes et décide de l’admission. L’adhésion prend effet à la date de
renouvelle à chaque échéance si la cotisation est payée. La création d’un son acceptation et est valable sans limitation de durée. La création d’un compte
compte utilisateur, lorsqu’elle sera proposée, ne vaudra pas à elle seule utilisateur ne vaut pas, à elle seule, adhésion à l’association.
adhésion à l’association.
En cas d’impayé, une relance laisse trente jours pour régler la cotisation. Aucune cotisation ni aucun don n’est exigé pour adhérer, conserver la
Pendant ce délai, le membre n’est plus considéré comme à jour : il ne peut qualité de membre ou exercer les droits qui y sont attachés. Les dons sont
pas voter en Assemblée générale et ses accès aux services réservés aux libres, volontaires et sans contrepartie. NETig invite les membres et les
membres à jour peuvent être suspendus. À défaut de paiement à l’expiration utilisateurs qui le peuvent à soutenir financièrement l’association, selon
de ce délai, l’adhésion prend fin. leurs moyens. Cette participation contribue au financement des
infrastructures et à la pérennité des services, mais ne conditionne ni
Les dons sont libres et volontaires. Ils contribuent au financement des l’adhésion, ni les droits des membres, ni l’accès aux services et ne donne
infrastructures, ne remplacent pas la cotisation nécessaire à l’adhésion droit à aucun avantage particulier.
et ne donnent droit à aucun accès ou avantage particulier.
L’accès à chaque service dépend des ressources disponibles et peut être L’accès à chaque service dépend des ressources disponibles et peut être
limité. Une disponibilité permanente n’est pas garantie. L’état des services limité. Une disponibilité permanente n’est pas garantie. L’état des services
est consultable sur la [page de statut](https://status.netig.net/status/services). est consultable sur la page de statut :
Les interruptions de maintenance peuvent être annoncées par la [page de statut](https://status.netig.net/status/services). Les interruptions
[liste de diffusion dédiée](https://list.netig.net/subscription/form). de maintenance peuvent être annoncées par la liste de diffusion dédiée :
[liste de diffusion](https://list.netig.net/subscription/form).
### Utilisation des services ### Utilisation des services
@@ -71,11 +70,12 @@ propres copies lorsque cela est possible.
La personne responsable d’un service peut limiter ou suspendre un accès La personne responsable d’un service peut limiter ou suspendre un accès
de manière proportionnée en cas d’abus ou de risque pour la sécurité, de manière proportionnée en cas d’abus ou de risque pour la sécurité,
les données, le fonctionnement ou les ressources du service. Elle informe les données, le fonctionnement ou les ressources du service. Elle en
la personne concernée et le Noyau des motifs de la mesure. informe la personne concernée ainsi que le Noyau, en indiquant les motifs
de la mesure.
La personne concernée peut demander au Noyau de réexaminer cette mesure. La personne concernée peut demander au Noyau de réexaminer cette mesure.
Le Noyau répond sous quinze jours. Si le Noyau a pris ou confirmé la mesure, Celui-ci répond sous quinze jours. Si le Noyau a pris ou confirmé la mesure,
la personne concernée ou tout membre peut demander son réexamen par la personne concernée ou tout membre peut demander son réexamen par
l’Assemblée générale. La suspension d’un accès ne met pas fin à la qualité l’Assemblée générale. La suspension d’un accès ne met pas fin à la qualité
de membre. de membre.
@@ -101,13 +101,16 @@ la personne ou de la structure qui détermine actuellement les finalités
et les moyens des traitements]. et les moyens des traitements].
La présente notice décrit les traitements réalisés sur le site netig.net, La présente notice décrit les traitements réalisés sur le site netig.net,
notamment par son formulaire de contact. Les services hébergés sur d’autres notamment par son formulaire de contact et par les fonctions
adresses peuvent mettre en œuvre des traitements propres ; leurs modalités d’authentification réservées aux comptes créés par l’administration. Les
spécifiques doivent être présentées dans les services concernés. services hébergés sur d’autres adresses peuvent mettre en œuvre des
traitements propres ; leurs modalités spécifiques doivent être présentées
dans les services concernés.
À la date de cette notice, le site netig.net ne propose pas encore de À la date de cette notice, le site netig.net ne propose pas encore
création de compte utilisateur ni d’adhésion en ligne. Les informations d’inscription publique ni de demande d’adhésion en ligne. Ces fonctionnalités
relatives à ces fonctions seront ajoutées avant leur mise en service. et les traitements associés feront l’objet d’une mise à jour de la notice
avant leur mise en service.
### Données traitées et finalités ### Données traitées et finalités
@@ -115,10 +118,18 @@ Le formulaire de contact recueille le nom, l’adresse électronique, l’objet
et le contenu du message. Ces informations sont utilisées pour répondre et le contenu du message. Ces informations sont utilisées pour répondre
aux demandes et assurer le suivi des échanges. aux demandes et assurer le suivi des échanges.
Pour les comptes déjà créés, l’adresse électronique sert d’identifiant.
Une empreinte du mot de passe est conservée pour permettre
l’authentification ; le mot de passe n’est pas enregistré en clair. Les
informations d’identité associées au compte, lorsqu’elles sont renseignées,
peuvent comprendre le type de membre, le prénom, le nom ou la dénomination
de la personne morale. Ces données sont utilisées pour gérer les comptes
et l’administration du site.
Des données techniques nécessaires au fonctionnement et à la sécurité du Des données techniques nécessaires au fonctionnement et à la sécurité du
site peuvent également être traitées, notamment dans les journaux du site peuvent également être traitées, notamment les données de session et
serveur. Leur nature et leur durée de conservation doivent être adaptées les journaux du serveur. Leur nature et leur durée de conservation doivent
à leur utilité réelle. être adaptées à leur utilité réelle.
Les données ne sont ni vendues ni utilisées à des fins publicitaires. Les données ne sont ni vendues ni utilisées à des fins publicitaires.
@@ -126,16 +137,19 @@ Les données ne sont ni vendues ni utilisées à des fins publicitaires.
Les messages sont traités dans le cadre de l’intérêt légitime à répondre Les messages sont traités dans le cadre de l’intérêt légitime à répondre
aux sollicitations adressées au projet NETig et à assurer le suivi des aux sollicitations adressées au projet NETig et à assurer le suivi des
échanges. Les données techniques nécessaires à la sécurité sont traitées échanges. Les données nécessaires à l’authentification et à la gestion
dans l’intérêt légitime de protéger le site, les services et leurs des comptes sont traitées dans l’intérêt légitime de sécuriser
utilisateurs. l’administration du site. Les données techniques nécessaires à la sécurité
sont traitées dans l’intérêt légitime de protéger le site, les services
et leurs utilisateurs.
### Destinataires et hébergement ### Destinataires et hébergement
Les messages sont transmis à l’adresse [contact@netig.net](mailto:contact@netig.net) Les messages sont transmis à l’adresse
et peuvent être consultés par les personnes habilitées à répondre aux [contact@netig.net](mailto:contact@netig.net) et peuvent être consultés par
demandes. Les données techniques peuvent être accessibles aux personnes les personnes habilitées à répondre aux demandes. Les données des comptes
chargées de l’administration des infrastructures et aux prestataires et les données techniques peuvent être accessibles aux personnes habilitées
à administrer le site et les infrastructures, ainsi qu’aux prestataires
techniques concernés. techniques concernés.
L’hébergement principal est assuré par Hetzner Online GmbH. Des sauvegardes L’hébergement principal est assuré par Hetzner Online GmbH. Des sauvegardes
@@ -147,12 +161,15 @@ Les messages reçus sont conservés dans la messagerie le temps nécessaire
au traitement de la demande et au suivi des échanges, puis supprimés, au traitement de la demande et au suivi des échanges, puis supprimés,
sauf nécessité de conservation plus longue ou obligation légale. sauf nécessité de conservation plus longue ou obligation légale.
Les journaux techniques et les sauvegardes peuvent conserver certaines Les données des comptes sont conservées pendant la durée nécessaire à leur
données après leur suppression des systèmes actifs. Les durées de gestion et à la sécurisation du site. Les journaux techniques et les
conservation effectivement appliquées doivent être précisées avant sauvegardes peuvent conserver certaines données après leur suppression des
l’ouverture des services concernés : systèmes actifs. Les durées de conservation effectivement appliquées doivent
être précisées avant l’ouverture des services concernés :
- **Messages de contact :** [durée à définir]. - **Messages de contact :** [durée à définir].
- **Comptes et fiches associées :** [durée à définir].
- **Données de session :** [durée à définir].
- **Journaux techniques :** [durée à définir]. - **Journaux techniques :** [durée à définir].
- **Sauvegardes :** [durée de rétention à définir]. - **Sauvegardes :** [durée de rétention à définir].
@@ -163,15 +180,15 @@ l’accès à vos données, leur rectification ou leur effacement, la limitation
de leur traitement et vous opposer à certains traitements. Vous pouvez de leur traitement et vous opposer à certains traitements. Vous pouvez
également demander la portabilité lorsque ce droit s’applique. également demander la portabilité lorsque ce droit s’applique.
Pour exercer vos droits, écrivez à [contact@netig.net](mailto:contact@netig.net). Pour exercer vos droits, écrivez à
Vous pouvez également introduire une réclamation auprès de la [contact@netig.net](mailto:contact@netig.net). Vous pouvez également
[CNIL](https://www.cnil.fr/). introduire une réclamation auprès de la [CNIL](https://www.cnil.fr/).
### Cookies ### Cookies
Le site utilise des cookies techniques nécessaires au fonctionnement Le site utilise des cookies techniques nécessaires au fonctionnement
des sessions et à la sécurité. Ils ne sont pas utilisés à des fins de des sessions et à la sécurité, notamment pour l’authentification. Ils ne
publicité ou de suivi de navigation. sont pas utilisés à des fins de publicité ou de suivi de navigation.
## Contact ## Contact
+27
View File
@@ -0,0 +1,27 @@
class Netig
private
def require_core_member!(r)
rodauth.require_account
core_member = DB[:members]
.join(:memberships, member_id: :id)
.where(
Sequel[:members][:account_id] => rodauth.account_id,
Sequel[:members][:kind] => "individual",
Sequel[:members][:core_member] => true,
Sequel[:memberships][:status] => "accepted",
Sequel[:memberships][:ended_at] => nil
)
.count
.positive?
return if core_member
r.halt [
403,
{ "Content-Type" => "text/plain; charset=utf-8" },
["Accès interdit."]
]
end
end
+2 -1
View File
@@ -35,7 +35,8 @@ class Netig < Roda
logout_redirect "/" logout_redirect "/"
end end
require_relative "routes/admin" require_relative "lib/authorization"
require_relative "routes/noyau"
require_relative "routes/contact" require_relative "routes/contact"
route do |r| route do |r|
+5 -5
View File
@@ -490,23 +490,23 @@ summary:focus-visible {
/* Admin /* Admin
------------------------------------------------------------------------ */ ------------------------------------------------------------------------ */
.admin-table { .members-table {
width: 100%; width: 100%;
border-collapse: collapse; border-collapse: collapse;
text-align: left; text-align: left;
} }
.admin-table th, .members-table th,
.admin-table td { .members-table td {
padding: 10px 16px; padding: 10px 16px;
border-bottom: 1px solid var(--color-shadow-light); border-bottom: 1px solid var(--color-shadow-light);
} }
.admin-table th { .members-table th {
color: var(--color-highlight); color: var(--color-highlight);
} }
.admin-table td { .members-table td {
vertical-align: top; vertical-align: top;
} }
-51
View File
@@ -1,51 +0,0 @@
class Netig
hash_branch "admin" do |r|
require_admin!(r)
@page_title = "Admin — NETig"
@page_description = "Interface d'administration de NETig."
@canonical_url = "https://netig.net/admin"
r.is do
r.get do
view "admin"
end
end
r.on "members" do
r.is do
r.get do
@page_title = "Adhérents — Admin — NETig"
@members = DB[:members]
.join(:accounts, id: :account_id)
.select_all(:members)
.select_append(Sequel[:accounts][:email].as(:email))
.order(Sequel[:members][:id])
.all
view "admin/members"
end
end
end
end
private
def require_admin!(r)
rodauth.require_account
admin = DB[:members].where(
account_id: rodauth.account_id,
admin: true
).get(:admin)
return if admin
r.halt [
403,
{ "Content-Type" => "text/plain; charset=utf-8" },
["Accès interdit."]
]
end
end
+34
View File
@@ -0,0 +1,34 @@
class Netig
hash_branch "noyau" do |r|
require_core_member!(r)
@page_title = "Noyau — NETig"
@page_description = "Espace de gestion de l'association NETig."
@canonical_url = "https://netig.net/noyau"
r.is do
r.get do
view "noyau"
end
end
r.on "members" do
r.is do
r.get do
@page_title = "Adhérents — Noyau — NETig"
@members = DB[:members]
.join(:accounts, id: :account_id)
.select_all(:members)
.select_append(Sequel[:accounts][:email].as(:email))
.order(Sequel[:members][:id])
.all
view "noyau/members"
end
end
end
end
end
+1 -2
View File
@@ -113,8 +113,7 @@
<section id="association" class="section-association"> <section id="association" class="section-association">
<div class="content"> <div class="content">
<h2>Aidez-nous à continuer !</h2> <h2>Aidez-nous à continuer !</h2>
<p>Les services sont financés grâce à vos adhésions et vos dons. Votre soutien nous aide à couvrir les frais d'hébergement et à maintenir les services accessibles.</p> <p>Les services ne sont financés que par vos dons. Votre soutien nous aide à couvrir les frais d'hébergement et à maintenir les services accessibles.</p>
<a class="button" href="/temporary_page">Adhérer ➜</a>
<a class="button" href="/temporary_page">Faire un don ➜</a> <a class="button" href="/temporary_page">Faire un don ➜</a>
</div> </div>
</section> </section>
+2 -2
View File
@@ -1,9 +1,9 @@
<section class="page-section"> <section class="page-section">
<div class="content"> <div class="content">
<h1>Administration de NETig</h1> <h1>Espace du Noyau</h1>
<p> <p>
<a class="button" href="/admin/members"> <a class="button" href="/noyau/members">
Gérer les adhérents Gérer les adhérents
</a> </a>
</p> </p>
@@ -5,13 +5,12 @@
<% if @members.empty? %> <% if @members.empty? %>
<p>Aucun adhérent enregistré.</p> <p>Aucun adhérent enregistré.</p>
<% else %> <% else %>
<table class="admin-table"> <table class="members-table">
<thead> <thead>
<tr> <tr>
<th>Identité</th> <th>Identité</th>
<th>Type</th> <th>Type</th>
<th>Adresse électronique</th> <th>Adresse électronique</th>
<th>Administration</th>
</tr> </tr>
</thead> </thead>
@@ -28,7 +27,6 @@
"Personne physique" : "Personne morale" %> "Personne physique" : "Personne morale" %>
</td> </td>
<td><%= member[:email] %></td> <td><%= member[:email] %></td>
<td><%= member[:admin] ? "Oui" : "Non" %></td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
@@ -36,3 +34,4 @@
<% end %> <% end %>
</div> </div>
</section> </section>