Compare commits
2 commits
main
...
kerl/group
Author | SHA1 | Date | |
---|---|---|---|
|
6a6b5a1b97 | ||
|
811f5cb53a |
36 changed files with 299 additions and 943 deletions
|
@ -1 +0,0 @@
|
||||||
insecure-secret_key
|
|
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
||||||
use nix
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,4 +3,3 @@ venv
|
||||||
.*.swp
|
.*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
.direnv
|
|
||||||
|
|
1
WikiENS/.gitignore
vendored
Normal file
1
WikiENS/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
settings.py
|
BIN
WikiENS/__pycache__/__init__.cpython-36.pyc
Normal file
BIN
WikiENS/__pycache__/__init__.cpython-36.pyc
Normal file
Binary file not shown.
BIN
WikiENS/__pycache__/settings.cpython-36.pyc
Normal file
BIN
WikiENS/__pycache__/settings.cpython-36.pyc
Normal file
Binary file not shown.
BIN
WikiENS/__pycache__/urls.cpython-36.pyc
Normal file
BIN
WikiENS/__pycache__/urls.cpython-36.pyc
Normal file
Binary file not shown.
BIN
WikiENS/__pycache__/wsgi.cpython-36.pyc
Normal file
BIN
WikiENS/__pycache__/wsgi.cpython-36.pyc
Normal file
Binary file not shown.
1
WikiENS/settings/.gitignore
vendored
Normal file
1
WikiENS/settings/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
secret.py
|
0
WikiENS/settings/__init__.py
Normal file
0
WikiENS/settings/__init__.py
Normal file
|
@ -1,34 +1,42 @@
|
||||||
"""
|
import os
|
||||||
Django settings for the wiki_ens project
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
from django.contrib.messages import constants as messages
|
from django.contrib.messages import constants as messages
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from loadcredential import Credentials
|
|
||||||
|
|
||||||
credentials = Credentials(env_prefix="WIKIENS_")
|
try:
|
||||||
|
from . import secret
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
except ImportError:
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
raise ImportError(
|
||||||
|
"The secret.py file is missing.\n"
|
||||||
# WARNING: keep the secret key used in production secret!
|
"For a development environment, simply copy secret_example.py"
|
||||||
SECRET_KEY = credentials["SECRET_KEY"]
|
)
|
||||||
|
|
||||||
# WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = credentials.get_json("DEBUG", False)
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = credentials.get_json("ALLOWED_HOSTS", [])
|
|
||||||
|
|
||||||
ADMINS = credentials.get_json("ADMINS", [])
|
|
||||||
|
|
||||||
SITE_ID = 1
|
|
||||||
|
|
||||||
|
|
||||||
###
|
def import_secret(name):
|
||||||
# List the installed applications
|
"""
|
||||||
|
Shorthand for importing a value from the secret module and raising an
|
||||||
|
informative exception if a secret is missing.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return getattr(secret, name)
|
||||||
|
except AttributeError:
|
||||||
|
raise RuntimeError("Secret missing: {}".format(name))
|
||||||
|
|
||||||
|
|
||||||
|
SECRET_KEY = import_secret("SECRET_KEY")
|
||||||
|
ADMINS = import_secret("ADMINS")
|
||||||
|
MANAGERS = ADMINS
|
||||||
|
EMAIL_HOST = import_secret("EMAIL_HOST")
|
||||||
|
|
||||||
|
DBNAME = import_secret("DBNAME")
|
||||||
|
DBUSER = import_secret("DBUSER")
|
||||||
|
DBPASSWD = import_secret("DBPASSWD")
|
||||||
|
|
||||||
|
SERVER_EMAIL = "wiki@www.eleves.ens.fr"
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
|
@ -58,10 +66,6 @@ INSTALLED_APPS = [
|
||||||
"allauth_ens.providers.clipper",
|
"allauth_ens.providers.clipper",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
# List the installed middlewares
|
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
|
@ -70,21 +74,9 @@ MIDDLEWARE = [
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
"allauth.account.middleware.AccountMiddleware",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ROOT_URLCONF = "WikiENS.urls"
|
||||||
###
|
|
||||||
# The main url configuration
|
|
||||||
|
|
||||||
ROOT_URLCONF = "app.urls"
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
# Template configuration:
|
|
||||||
# - Django Templating Language is used
|
|
||||||
# - Application directories can be used
|
|
||||||
|
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
|
@ -98,72 +90,65 @@ TEMPLATES = [
|
||||||
"django.contrib.auth.context_processors.auth",
|
"django.contrib.auth.context_processors.auth",
|
||||||
"django.contrib.messages.context_processors.messages",
|
"django.contrib.messages.context_processors.messages",
|
||||||
"sekizai.context_processors.sekizai",
|
"sekizai.context_processors.sekizai",
|
||||||
],
|
]
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
WSGI_APPLICATION = "WikiENS.wsgi.application"
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
"NAME": DBNAME,
|
||||||
|
"USER": DBUSER,
|
||||||
|
"PASSWORD": DBPASSWD,
|
||||||
|
"HOST": "localhost",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Password validation
|
||||||
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
"NAME": (
|
||||||
|
# XXX. Cette chaîne est très longue… Je la coupe en deux sinon
|
||||||
|
# black ne me fiche pas la paix (mais c'est vraiment nul)
|
||||||
|
"django.contrib.auth.password_validation."
|
||||||
|
"UserAttributeSimilarityValidator"
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
||||||
|
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
###
|
# Internationalization
|
||||||
# Database configuration
|
# https://docs.djangoproject.com/en/1.11/topics/i18n/
|
||||||
# -> https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|
||||||
|
|
||||||
DATABASES = credentials.get_json(
|
|
||||||
"DATABASES",
|
|
||||||
{
|
|
||||||
"default": {
|
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
|
||||||
"NAME": BASE_DIR / "db.sqlite3",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
CACHES = credentials.get_json(
|
|
||||||
"CACHES",
|
|
||||||
default={
|
|
||||||
"default": {
|
|
||||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
# WSGI application configuration
|
|
||||||
|
|
||||||
WSGI_APPLICATION = "app.wsgi.application"
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
# Staticfiles configuration
|
|
||||||
|
|
||||||
STATIC_ROOT = credentials["STATIC_ROOT"]
|
|
||||||
STATIC_URL = "/static/"
|
|
||||||
|
|
||||||
MEDIA_ROOT = credentials.get("MEDIA_ROOT", BASE_DIR / "media")
|
|
||||||
MEDIA_URL = "/media/"
|
|
||||||
|
|
||||||
|
|
||||||
###
|
|
||||||
# Internationalization configuration
|
|
||||||
# -> https://docs.djangoproject.com/en/4.2/topics/i18n/
|
|
||||||
|
|
||||||
LANGUAGE_CODE = "fr-fr"
|
LANGUAGE_CODE = "fr-fr"
|
||||||
|
|
||||||
TIME_ZONE = "Europe/Paris"
|
TIME_ZONE = "Europe/Paris"
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
LANGUAGES = [
|
|
||||||
("fr", _("Français")),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
# Authentication
|
||||||
###
|
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth
|
||||||
# Authentication configuration
|
# https://django-allauth.readthedocs.io/en/latest/index.html
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
"allauth.account.auth_backends.AuthenticationBackend",
|
"allauth.account.auth_backends.AuthenticationBackend",
|
||||||
|
@ -195,18 +180,14 @@ SOCIALACCOUNT_PROVIDERS = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
|
||||||
{"NAME": f"django.contrib.auth.password_validation.{v}"}
|
|
||||||
for v in [
|
|
||||||
"UserAttributeSimilarityValidator",
|
|
||||||
"MinimumLengthValidator",
|
|
||||||
"CommonPasswordValidator",
|
|
||||||
"NumericPasswordValidator",
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
###
|
# Static / media contents
|
||||||
# Wiki configuration
|
|
||||||
|
STATIC_URL = "/_static/"
|
||||||
|
MEDIA_URL = "/_media/"
|
||||||
|
|
||||||
|
|
||||||
|
# WIKI SETTINGS
|
||||||
|
|
||||||
WIKI_ATTACHMENTS_EXTENSIONS = [
|
WIKI_ATTACHMENTS_EXTENSIONS = [
|
||||||
"pdf",
|
"pdf",
|
||||||
|
@ -228,6 +209,12 @@ WIKI_ATTACHMENTS_EXTENSIONS = [
|
||||||
WIKI_REVISIONS_PER_HOUR = 180
|
WIKI_REVISIONS_PER_HOUR = 180
|
||||||
WIKI_REVISIONS_PER_MINUTES = 180
|
WIKI_REVISIONS_PER_MINUTES = 180
|
||||||
|
|
||||||
|
# Dark magic - tell django to use X-Forwarded-*
|
||||||
|
# This is needed for django-allauth-cas, see
|
||||||
|
# https://blog.ubuntu.com/2015/08/18/django-behind-a-proxy-fixing-absolute-urls
|
||||||
|
USE_X_FORWARDED_HOST = True
|
||||||
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
# Use sign up, login, logout, profile settings views of allauth.
|
# Use sign up, login, logout, profile settings views of allauth.
|
||||||
WIKI_ACCOUNT_HANDLING = False
|
WIKI_ACCOUNT_HANDLING = False
|
||||||
|
|
||||||
|
@ -239,10 +226,3 @@ WIKI_ACCOUNT_SIGNUP_ALLOWED = True
|
||||||
# will be treated as the others_write boolean field on models.Article.
|
# will be treated as the others_write boolean field on models.Article.
|
||||||
WIKI_ANONYMOUS_WRITE = False
|
WIKI_ANONYMOUS_WRITE = False
|
||||||
WIKI_ANONYMOUS = False
|
WIKI_ANONYMOUS = False
|
||||||
|
|
||||||
|
|
||||||
# FIXME: Add correct email settings
|
|
||||||
|
|
||||||
# Development settings
|
|
||||||
if DEBUG:
|
|
||||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
|
16
WikiENS/settings/local.py
Normal file
16
WikiENS/settings/local.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
from .common import * # noqa
|
||||||
|
from .common import BASE_DIR
|
||||||
|
|
||||||
|
|
||||||
|
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
|
||||||
|
}
|
||||||
|
}
|
11
WikiENS/settings/prod.py
Normal file
11
WikiENS/settings/prod.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from .common import * # noqa
|
||||||
|
from .common import BASE_DIR
|
||||||
|
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
ALLOWED_HOSTS = ["www.eleves.ens.fr", "wiki.eleves.ens.fr"]
|
||||||
|
|
||||||
|
STATIC_ROOT = os.path.join(BASE_DIR, "..", "static")
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, "..", "media")
|
7
WikiENS/settings/secret_example.py
Normal file
7
WikiENS/settings/secret_example.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
SECRET_KEY = "_u5q4-^1qgkqg=i5o5ha*xkd@82#l$e+%m)$v+4y#t-5!g-%g2"
|
||||||
|
ADMINS = None
|
||||||
|
EMAIL_HOST = "localhost"
|
||||||
|
|
||||||
|
DBNAME = "wiki"
|
||||||
|
DBUSER = "wiki"
|
||||||
|
DBPASSWD = "dummy"
|
23
WikiENS/urls.py
Normal file
23
WikiENS/urls.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from django.conf.urls import url, include
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from allauth_ens.views import capture_login, capture_logout
|
||||||
|
from wiki.urls import get_pattern as get_wiki_pattern
|
||||||
|
from django_nyt.urls import get_pattern as get_nyt_pattern
|
||||||
|
|
||||||
|
allauth_urls = [
|
||||||
|
# Catch login/logout views of admin site.
|
||||||
|
url(r'^_admin/login/$', capture_login),
|
||||||
|
url(r'^_admin/logout/$', capture_logout),
|
||||||
|
# Allauth urls.
|
||||||
|
url(r'^_profil/', include('allauth.urls')),
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns = allauth_urls + [
|
||||||
|
url(r'^_admin/', admin.site.urls),
|
||||||
|
url(r'^notifications/', get_nyt_pattern()),
|
||||||
|
url(r'^_groups/', include("wiki_groups.urls")),
|
||||||
|
url(r'', get_wiki_pattern()),
|
||||||
|
]
|
||||||
|
|
||||||
|
# TODO add MEDIA_ROOT
|
|
@ -11,6 +11,6 @@ import os
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "WikiENS.settings")
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
20
app/urls.py
20
app/urls.py
|
@ -1,20 +0,0 @@
|
||||||
from allauth_ens.views import capture_login, capture_logout
|
|
||||||
from django.contrib import admin
|
|
||||||
from django.urls import include, path
|
|
||||||
|
|
||||||
allauth_urls = [
|
|
||||||
# Catch login/logout views of admin site.
|
|
||||||
path("admin/login/", capture_login),
|
|
||||||
path("admin/logout/", capture_logout),
|
|
||||||
# Allauth urls.
|
|
||||||
path("profil/", include("allauth.urls")),
|
|
||||||
]
|
|
||||||
|
|
||||||
urlpatterns = allauth_urls + [
|
|
||||||
path("_admin/", admin.site.urls),
|
|
||||||
path("notifications/", include("django_nyt.urls")),
|
|
||||||
path("_groups/", include("wiki_groups.urls")),
|
|
||||||
path("", include("wiki.urls")),
|
|
||||||
]
|
|
||||||
|
|
||||||
# TODO add MEDIA_ROOT
|
|
45
default.nix
45
default.nix
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
sources ? import ./npins,
|
|
||||||
pkgs ? import sources.nixpkgs { },
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
nix-pkgs = import sources.nix-pkgs { inherit pkgs; };
|
|
||||||
|
|
||||||
python3 = pkgs.python3.override {
|
|
||||||
packageOverrides = _: _: {
|
|
||||||
inherit (nix-pkgs) django-allauth-ens django-wiki loadcredential;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
devShell = pkgs.mkShell {
|
|
||||||
name = "annuaire.dev";
|
|
||||||
|
|
||||||
packages = [
|
|
||||||
(python3.withPackages (ps: [
|
|
||||||
ps.django
|
|
||||||
ps.django-allauth-ens
|
|
||||||
ps.django-wiki
|
|
||||||
ps.loadcredential
|
|
||||||
ps.tinycss2
|
|
||||||
]))
|
|
||||||
];
|
|
||||||
|
|
||||||
env = {
|
|
||||||
DJANGO_SETTINGS_MODULE = "app.settings";
|
|
||||||
|
|
||||||
CREDENTIALS_DIRECTORY = builtins.toString ./.credentials;
|
|
||||||
|
|
||||||
WIKIENS_DEBUG = builtins.toJSON true;
|
|
||||||
WIKIENS_STATIC_ROOT = builtins.toString ./.static;
|
|
||||||
};
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
if [ ! -d .static ]; then
|
|
||||||
mkdir .static
|
|
||||||
fi
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "WikiENS.settings.local")
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
# Generated by npins. Do not modify; will be overwritten regularly
|
|
||||||
let
|
|
||||||
data = builtins.fromJSON (builtins.readFile ./sources.json);
|
|
||||||
version = data.version;
|
|
||||||
|
|
||||||
mkSource =
|
|
||||||
spec:
|
|
||||||
assert spec ? type;
|
|
||||||
let
|
|
||||||
path =
|
|
||||||
if spec.type == "Git" then
|
|
||||||
mkGitSource spec
|
|
||||||
else if spec.type == "GitRelease" then
|
|
||||||
mkGitSource spec
|
|
||||||
else if spec.type == "PyPi" then
|
|
||||||
mkPyPiSource spec
|
|
||||||
else if spec.type == "Channel" then
|
|
||||||
mkChannelSource spec
|
|
||||||
else
|
|
||||||
builtins.throw "Unknown source type ${spec.type}";
|
|
||||||
in
|
|
||||||
spec // { outPath = path; };
|
|
||||||
|
|
||||||
mkGitSource =
|
|
||||||
{
|
|
||||||
repository,
|
|
||||||
revision,
|
|
||||||
url ? null,
|
|
||||||
hash,
|
|
||||||
branch ? null,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
assert repository ? type;
|
|
||||||
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
|
|
||||||
# In the latter case, there we will always be an url to the tarball
|
|
||||||
if url != null then
|
|
||||||
(builtins.fetchTarball {
|
|
||||||
inherit url;
|
|
||||||
sha256 = hash; # FIXME: check nix version & use SRI hashes
|
|
||||||
})
|
|
||||||
else
|
|
||||||
assert repository.type == "Git";
|
|
||||||
let
|
|
||||||
urlToName =
|
|
||||||
url: rev:
|
|
||||||
let
|
|
||||||
matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url;
|
|
||||||
|
|
||||||
short = builtins.substring 0 7 rev;
|
|
||||||
|
|
||||||
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
|
|
||||||
in
|
|
||||||
"${if matched == null then "source" else builtins.head matched}${appendShort}";
|
|
||||||
name = urlToName repository.url revision;
|
|
||||||
in
|
|
||||||
builtins.fetchGit {
|
|
||||||
url = repository.url;
|
|
||||||
rev = revision;
|
|
||||||
inherit name;
|
|
||||||
# hash = hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
mkPyPiSource =
|
|
||||||
{ url, hash, ... }:
|
|
||||||
builtins.fetchurl {
|
|
||||||
inherit url;
|
|
||||||
sha256 = hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
mkChannelSource =
|
|
||||||
{ url, hash, ... }:
|
|
||||||
builtins.fetchTarball {
|
|
||||||
inherit url;
|
|
||||||
sha256 = hash;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
if version == 3 then
|
|
||||||
builtins.mapAttrs (_: mkSource) data.pins
|
|
||||||
else
|
|
||||||
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
|
|
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"pins": {
|
|
||||||
"nix-pkgs": {
|
|
||||||
"type": "Git",
|
|
||||||
"repository": {
|
|
||||||
"type": "Git",
|
|
||||||
"url": "https://git.hubrecht.ovh/hubrecht/nix-pkgs"
|
|
||||||
},
|
|
||||||
"branch": "main",
|
|
||||||
"revision": "6f56463c0034d4162dabb98ee8e70d6c43214ac0",
|
|
||||||
"url": null,
|
|
||||||
"hash": "0dqm2n88f0yl63wacizwpjrcv51arz5z31nhwbjcbyjxrwiwxamq"
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"type": "Channel",
|
|
||||||
"name": "nixpkgs-unstable",
|
|
||||||
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-24.11pre694416.ccc0c2126893/nixexprs.tar.xz",
|
|
||||||
"hash": "0cn1z4wzps8nfqxzr6l5mbn81adcqy2cy2ic70z13fhzicmxfsbx"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"version": 3
|
|
||||||
}
|
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Django==2.2.*
|
||||||
|
git+https://git.eleves.ens.fr/klub-dev-ens/django-allauth-ens.git@1.1.3
|
||||||
|
wiki==0.7
|
3
requirements_prod.txt
Normal file
3
requirements_prod.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
-r requirements.txt
|
||||||
|
psycopg2
|
||||||
|
gunicorn
|
|
@ -1,5 +1,6 @@
|
||||||
{% extends "wiki/base_site.html" %}
|
{% extends "wiki/base_site.html" %}
|
||||||
{% load i18n static sekizai_tags %}
|
{% load i18n staticfiles %}
|
||||||
|
{% load sekizai_tags %}
|
||||||
|
|
||||||
{% block wiki_site_title %} - WikiENS{% endblock %}
|
{% block wiki_site_title %} - WikiENS{% endblock %}
|
||||||
|
|
||||||
|
@ -10,49 +11,49 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block wiki_header_navlinks %}
|
{% block wiki_header_navlinks %}
|
||||||
<ul class="navbar-nav mr-auto">
|
<ul class="nav navbar-nav">
|
||||||
<li class="nav-item">
|
<li class="active"><a href="{% url 'wiki:root' %}">Accueil</a></li>
|
||||||
<a class="nav-link text-primary" href="{% url 'wiki:root' %}">Accueil</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="navbar-right">
|
<div class="navbar-right">
|
||||||
<ul class="navbar-nav">
|
<ul class="nav navbar-nav">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<li class="nav-item dropdown">
|
<li class="dropdown">
|
||||||
<a class="nav-link dropdown-toggle" href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
{% trans "Paramètres de compte" %}
|
{% trans "Paramètres de compte" %}
|
||||||
|
<b class="caret"></b>
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<a class="dropdown-item" href="{% url 'account_email' %}">
|
<li>
|
||||||
<i class="fa fa-envelope"></i>
|
<a href="{% url "account_email" %}">
|
||||||
{% trans "Email" %}
|
<i class="fa fa-envelope"></i>
|
||||||
</a>
|
{% trans "Email" %}
|
||||||
<a class="dropdown-item" href="{% url 'account_change_password' %}">
|
</a>
|
||||||
<i class="fa fa-lock"></i>
|
</li>
|
||||||
{% trans "Mot de passe" %}
|
<li>
|
||||||
</a>
|
<a href="{% url "account_change_password" %}">
|
||||||
<a class="dropdown-item" href="{% url 'socialaccount_connections' %}" title="Clipper…">
|
<i class="fa fa-lock"></i>
|
||||||
<i class="fa fa-sign-in-alt"></i>
|
{% trans "Mot de passe" %}
|
||||||
{% trans "Connexions par tiers" %}
|
</a>
|
||||||
</a>
|
</li>
|
||||||
{% if request.user.is_staff or request.user.managed_groups.exists %}
|
<li>
|
||||||
<div class="dropdown-divider"></div>
|
<a href="{% url "socialaccount_connections" %}" title="Clipper…">
|
||||||
<a class="dropdown-item" href="{% url 'wiki_groups:managed-groups' %}">
|
<i class="fa fa-sign-in"></i>
|
||||||
<i class="fa fa-cog"></i>
|
{% trans "Connexions par tiers" %}
|
||||||
{% trans "Liste des groupes gérés" %}
|
</a>
|
||||||
</a>
|
</li>
|
||||||
{% endif %}
|
<li class="divider"></span>
|
||||||
<div class="dropdown-divider"></div>
|
<li>
|
||||||
<a class="dropdown-item" href="{% url 'account_logout' %}">
|
<a href="{% url "account_logout" %}">
|
||||||
<i class="fa fa-power-off"></i>
|
<i class="fa fa-power-off"></i>
|
||||||
{% trans "Déconnexion" %}
|
{% trans "Déconnexion" %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class="nav-item">
|
<li>
|
||||||
<a class="nav-link" href="{% url 'account_signup' %}">{% trans "Sign Up" %}</a>
|
<a href="{% url "account_signup" %}">{% trans "Sign Up" %}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -61,5 +62,5 @@
|
||||||
|
|
||||||
{% block wiki_footer_logo %}
|
{% block wiki_footer_logo %}
|
||||||
<a href="http://www.eleves.ens.fr" class="pull-right"><img height="100px" src="{% static 'img/logoEleves.png' %}" /></a>
|
<a href="http://www.eleves.ens.fr" class="pull-right"><img height="100px" src="{% static 'img/logoEleves.png' %}" /></a>
|
||||||
<p>Wiki maintenu par les élèves de l'École normale supérieure. <br /> En cas de pépin contacter <tt>klub-dev [chez] ens [point] fr</tt></p>
|
<p>Wiki maintenu par les élèves de l'École normale supérieure. <br /> En cas de pépin contacter <tt>cof-geek [chez] ens [point] fr</tt></p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
(import ./. { }).devShell
|
|
|
@ -0,0 +1 @@
|
||||||
|
default_app_config = "wiki_groups.apps.WikiGroupsConfig"
|
|
@ -1,52 +0,0 @@
|
||||||
from django import forms
|
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
|
|
||||||
from wiki_groups.models import WikiGroup
|
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class SelectUserForm(forms.Form):
|
|
||||||
user = forms.CharField(max_length=150)
|
|
||||||
|
|
||||||
def clean_user(self):
|
|
||||||
user = User.objects.filter(username=self.cleaned_data["user"]).first()
|
|
||||||
|
|
||||||
if user is None:
|
|
||||||
self.add_error(
|
|
||||||
"user", "Aucune utilisatrice ou utilisateur avec ce login n'existe."
|
|
||||||
)
|
|
||||||
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
class SelectGroupForm(forms.Form):
|
|
||||||
group = forms.CharField(max_length=150)
|
|
||||||
|
|
||||||
def clean_group(self):
|
|
||||||
group = WikiGroup.objects.filter(
|
|
||||||
django_group__name=self.cleaned_data["group"]
|
|
||||||
).first()
|
|
||||||
|
|
||||||
if group is None:
|
|
||||||
self.add_error("group", "Aucun groupe avec ce nom n'existe.")
|
|
||||||
|
|
||||||
return group
|
|
||||||
|
|
||||||
|
|
||||||
class CreateGroupForm(forms.Form):
|
|
||||||
group = forms.CharField(max_length=150)
|
|
||||||
|
|
||||||
def clean_group(self):
|
|
||||||
name = self.cleaned_data["group"]
|
|
||||||
|
|
||||||
django_group, created = Group.objects.get_or_create(name=name)
|
|
||||||
|
|
||||||
if hasattr(django_group, "wikigroup"):
|
|
||||||
self.add_error("group", "Un groupe avec ce nom existe déjà.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
group = WikiGroup.objects.create(django_group=django_group)
|
|
||||||
|
|
||||||
return group
|
|
|
@ -1,20 +0,0 @@
|
||||||
# Generated by Django 2.2.19 on 2021-07-23 09:01
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
('wiki_groups', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='wikigroup',
|
|
||||||
name='managers',
|
|
||||||
field=models.ManyToManyField(blank=True, related_name='managed_groups', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,14 +1,12 @@
|
||||||
from django.contrib.auth import get_user_model
|
|
||||||
from django.contrib.auth.models import Group as DjangoGroup
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.signals import m2m_changed, post_save
|
from django.contrib.auth.models import Group as DjangoGroup
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.db.models.signals import post_save, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
class WikiGroup(models.Model):
|
class WikiGroup(models.Model):
|
||||||
"""A structured user group, used to grant permissions on sub-wikis
|
""" A structured user group, used to grant permissions on sub-wikis
|
||||||
|
|
||||||
This model contains a structured group of users, in the sense that a group contains
|
This model contains a structured group of users, in the sense that a group contains
|
||||||
both users and other groups, allowing a DAG group structure.
|
both users and other groups, allowing a DAG group structure.
|
||||||
|
@ -22,8 +20,8 @@ class WikiGroup(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class CyclicStructureException(Exception):
|
class CyclicStructureException(Exception):
|
||||||
"""Exception raised when a new edge introduces a cycle in the groups'
|
""" Exception raised when a new edge introduces a cycle in the groups'
|
||||||
structure"""
|
structure """
|
||||||
|
|
||||||
def __init__(self, from_group, to_group):
|
def __init__(self, from_group, to_group):
|
||||||
self.from_group = from_group
|
self.from_group = from_group
|
||||||
|
@ -42,54 +40,22 @@ class WikiGroup(models.Model):
|
||||||
related_name="included_in_groups",
|
related_name="included_in_groups",
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
users = models.ManyToManyField(User, blank=True)
|
users = models.ManyToManyField(get_user_model(), blank=True)
|
||||||
managers = models.ManyToManyField(User, related_name="managed_groups", blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.django_group)
|
return str(self.django_group)
|
||||||
|
|
||||||
def get_all_users(self):
|
def get_all_users(self):
|
||||||
"""Get the queryset of all the users in this group, including recursively
|
""" Get the queryset of all the users in this group, including recursively
|
||||||
included users"""
|
included users """
|
||||||
users_set = self.users.all()
|
users_set = self.users.all()
|
||||||
for subgroup in self.includes_groups.all():
|
for subgroup in self.includes_groups.all():
|
||||||
users_set = users_set.union(subgroup.get_all_users())
|
users_set = users_set.union(subgroup.get_all_users())
|
||||||
return users_set
|
return users_set
|
||||||
|
|
||||||
def is_manager(self, user):
|
|
||||||
"""Checks wether the user is a manager of this group or any subgroup"""
|
|
||||||
|
|
||||||
# Base case: the user is an explicit manager
|
|
||||||
if user in self.managers.all():
|
|
||||||
return True
|
|
||||||
|
|
||||||
for subgroup in self.includes_groups.all():
|
|
||||||
# If the user is a manager of a subgroup
|
|
||||||
if subgroup.is_manager(user):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_all_groups(self, already_notified=None):
|
|
||||||
"""Returns the set of metagroups i.e. self plus the list of groups including
|
|
||||||
this group recursively"""
|
|
||||||
|
|
||||||
if already_notified is None:
|
|
||||||
already_notified = set()
|
|
||||||
elif self.pk in already_notified:
|
|
||||||
return set()
|
|
||||||
already_notified.add(self.pk)
|
|
||||||
|
|
||||||
groups = {self}
|
|
||||||
|
|
||||||
for metagroup in self.included_in_groups.all():
|
|
||||||
groups |= metagroup.get_all_groups(already_notified=already_notified)
|
|
||||||
|
|
||||||
return groups
|
|
||||||
|
|
||||||
def propagate_update(self, already_notified=None):
|
def propagate_update(self, already_notified=None):
|
||||||
"""Commits itself to the Django group, and calls this method on every group in
|
""" Commits itself to the Django group, and calls this method on every group in
|
||||||
`included_in_groups`"""
|
`included_in_groups` """
|
||||||
|
|
||||||
# Check that we did not already propagate the update signal to this group
|
# Check that we did not already propagate the update signal to this group
|
||||||
if already_notified is None:
|
if already_notified is None:
|
||||||
|
@ -103,20 +69,20 @@ class WikiGroup(models.Model):
|
||||||
metagroup.propagate_update(already_notified=already_notified)
|
metagroup.propagate_update(already_notified=already_notified)
|
||||||
|
|
||||||
def commit_to_django_group(self):
|
def commit_to_django_group(self):
|
||||||
"""Writes this model's data to the related Django group"""
|
""" Writes this model's data to the related Django group """
|
||||||
|
|
||||||
self.django_group.user_set.set(self.get_all_users())
|
self.django_group.user_set.set(self.get_all_users())
|
||||||
|
|
||||||
def group_in_cycle(self, with_children):
|
def group_in_cycle(self, with_children):
|
||||||
"""Checks whether this group would be in a group cycle if it had
|
""" Checks whether this group would be in a group cycle if it had
|
||||||
`with_children` as child nodes. This assumes that the graph currently stored is
|
`with_children` as child nodes. This assumes that the graph currently stored is
|
||||||
acyclic.
|
acyclic.
|
||||||
|
|
||||||
Returns `None` if no cycle is found, else retuns a child from `with_children`
|
Returns `None` if no cycle is found, else retuns a child from `with_children`
|
||||||
causing the cycle to appear."""
|
causing the cycle to appear. """
|
||||||
|
|
||||||
def do_dfs(cur_node, visited_nodes):
|
def do_dfs(cur_node, visited_nodes):
|
||||||
"""DFS to check whether we find `self` again"""
|
""" DFS to check whether we find `self` again """
|
||||||
if cur_node.pk in visited_nodes:
|
if cur_node.pk in visited_nodes:
|
||||||
return False
|
return False
|
||||||
if cur_node.pk == self.pk:
|
if cur_node.pk == self.pk:
|
||||||
|
@ -137,7 +103,7 @@ class WikiGroup(models.Model):
|
||||||
|
|
||||||
@receiver(post_save, sender=WikiGroup, dispatch_uid="on_wiki_group_changed")
|
@receiver(post_save, sender=WikiGroup, dispatch_uid="on_wiki_group_changed")
|
||||||
def on_wiki_group_changed(sender, instance, **kwargs):
|
def on_wiki_group_changed(sender, instance, **kwargs):
|
||||||
"""Commit the related WikiGroups to Django Group upon model change"""
|
""" Commit the related WikiGroups to Django Group upon model change """
|
||||||
instance.propagate_update()
|
instance.propagate_update()
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,8 +113,8 @@ def on_wiki_group_changed(sender, instance, **kwargs):
|
||||||
dispatch_uid="on_wiki_group_includes_changed",
|
dispatch_uid="on_wiki_group_includes_changed",
|
||||||
)
|
)
|
||||||
def on_wiki_group_includes_changed(sender, instance, action, **kwargs):
|
def on_wiki_group_includes_changed(sender, instance, action, **kwargs):
|
||||||
"""Commit the related WikiGroups to Django Group upon change of the set of
|
""" Commit the related WikiGroups to Django Group upon change of the set of
|
||||||
included other groups"""
|
included other groups """
|
||||||
if action in ["post_add", "post_remove", "post_clear"]:
|
if action in ["post_add", "post_remove", "post_clear"]:
|
||||||
instance.propagate_update()
|
instance.propagate_update()
|
||||||
|
|
||||||
|
@ -159,7 +125,7 @@ def on_wiki_group_includes_changed(sender, instance, action, **kwargs):
|
||||||
dispatch_uid="on_wiki_group_users_changed",
|
dispatch_uid="on_wiki_group_users_changed",
|
||||||
)
|
)
|
||||||
def on_wiki_group_users_changed(sender, instance, action, **kwargs):
|
def on_wiki_group_users_changed(sender, instance, action, **kwargs):
|
||||||
"""Commit the related WikiGroups to Django Group upon change of included users"""
|
""" Commit the related WikiGroups to Django Group upon change of included users """
|
||||||
if action in ["post_add", "post_remove", "post_clear"]:
|
if action in ["post_add", "post_remove", "post_clear"]:
|
||||||
instance.propagate_update()
|
instance.propagate_update()
|
||||||
|
|
||||||
|
@ -170,7 +136,7 @@ def on_wiki_group_users_changed(sender, instance, action, **kwargs):
|
||||||
dispatch_uid="on_wiki_group_includes_check_acyclic",
|
dispatch_uid="on_wiki_group_includes_check_acyclic",
|
||||||
)
|
)
|
||||||
def on_wiki_group_includes_check_acyclic(sender, instance, action, pk_set, **kwargs):
|
def on_wiki_group_includes_check_acyclic(sender, instance, action, pk_set, **kwargs):
|
||||||
"""Checks the acyclicity of the groups' graph before committing new edges.
|
""" Checks the acyclicity of the groups' graph before committing new edges.
|
||||||
|
|
||||||
PLEASE NOTE that this check is only a fallback, and that forms should validate
|
PLEASE NOTE that this check is only a fallback, and that forms should validate
|
||||||
the acyclicity before committing anything.
|
the acyclicity before committing anything.
|
||||||
|
|
|
@ -1,207 +0,0 @@
|
||||||
{% extends "wiki/base.html" %}
|
|
||||||
{% load sekizai_tags %}
|
|
||||||
|
|
||||||
{% block wiki_site_title %}Groupes administrés - WikiENS{% endblock %}
|
|
||||||
|
|
||||||
{% block wiki_contents %}
|
|
||||||
<h2>Gestion du groupe « {{ wikigroup }} »</h2>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col">
|
|
||||||
<h4>Liste des membres</h4>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
<div class="list-group-item">
|
|
||||||
<form action="{% url 'wiki_groups:add-user' wikigroup.pk %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control {% if errors.user %}is-invalid{% endif %}" name="user" value="{{ errors.user.value }}" placeholder="Ajouter un membre">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button type="submit" class="btn btn-primary rounded-right">Enregistrer</button>
|
|
||||||
</div>
|
|
||||||
{% if errors.user %}
|
|
||||||
{% for msg in errors.user.msg %}
|
|
||||||
<div class="invalid-feedback">{{ msg }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">Entrer le login (clipper) de la personne à ajouter</small>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% for user in wikigroup.users.all %}
|
|
||||||
<div class="list-group-item pb-2">
|
|
||||||
<span class="font-italic">{{ user }}</span>
|
|
||||||
<button type="button" class="btn btn-danger btn-sm float-right" data-toggle="modal" data-target="#modal-confirm" data-href="{% url 'wiki_groups:remove-user' wikigroup.pk user.pk %}" data-name="{{ user }}" data-kind="membre">Enlever</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col">
|
|
||||||
<h4>Liste des groupes inclus</h4>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
<div class="list-group-item">
|
|
||||||
<form action="{% url 'wiki_groups:add-group' wikigroup.pk %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control {% if errors.group_add %}is-invalid{% endif %}" name="group" value="{{ errors.group_add.value }}" placeholder="Ajouter un groupe existant">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button type="submit" class="btn btn-primary rounded-right">Enregistrer</button>
|
|
||||||
</div>
|
|
||||||
{% if errors.group_add %}
|
|
||||||
{% for msg in errors.group_add.msg %}
|
|
||||||
<div class="invalid-feedback">{{ msg }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">Entrer le nom du groupe à ajouter</small>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% for group in wikigroup.includes_groups.all %}
|
|
||||||
<div class="list-group-item pb-2">
|
|
||||||
<span class="font-italic">{{ group }}</span>
|
|
||||||
<button type="button" class="btn btn-danger btn-sm float-right" data-toggle="modal" data-target="#modal-confirm" data-href="{% url 'wiki_groups:remove-group' wikigroup.pk group.pk %}" data-name="{{ group }}" data-kind="groupe">Enlever</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<div class="list-group-item">
|
|
||||||
<form action="{% url 'wiki_groups:create-group' wikigroup.pk %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control {% if errors.group_create %}is-invalid{% endif %}" name="group" value="{{ errors.group_create.value }}" placeholder="Créer et ajouter un groupe">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button type="submit" class="btn btn-primary rounded-right">Enregistrer</button>
|
|
||||||
</div>
|
|
||||||
{% if errors.group_create %}
|
|
||||||
{% for msg in errors.group_create.msg %}
|
|
||||||
<div class="invalid-feedback">{{ msg }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">Entrer le nom du groupe à créer</small>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-6">
|
|
||||||
<h4>Liste des gestionnaires</h4>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="list-group">
|
|
||||||
<div class="list-group-item">
|
|
||||||
<form action="{% url 'wiki_groups:add-manager' wikigroup.pk %}" method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control {% if errors.manager %}is-invalid{% endif %}" name="user" value="{{ errors.manager.value }}" placeholder="Ajouter un·e gestionnaire">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button type="submit" class="btn btn-primary rounded-right">Enregistrer</button>
|
|
||||||
</div>
|
|
||||||
{% if errors.manager %}
|
|
||||||
{% for msg in errors.manager.msg %}
|
|
||||||
<div class="invalid-feedback">{{ msg }}</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<small class="form-text text-muted">Entrer le login (clipper) de la personne à ajouter</small>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% for user in wikigroup.managers.all %}
|
|
||||||
<div class="list-group-item pb-2">
|
|
||||||
<span class="font-italic">{{ user }}</span>
|
|
||||||
<button type="button" class="btn btn-danger btn-sm float-right" data-toggle="modal" data-target="#modal-confirm" data-href="{% url 'wiki_groups:remove-manager' wikigroup.pk user.pk %}" data-name="{{ user }}" data-kind="gestionnaire">Enlever</a>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if request.user.is_staff %}
|
|
||||||
<div class="col">
|
|
||||||
<h4>Supprimer ce groupe</h4>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<div class="jumbotron">
|
|
||||||
<p class="text-danger">Attention, cette action est irréversible.</p>
|
|
||||||
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#modal-delete">Supprimer</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal fade" id="modal-delete">
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Supprimer le groupe {{ wikigroup }} ?</h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-body">
|
|
||||||
La suppression du groupe « {{ wikigroup }} » est irréversible et tous ses membres seront enlevés des groupes suivants :
|
|
||||||
<ul>
|
|
||||||
{% for g in wikigroup.get_all_groups %}
|
|
||||||
<li>{{ g }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
|
||||||
<a class="btn btn-danger" href="{% url 'wiki_groups:delete-group' wikigroup.pk %}">Supprimer</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{# Confirmation modal #}
|
|
||||||
<div class="modal fade" id="modal-confirm">
|
|
||||||
<div class="modal-dialog" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title"></h5>
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-body">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
|
|
||||||
<a class="btn btn-danger">Enlever</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% addtoblock "js" %}
|
|
||||||
<script>
|
|
||||||
$('#modal-confirm').on('show.bs.modal', function(event) {
|
|
||||||
const b = $(event.relatedTarget);
|
|
||||||
|
|
||||||
const modal = $(this);
|
|
||||||
modal.find('.modal-title').text(`Enlever un ${b.data('kind')}`);
|
|
||||||
modal.find('.modal-body').html(`Enlever <span class="font-weight-bold font-italic">${b.data('name')}</span> en tant que ${b.data('kind')} du groupe {{ wikigroup }} ?`);
|
|
||||||
modal.find('.modal-footer a').prop('href', b.data('href'));
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{% endaddtoblock %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
41
wiki_groups/templates/wiki_groups/detail.html
Normal file
41
wiki_groups/templates/wiki_groups/detail.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{% extends "wiki/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
|
||||||
|
{% block wiki_site_title %}Groups - WikiENS{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_contents %}
|
||||||
|
|
||||||
|
<h2>Informations sur le groupe : {{ group }}</h2>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{% if group.users.exists %}
|
||||||
|
<h3>Membres du groupe</h3>
|
||||||
|
<hr />
|
||||||
|
<ul>
|
||||||
|
{% for user in group.users.all %}
|
||||||
|
<li>{{ user }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if group.includes_groups.exists %}
|
||||||
|
<h3>{{ group }} contient les groupes suivants</h3>
|
||||||
|
<hr />
|
||||||
|
<ul>
|
||||||
|
{% for g in group.includes_groups.all %}
|
||||||
|
<li><a href="{% url "wiki_groups:detail" g.id %}">{{ g }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if group.included_in_groups.exists %}
|
||||||
|
<h3>{{ group }} est contenu dans les groupes suivants</h3>
|
||||||
|
<hr />
|
||||||
|
<ul>
|
||||||
|
{% for g in group.included_in_groups.all %}
|
||||||
|
<li><a href="{% url "wiki_groups:detail" g.id %}">{{ g }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -1,52 +1,47 @@
|
||||||
{% extends "wiki/base.html" %}
|
{% extends "wiki/base.html" %}
|
||||||
{% load static %}
|
{% load staticfiles %}
|
||||||
|
|
||||||
{% block wiki_site_title %}Groupes - WikiENS{% endblock %}
|
{% block wiki_site_title %}Groupes - WikiENS{% endblock %}
|
||||||
|
|
||||||
{% block wiki_contents %}
|
{% block wiki_contents %}
|
||||||
<h2>
|
<h2>Liste des groupes du wiki</h2>
|
||||||
Liste des groupes du wiki
|
|
||||||
{% if request.user.is_staff or request.user.managed_groups.exists %}
|
|
||||||
<a class="btn btn-primary float-right" href="{% url 'wiki_groups:managed-groups' %}">
|
|
||||||
Liste des groupes gérés
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</h2>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<ul>
|
<hr />
|
||||||
{% for group in wikigroup_list %}
|
|
||||||
<li>{{ group.django_group.name }} {% if group.managers.exists %}(Géré par {% for m in group.managers.all %}{{ m }}{% if not forloop.last %}, {% endif %}{% endfor %}){% endif %}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Graphe des groupes du wiki</h2>
|
<ul>
|
||||||
<hr>
|
{% for id, name in groups %}
|
||||||
|
<li><a href="{% url "wiki_groups:detail" id %}">{{ name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<div id="svg-graph" class="overflow-auto"></div>
|
<h2>Graphe des groupes du wiki</h2>
|
||||||
<hr>
|
|
||||||
|
|
||||||
<p>
|
<hr />
|
||||||
Les flèches représentent l'inclusion : <code>A -> B</code> signifie que
|
|
||||||
le groupe <code>A</code> est contenu dans le groupe <code>B</code>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<script src="{% static 'wiki_groups/js/vendor/viz.js' %}"></script>
|
<div id="svg-graph"></div>
|
||||||
<script src="{% static 'wiki_groups/js/vendor/lite.render.js' %}"></script>
|
|
||||||
<script>
|
|
||||||
var viz = new Viz();
|
|
||||||
|
|
||||||
var xhttp = new XMLHttpRequest();
|
<hr />
|
||||||
xhttp.onreadystatechange = function() {
|
|
||||||
if (this.readyState == 4 && this.status == 200) {
|
<p>
|
||||||
viz.renderSVGElement(this.responseText)
|
Les flèches représentent l'inclusion : <code>A -> B</code> signifie que
|
||||||
|
le groupe <code>A</code> est contenu dans le groupe <code>B</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<script src="{% static 'wiki_groups/js/vendor/viz.js' %}"></script>
|
||||||
|
<script src="{% static 'wiki_groups/js/vendor/lite.render.js' %}"></script>
|
||||||
|
<script>
|
||||||
|
var viz = new Viz();
|
||||||
|
|
||||||
|
var xhttp = new XMLHttpRequest();
|
||||||
|
xhttp.onreadystatechange = function() {
|
||||||
|
if (this.readyState == 4 && this.status == 200) {
|
||||||
|
viz.renderSVGElement(this.responseText)
|
||||||
.then(element => {
|
.then(element => {
|
||||||
document.getElementById("svg-graph").appendChild(element);
|
document.getElementById("svg-graph").appendChild(element);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhttp.open('GET', '{% url 'wiki_groups:dot_graph' %}', true);
|
xhttp.open("GET", "{% url 'wiki_groups:dot_graph' %}", true);
|
||||||
xhttp.send();
|
xhttp.send();
|
||||||
|
</script>
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
{% extends "wiki/base.html" %}
|
|
||||||
|
|
||||||
{% block wiki_site_title %}Groupes administrés - WikiENS{% endblock %}
|
|
||||||
|
|
||||||
{% block wiki_contents %}
|
|
||||||
<h2>Liste des groupes administrés</h2>
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
{% if wikigroup_list %}
|
|
||||||
<div class="list-group">
|
|
||||||
{% for group in wikigroup_list %}
|
|
||||||
<a class="list-group-item list-group-item-action" href="{% url 'wiki_groups:admin-group' group.pk %}">{{ group }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -6,26 +6,5 @@ app_name = "wiki_groups"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.GroupList.as_view(), name="list"),
|
path("", views.GroupList.as_view(), name="list"),
|
||||||
path("dot_graph", views.dot_graph, name="dot_graph"),
|
path("dot_graph", views.dot_graph, name="dot_graph"),
|
||||||
path("managed", views.ManagedGroupsView.as_view(), name="managed-groups"),
|
path("detail/<int:pk>", views.GroupDetail.as_view(), name="detail"),
|
||||||
path("<int:pk>", views.WikiGroupView.as_view(), name="admin-group"),
|
|
||||||
path(
|
|
||||||
"<int:pk>/rm-user/<int:user_pk>",
|
|
||||||
views.RemoveUserView.as_view(),
|
|
||||||
name="remove-user",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"<int:pk>/rm-manager/<int:user_pk>",
|
|
||||||
views.RemoveManagerView.as_view(),
|
|
||||||
name="remove-manager",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"<int:pk>/rm-group/<int:group_pk>",
|
|
||||||
views.RemoveGroupView.as_view(),
|
|
||||||
name="remove-group",
|
|
||||||
),
|
|
||||||
path("<int:pk>/add-user", views.AddUserView.as_view(), name="add-user"),
|
|
||||||
path("<int:pk>/add-manager", views.AddManagerView.as_view(), name="add-manager"),
|
|
||||||
path("<int:pk>/add-group", views.AddGroupView.as_view(), name="add-group"),
|
|
||||||
path("<int:pk>/create-group", views.CreateGroupView.as_view(), name="create-group"),
|
|
||||||
path("<int:pk>/delete", views.DeleteGroupView.as_view(), name="delete-group"),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,16 +1,8 @@
|
||||||
from django.contrib.auth import get_user_model
|
from django.http import HttpResponse
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.views.generic import DetailView, ListView
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
|
||||||
from django.urls import reverse, reverse_lazy
|
|
||||||
from django.views.generic import DetailView, ListView, RedirectView
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
|
||||||
from django.views.generic.edit import BaseFormView
|
|
||||||
|
|
||||||
from wiki_groups.forms import CreateGroupForm, SelectGroupForm, SelectUserForm
|
|
||||||
from wiki_groups.models import WikiGroup
|
from wiki_groups.models import WikiGroup
|
||||||
|
|
||||||
User = get_user_model()
|
|
||||||
|
|
||||||
|
|
||||||
def dot_graph(request):
|
def dot_graph(request):
|
||||||
response = HttpResponse(content_type="text/vnd.graphviz")
|
response = HttpResponse(content_type="text/vnd.graphviz")
|
||||||
|
@ -33,211 +25,15 @@ def dot_graph(request):
|
||||||
class GroupList(ListView):
|
class GroupList(ListView):
|
||||||
template_name = "wiki_groups/list.html"
|
template_name = "wiki_groups/list.html"
|
||||||
model = WikiGroup
|
model = WikiGroup
|
||||||
|
context_object_name = "groups"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return WikiGroup.objects.select_related("django_group").order_by(
|
return WikiGroup.objects.values_list("id", "django_group__name").order_by(
|
||||||
"django_group__name"
|
"django_group__name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class WikiGroupMixin(SingleObjectMixin, UserPassesTestMixin):
|
class GroupDetail(DetailView):
|
||||||
"""
|
template_name = "wiki_groups/detail.html"
|
||||||
Restricts the view to a manager of the wikigroup, and selects automatically
|
|
||||||
the required WikiGroup with its Django group
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = WikiGroup
|
model = WikiGroup
|
||||||
|
context_object_name = "group"
|
||||||
def get_queryset(self):
|
|
||||||
return super().get_queryset().select_related("django_group")
|
|
||||||
|
|
||||||
def test_func(self):
|
|
||||||
self.object = self.get_object()
|
|
||||||
return self.request.user.is_staff or self.object.is_manager(self.request.user)
|
|
||||||
|
|
||||||
|
|
||||||
class StaffMixin(UserPassesTestMixin):
|
|
||||||
"""Restricts the view to a staff member"""
|
|
||||||
|
|
||||||
def test_func(self):
|
|
||||||
return self.request.user.is_staff
|
|
||||||
|
|
||||||
|
|
||||||
class ManagedGroupsView(LoginRequiredMixin, ListView):
|
|
||||||
model = WikiGroup
|
|
||||||
template_name = "wiki_groups/managed.html"
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
if self.request.user.is_staff:
|
|
||||||
return WikiGroup.objects.select_related("django_group")
|
|
||||||
return self.request.user.managed_groups.select_related("django_group")
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
ctx = super().get_context_data(**kwargs)
|
|
||||||
wikigroups = set()
|
|
||||||
|
|
||||||
for group in ctx["wikigroup_list"]:
|
|
||||||
wikigroups |= group.get_all_groups()
|
|
||||||
|
|
||||||
ctx["wikigroup_list"] = wikigroups
|
|
||||||
ctx["object_list"] = wikigroups
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
|
|
||||||
class WikiGroupView(WikiGroupMixin, DetailView):
|
|
||||||
template_name = "wiki_groups/admin.html"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
kwargs.update({"errors": self.request.session.pop("wiki_form_errors", None)})
|
|
||||||
return super().get_context_data(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveUserView(WikiGroupMixin, RedirectView):
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
user = User.objects.filter(pk=kwargs["user_pk"]).first()
|
|
||||||
|
|
||||||
if user is not None:
|
|
||||||
self.object.users.remove(user)
|
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveManagerView(WikiGroupMixin, RedirectView):
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
user = User.objects.filter(pk=kwargs["user_pk"]).first()
|
|
||||||
|
|
||||||
if user is not None:
|
|
||||||
self.object.users.remove(user)
|
|
||||||
|
|
||||||
self.object.managers.remove(user)
|
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class RemoveGroupView(WikiGroupMixin, RedirectView):
|
|
||||||
def get_redirect_url(self, *args, **kwargs):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[kwargs["pk"]])
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
group = WikiGroup.objects.filter(pk=kwargs["group_pk"]).first()
|
|
||||||
|
|
||||||
if group is not None:
|
|
||||||
self.object.includes_groups.remove(group)
|
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class AddUserView(WikiGroupMixin, BaseFormView):
|
|
||||||
form_class = SelectUserForm
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[self.object.pk])
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object.users.add(form.cleaned_data["user"])
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def form_invalid(self, form):
|
|
||||||
self.request.session["wiki_form_errors"] = {
|
|
||||||
"user": {"value": form["user"].value(), "msg": form.errors["user"]}
|
|
||||||
}
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class AddManagerView(WikiGroupMixin, BaseFormView):
|
|
||||||
form_class = SelectUserForm
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[self.object.pk])
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
self.object.managers.add(form.cleaned_data["user"])
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def form_invalid(self, form):
|
|
||||||
self.request.session["wiki_form_errors"] = {
|
|
||||||
"manager": {"value": form["user"].value(), "msg": form.errors["user"]}
|
|
||||||
}
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class AddGroupView(WikiGroupMixin, BaseFormView):
|
|
||||||
"""Adds an existing metagroup to this group"""
|
|
||||||
|
|
||||||
form_class = SelectGroupForm
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[self.object.pk])
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
subgroup = form.cleaned_data["group"]
|
|
||||||
group = self.object
|
|
||||||
|
|
||||||
if group.group_in_cycle(list(group.includes_groups.all()) + [subgroup]):
|
|
||||||
form.add_error(
|
|
||||||
"group",
|
|
||||||
(
|
|
||||||
"Ajout impossible sous peine de créer un cycle "
|
|
||||||
f"({group} est inclus dans {subgroup})"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
group.includes_groups.add(subgroup)
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def form_invalid(self, form):
|
|
||||||
self.request.session["wiki_form_errors"] = {
|
|
||||||
"group_add": {"value": form["group"].value(), "msg": form.errors["group"]}
|
|
||||||
}
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class CreateGroupView(WikiGroupMixin, BaseFormView):
|
|
||||||
"""Creates a metagroup included in this group"""
|
|
||||||
|
|
||||||
form_class = CreateGroupForm
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return reverse("wiki_groups:admin-group", args=[self.object.pk])
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
new_group = form.cleaned_data["group"]
|
|
||||||
|
|
||||||
self.object.includes_groups.add(new_group)
|
|
||||||
new_group.managers.add(self.request.user)
|
|
||||||
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
def form_invalid(self, form):
|
|
||||||
self.request.session["wiki_form_errors"] = {
|
|
||||||
"group_create": {
|
|
||||||
"value": form["group"].value(),
|
|
||||||
"msg": form.errors["group"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteGroupView(SingleObjectMixin, StaffMixin, RedirectView):
|
|
||||||
model = WikiGroup
|
|
||||||
url = reverse_lazy("wiki_groups:managed-groups")
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
group = self.get_object()
|
|
||||||
# On enlève les membres pour répercuter les changements
|
|
||||||
group.users.clear()
|
|
||||||
|
|
||||||
# On utilise la propagation de la suppression django_group -> wiki_group
|
|
||||||
group.django_group.delete()
|
|
||||||
|
|
||||||
return super().get(request, *args, **kwargs)
|
|
||||||
|
|
Loading…
Reference in a new issue