Skip to content

Commit

Permalink
Merge pull request AdminTL#95 from mathben/generate_project_archive_#60
Browse files Browse the repository at this point in the history
[AdminTL#60] Admin: Add admin setting page and feature to download archive project
  • Loading branch information
mathben authored Apr 8, 2018
2 parents 985e05b + 5e35f25 commit b71c5fb
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/web/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class BaseHandler(tornado.web.RequestHandler):
_redirect_http_to_https = None
_config = None
_doc_generator_gspread = None
_project_archive = None
_global_arg = {}

def initialize(self, **kwargs):
Expand All @@ -27,6 +28,7 @@ def initialize(self, **kwargs):
self._redirect_http_to_https = kwargs.get("redirect_http_to_https")
self._config = kwargs.get("config")
self._doc_generator_gspread = kwargs.get("doc_generator_gspread")
self._project_archive = kwargs.get("project_archive")

self._global_arg = {
"debug": self._debug,
Expand Down
45 changes: 45 additions & 0 deletions src/web/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import base_handler
import jsonhandler
import os
import datetime

io_loop = tornado.ioloop.IOLoop.instance()
config_path = "config"
Expand Down Expand Up @@ -452,6 +453,7 @@ def get(self):
self.set_status(404)
self.send_error(404)
raise tornado.web.Finish()

if self.is_permission_admin():
self.render('admin/editor.html', **self._global_arg)
else:
Expand All @@ -462,6 +464,26 @@ def get(self):
raise tornado.web.Finish()


class AdminSettingHandler(base_handler.BaseHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
def get(self):
if self._global_arg["disable_admin"]:
# Not Found
self.set_status(404)
self.send_error(404)
raise tornado.web.Finish()

if self.is_permission_admin():
self.render('admin/setting.html', **self._global_arg)
else:
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()


class ProfileHandler(base_handler.BaseHandler):
@tornado.web.asynchronous
@tornado.web.authenticated
Expand Down Expand Up @@ -908,3 +930,26 @@ def get(self):
self.send_error(400)
raise tornado.web.Finish()
self.finish()


class SettingArchiveGenerateProjectHandler(base_handler.BaseHandler):
"""This class generate an archive of this project repository."""

@tornado.web.authenticated
def get(self):
if not self.is_permission_admin():
print("Insufficient permissions from %s" % self.request.remote_ip, file=sys.stderr)
# Forbidden
self.set_status(403)
self.send_error(403)
raise tornado.web.Finish()

# Create header
file_name = "gestion_personnage_tl_archive_%s.zip" % datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S")
self.set_header('Content-Type', 'application/octet-stream')
self.set_header('Content-Disposition', 'attachment; filename=' + file_name)

# Generate archive project
data = self._project_archive.generate_archive()
self.write(data)
self.finish()
2 changes: 2 additions & 0 deletions src/web/partials/admin/_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<li ng-class="{ active: isActive('/admin/character') }"><a href="/admin/character"><span class="glyphicon glyphicon-knight"></span> Personnage</a></li>
{% end %}
<li ng-class="{ active: isActive('/admin/editor') }"><a href="/admin/editor"><span class="glyphicon glyphicon-edit"></span> Éditeur</a></li>
<li ng-class="{ active: isActive('/admin/setting') }"><a href="/admin/setting"><span class="glyphicon glyphicon-cog"></span> Paramètre</a></li>

{% if not disable_custom_css %}
<li class="dropdown">
Expand Down Expand Up @@ -181,6 +182,7 @@ <h1 class="detect_javascript_enable">
<script src="{{ static_url('resources/js/tl_module/lore_ctrl/lore_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/profile_ctrl/profile_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/editor_ctrl/editor_ctrl.js') }}"></script>
<script src="{{ static_url('resources/js/tl_module/setting_ctrl/setting_ctrl.js') }}"></script>

<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode.js') }}"></script>
<script src="{{ static_url('bower_components/qrcode-generator/js/qrcode_UTF8.js') }}"></script>
Expand Down
2 changes: 1 addition & 1 deletion src/web/partials/admin/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ <h2>Générateur de documentation à partir de Google Drive Spreadsheet</h2>
<div ng-show="model_editor.info.can_generate">
<a ng-click="generate_doc()" class="btn btn-lg btn-success" ng-class="model_editor.is_generating_doc ? 'disabled' : ''" role="button">
<span class="fa fa-spinner fa-spin" ng-show="model_editor.is_generating_doc"></span> Générer le document.
</a><br/>
</a>
</div>
</div>
</div>
Expand Down
19 changes: 19 additions & 0 deletions src/web/partials/admin/setting.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends "_base.html" %}

{% block content %}

<div ng-controller="setting_ctrl" ng-cloak>
<h1>Configuration du site et ses paramètres</h1>
<h2>Générateur d'archive du projet</h2>
<p>Cet outil permet de télécharger le projet dans un fichier format zip et l'exécuter dans un autre environnement.</p>
<div>
<a ng-click="download_archive()" class="btn btn-lg btn-success" role="button">
<span class="fa fa-spinner fa-spin" ng-show="model_setting.is_downloading_archive"></span> Télécharger le fichier archive.
</a>
<a ng-show="model_setting.downloading_archive.status.enabled" ng-style="model_setting.downloading_archive.status.is_error ? {'color': 'red'} : {'color': 'green'}">
{{! model_setting.downloading_archive.status.text }}
</a>
</div>
</div>

{% end %}
71 changes: 71 additions & 0 deletions src/web/py_class/project_archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import io
import zipfile
import os


class ProjectArchive(object):
def __init__(self, parser):
# self._error = None
self._parser = parser

actual_path_dir = os.path.dirname(os.path.realpath(__file__))
self._root_project_path = os.path.normpath(os.path.join(actual_path_dir, os.pardir, os.pardir, os.pardir))
self._project_name = "gestion_personnage_TL"
self._ignore_directory = [".git", ".idea", "__pycache__"]
self._ignore_file = [".gitmodules", ".gitignore"]

def generate_archive(self):
# debug_writing_file = []
# Append all data in buffer
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, mode="w", compression=zipfile.ZIP_DEFLATED) as zip_mem:
for root, dirs, files in os.walk(self._root_project_path):
# Ignore directory
ignore_dir = False
for exclude_path in self._ignore_directory:
if exclude_path in root:
ignore_dir = True
break
if ignore_dir:
continue

for file in files:
# Ignore file
if file in self._ignore_file:
continue

file_path = os.path.join(root, file)
file_rel_path = file_path[len(self._root_project_path) + 1:]
zip_file_path = os.path.join(self._project_name, file_rel_path)
# Write file in buffer with zip
with open(file_path, mode="r+b") as obj_file:
zip_mem.writestr(zip_file_path, obj_file.read())
# debug_writing_file.append((file_path, zip_file_path))

io_buffer = buffer.getvalue()
return io_buffer

# def has_error(self):
# """
#
# :return: If contain error.
# """
# return bool(self._error)
#
# def get_error(self, create_object=True, force_error=False):
# """
#
# :param create_object: if return dict with key "error" or return the message in string
# :param force_error: if activate, generate an unknown error message.
# :return: information about error.
# """
# msg = self._error
# if not msg and force_error:
# msg = "Unknown error."
#
# if create_object:
# return {"error": msg}
# return msg
21 changes: 21 additions & 0 deletions src/web/resources/js/tl_module/setting_ctrl/setting_ctrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Formulaire de Traitre-Lame
"use strict";

characterApp.controller("setting_ctrl", ["$scope", "$q", "$http", "$window", /*"$timeout",*/ function ($scope, $q, $http, $window) {
$scope.model_setting = {
is_ctrl_ready: false,

is_downloading_archive: false,
downloading_archive: {
status: {
enabled: false,
is_error: false,
text: ""
}
}
};

$scope.download_archive = function () {
window.open("/cmd/archive/generate_project", "_blank", "");
}
}]);
7 changes: 7 additions & 0 deletions src/web/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from py_class.lore import Lore
from py_class.doc_generator.doc_generator_gspread import DocGeneratorGSpread
from py_class.auth_keys import AuthKeys
from py_class.project_archive import ProjectArchive

WEB_ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
DEFAULT_SSL_DIRECTORY = os.path.join(WEB_ROOT_DIR, "..", "..", "ssl_cert", "certs")
Expand Down Expand Up @@ -80,6 +81,7 @@ def main(parse_arg):
"manual": Manual(parse_arg),
"lore": Lore(parse_arg),
"doc_generator_gspread": DocGeneratorGSpread(parse_arg),
"project_archive": ProjectArchive(parse_arg),
"disable_character": parse_arg.disable_character,
"disable_user_character": parse_arg.disable_user_character,
"disable_admin": parse_arg.disable_admin,
Expand Down Expand Up @@ -120,6 +122,7 @@ def main(parse_arg):
# Admin web page
tornado.web.url(r"/admin/character?", handlers.AdminCharacterHandler, name='admin character', kwargs=settings),
tornado.web.url(r"/admin/editor?", handlers.AdminEditorHandler, name='admin editor', kwargs=settings),
tornado.web.url(r"/admin/setting?", handlers.AdminSettingHandler, name='admin setting', kwargs=settings),

# Command
tornado.web.url(r"/cmd/character_view/?", handlers.CharacterViewHandler, name='character_view',
Expand Down Expand Up @@ -147,6 +150,10 @@ def main(parse_arg):
tornado.web.url(r"/cmd/editor/update_file_url/?", handlers.EditorCmdUpdateFileUrlHandler,
name='cmd_editor_update_file_url', kwargs=settings),

# Archive
tornado.web.url(r"/cmd/archive/generate_project", handlers.SettingArchiveGenerateProjectHandler,
name='generate_project_archive', kwargs=settings),

# Auto ssl
tornado.web.url(r"/.well-known/acme-challenge.*", handlers.AutoSSLHandler, name="auto_ssl")
]
Expand Down

0 comments on commit b71c5fb

Please sign in to comment.