Big commit i'm sorry ...

This commit is contained in:
2026-05-09 01:53:54 +02:00
parent 4a56042b25
commit 4192e10f34
184 changed files with 54564 additions and 588 deletions

View File

@@ -7,14 +7,15 @@ from wtforms import validators
from wtforms.validators import DataRequired, Length from wtforms.validators import DataRequired, Length
class FormBloc(FlaskForm): class FormBloc(FlaskForm):
title = StringField('Titre', validators=[DataRequired()]) title = StringField('Titre', validators=[])
name = StringField('Nom du bloc', validators=[DataRequired()]) subtitle = StringField('Sous-titre', validators=[])
property = StringField('Classes CSS', validators=[])
bg_color = ColorField('Couleur de fond', validators=[]) bg_color = ColorField('Couleur de fond', validators=[])
fg_color = ColorField('Couleur du texte', validators=[]) fg_color = ColorField('Couleur du texte', validators=[])
text = TextAreaField('Présentation', validators=[Length(min=0, max=1000)]) text = TextAreaField('Présentation', validators=[Length(min=0, max=30000)])
text_button = StringField('Texte du bouton', validators=[]) text_button = StringField('Texte du bouton', validators=[])
link = StringField('Lien', validators=[]) link = StringField('Lien', validators=[])
image = FileField('image', validators=[FileAllowed(['jpg', 'png', 'gif'], 'Images only!')]) image = FileField('image', validators=[FileAllowed(['jpg','jpeg','png'])])
email = StringField('Email', validators=[]) email = StringField('Email', validators=[])
phone = StringField('Téléphone', validators=[]) phone = StringField('Téléphone', validators=[])
address = StringField('Addresse', validators=[]) address = StringField('Addresse', validators=[])
@@ -35,8 +36,7 @@ class ImageUpload(FlaskForm):
subtitle = StringField('Sous-titre',validators=[]) subtitle = StringField('Sous-titre',validators=[])
text = StringField('Une description de votre image', validators=[]) text = StringField('Une description de votre image', validators=[])
category = StringField('Categories',validators=[]) category = StringField('Categories',validators=[])
image = FileField('image', validators=[FileRequired(), image = FileField('image', validators=[FileAllowed(['jpg', 'png', 'gif'], 'Images only!')])
FileAllowed(['jpg', 'png', 'gif'], 'Images only!')])
class Login(FlaskForm): class Login(FlaskForm):
login = StringField('Votre identifiant', validators=[DataRequired()]) login = StringField('Votre identifiant', validators=[DataRequired()])

View File

@@ -4,13 +4,15 @@ from werkzeug.utils import secure_filename
from app.forms import * from app.forms import *
from PIL import Image from PIL import Image
from os import remove, path from os import remove, path
from pathlib import Path, PurePath, PurePosixPath
class MyBlocs: class MyBlocs:
def new(blocName, blocType): def new(blocName, blocType, theme):
mybloc = None mybloc = None
if blocType in MyBlocs.listTypeBlocs(): if blocType in MyBlocs.listTypeBlocs(theme):
mybloc=dict() mybloc=dict()
mybloc['name'] = str(blocName) mybloc['name'] = str(blocName)
mybloc['type'] = str(blocType) mybloc['type'] = str(blocType)
@@ -26,4 +28,13 @@ class MyBlocs:
def newImgForm(): def newImgForm():
return ImageUpload() return ImageUpload()
def listTypeBlocs(theme):
typeBlocs = list()
folders = Path(PurePosixPath('./app').joinpath('templates', theme, 'blocs'))
for folder in folders.iterdir():
if not(folder.is_dir()):
typeBloc=folder.name.split('.')[0]
typeBlocs.append(typeBloc[1:])
return typeBlocs

View File

@@ -3,105 +3,179 @@ import os
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from app.forms import * from app.forms import *
from PIL import Image from PIL import Image
from os import remove, path from pathlib import Path, PurePath, PurePosixPath
class DataSite: class DataSite:
user_folder = None
datas = "" username = str()
static_path = "" datas = str()
static_path = None
trash = list() trash = list()
filej= "" filej= None
def __init__(self, username, users_folder ): def __init__(self, username :str , users_folder :str ):
self.filej = os.path.join(users_folder, username,'ident.json') self.username = username
self.user_folder = PurePosixPath(users_folder).joinpath(username)
self.filej = Path(PurePosixPath(self.user_folder).joinpath('ident.json'))
with open(self.filej, 'r') as f:
with self.filej.open(newline='', mode='r', encoding='utf-8') as f:
self.datas = json.load(f) self.datas = json.load(f)
self.static_path = os.path.join(users_folder, username, 'public') self.static_path = Path(PurePosixPath(self.user_folder.joinpath('public')))
self.static_path.mkdir(parents=True, exist_ok=True)
def loadFile(self): def loadFile(self):
with open(self.filej, 'r') as f: if not self.filej.isfile():
self.datas = json.load(f) raise FileNotFoundError(f"Le fichier ident.json n'existe pas : {self.filej}")
def loadFile_tmp(self): with self.filej.open(newline='', mode='r', encoding='utf-8') as f:
with open(self.filej_tmp, 'r') as f:
self.datas = json.load(f) self.datas = json.load(f)
def updateBloc(bloc, form):
bloc['name'] = form.name.data
bloc['bg_color'] = form.bg_color.data def getBlocs(self, page: str):
bloc['fg_color']=form.fg_color.data
bloc['title'] = form.title.data if page != '':
bloc['text'] = form.text.data page = Path(self.user_folder.joinpath(page, 'page.json'))
bloc['text_button']=form.text_button.data datas = ""
bloc['link'] = form.link.data if not page.exists():
if form.image.data : raise FileNotFoundError(f"Le fichier page.json n'existe pas : {page}")
f=form.image.data
datas = json.loads(page.read_text())
else:
datas = self.datas
return datas
def updateBloc(self, bloc, form, files=None):
print (form)
for field in form():
print(field)
if files != None:
f = files['image']
filename = secure_filename(f.filename) filename = secure_filename(f.filename)
extension = filename.rsplit('.', 1)[1].lower() extension = filename.rsplit('.', 1)[1].lower()
f.save(os.path.join( bg_custom = 'bg_custom_'+bloc['name']+'.'+extension
self.static_path, 'img','bg_custom.'+ extension)) f.save(PurePath.joinpath(
self.static_path, 'img', bg_custom))
bloc['bg_img']='bg_custom.'+extension bloc['bg_img'] = bg_custom
bloc['email'] = form.email.data
bloc['address']=form.address.data
bloc['phone'] = form.phone.data
bloc['postal_code'] = form.postal_code.data
bloc['city'] = form.city.data
return bloc return bloc
def updateImg(self, bloc, img, form): def updateImg(self, image, form):
filename = img['file'] img = dict()
img['title'] = form.title.data img['title'] = form.title.data
img['subtitle'] = form.subtitle.data img['subtitle'] = form.subtitle.data
img['description'] = form.description.data img['description'] = form.description.data
img['category'] = form.category.data img['category'] = form.category.data
self.datas['blocs'][bloc][filename]=img img['file'] = image
return img
def updateImg_tmp(self, bloc, img, form): def addImg(self, image_uploaded):
filename = img['file']
img['title'] = form.title.data
img['subtitle'] = form.subtitle.data
img['description'] = form.description.data
img['category'] = form.category.data
self.datas['blocs'][bloc][filename]=img
def addImg(self, bloc, image_uploaded):
img = dict() img = dict()
img['title'] = "" img['title'] = ""
img['subtitle']="" img['subtitle']=""
img['text'] = "" img['text'] = ""
img['category'] = "" img['category'] = ""
img['file']=image_uploaded img['file'] = image_uploaded
self.datas['blocs'][bloc][image_uploaded]=img
return img
def addImg_tmp(self, bloc, image_uploaded): def delImg(self, bloc, img):
img = dict()
img['title'] = ""
img['subtitle']=""
img['text'] = ""
img['category'] = ""
img['file']=image_uploaded
self.datas_tmp['blocs'][bloc][image_uploaded]=img
def saveJson(self): if bloc['type'] == 'gallery':
remove(self.filej) img_file = Path(PurePosixPath(self.user_folder).joinpath('public', 'img', bloc[img]['file'] ))
with open (self.filej, 'w') as f: img_thumb_file = Path(PurePosixPath(self.user_folder).joinpath('public','img', 'thumbnails', bloc[img]['file']))
f.write(json.dumps(self.datas, indent=5)) if img_file.exists():
img_file.unlink()
def delImg(self, img): if img_thumb_file.exists():
try: img_thumb_file.unlink()
images = self.datas['images']
images.pop(img) bloc.pop(img)
except KeyError:
print("Le fichier n'est pas dans la liste")
else: else:
self.trash.append(img) img_file = Path(PurePosixPath(self.user_folder).joinpath('public', 'img', img ))
return bloc
def saveMenu(self, menu:dict):
self.datas['menu'] = menu
self.filej.write_text(json.dumps(self.datas, indent=5))
def saveBlocs(self, page='', blocs=dict()):
if page != '':
datas = dict()
datas['blocs'] = blocs
page_info = Path(PurePosixPath(self.user_folder).joinpath(page, 'page.json'))
if not page_info.exists():
raise FileNotFoundError(f"Le fichier page.json n'existe pas : {page}")
page_info.write_text(json.dumps(datas, indent=3))
else:
self.datas['blocs']= blocs
self.filej.write_text(json.dumps(self.datas, indent=5))
def getPages(self):
mypages = list()
folders = Path(self.user_folder)
for folder in folders.iterdir():
if folder.name != 'public' and folder.is_dir():
mypages.append(folder.name)
return mypages
def new_page(self, pageName:str, pageType:str):
page_folder = Path(PurePosixPath(self.user_folder).joinpath(pageName))
page = Path(PurePosixPath(self.user_folder).joinpath(pageName, 'page.json'))
page_type = Path('./app/templates/themes/'+self.datas['config']['theme']+'/pages/'+pageType+'.json')
if not page_type.exists():
raise FileNotFoundError(f"Le type de page n'existe pas : {page_type}")
else:
page_folder.mkdir()
page.write_text(page_type.read_text())
if not page.exists():
raise FileNotFoundError(f"La page n'a pas pu etre créé : {page}")
return True
def rm_page(self, pageName :str):
page_folder = Path(PurePosixPath(self.user_folder).joinpath(pageName))
page = Path(PurePosixPath(self.user_folder).joinpath(pageName, 'page.json'))
#First delete the json page
try:
page.unlink()
print(f"Successfully deleted: {page}")
except FileNotFoundError:
print(f"File not found: {page}. Nothing to delete.")
# Delete the empty directory
if page_folder.is_dir():
try:
page_folder.rmdir()
print(f"Successfully removed empty directory: {page_folder}")
except OSError as e:
print(f"Error removing directory (might not be empty): {e}")
def writeHTML(self, pageName :str, htmlExport :str):
public_folder = Path(PurePosixPath(self.user_folder).joinpath('public'))
pageName = pageName+'.html'
page = Path(PurePosixPath(self.user_folder).joinpath('public', pageName))
page.write_text(htmlExport)
def emptyTrash(self): def emptyTrash(self):
for file in self.trash: for file in self.trash:

View File

@@ -12,7 +12,7 @@ class Users:
if auth_file != None: if auth_file != None:
self.__auth_file = auth_file self.__auth_file = auth_file
self.__auth_method = "file" self.__auth_method = "file"
x
def getPasswd(self, username): def getPasswd(self, username):
with open(self.auth_file, 'r') as f: with open(self.auth_file, 'r') as f:
users_info = f.readlines() users_info = f.readlines()

View File

@@ -1,9 +1,10 @@
from app import app from app import app
from flask import render_template, url_for, request, flash, redirect, session, make_response from flask import render_template, url_for, request, flash, redirect, session, make_response, send_from_directory
import json import json
from markupsafe import escape from markupsafe import escape
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
import os import os
import re
from app.forms import ImageForm, ImageUpload from app.forms import ImageForm, ImageUpload
from app.models.sites import DataSite from app.models.sites import DataSite
from app.models.blocs import MyBlocs from app.models.blocs import MyBlocs
@@ -17,20 +18,15 @@ from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app) csrf = CSRFProtect(app)
app.secret_key = 'your_secret_key' app.secret_key = 'your_secret_key'
app.config.from_pyfile('config.py') app.config.from_pyfile('config.py')
THEMES_FOLDER = './themes/'
USERS_FOLDER = app.config['USERS_FOLDER'] USERS_FOLDER = app.config['USERS_FOLDER']
bcrypt = Bcrypt(app) bcrypt = Bcrypt(app)
EXT_IMG = {'.jpg', '.JPG', '.png', '.PNG', '.gif', '.GIF', '.bmp', '.BMP', '.jpeg', '.JPEG' } EXT_IMG = {'.jpg', '.JPG', '.png', '.PNG', '.gif', '.GIF', '.bmp', '.BMP', '.jpeg', '.JPEG' }
@app.route('/', methods=['GET', 'POST'])
@app.route('/index.html', methods=['GET', 'POST'])
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
form = Login() form = Login()
@@ -65,91 +61,79 @@ def logout():
return redirect(url_for('login')) return redirect(url_for('login'))
@app.route('/edit/save') @app.route('/', methods=['GET'], defaults={'page': ''})
@login_required @app.route('/<page>/', methods=['GET'])
def savefile():
user = '%s'% escape(session['username'])
db = DataSite(user, USERS_FOLDER)
db.saveJson(TMP.datas)
db.emptyTrash()
return redirect(url_for('edit'))
@app.route('/edit')
@login_required
def edit():
user = '%s'% escape(session['username'])
db = DataSite(user, USERS_FOLDER)
theme = db.datas['config']['theme']
menu = db.datas['menu']
myblocs = db.datas['blocs']
return render_template('/edit.html.tpl',
blocEdit="",
config=db.datas['config'],
menu=menu,
theme=theme,
myblocs=myblocs
)
@app.route('/edit/<page>')
@login_required @login_required
def edit(page): def edit(page):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
blocs_page = db.getpage() mypages = db.getPages()
theme = db.datas['config']['theme'] theme = db.datas['config']['theme']
menu = db.datas['menu'] menu = db.datas['menu']
myblocs = db.datas['blocs'] myblocs = db.getBlocs(page)['blocs']
return render_template('/edit.html.tpl', return render_template('/edit.html.tpl',
blocEdit="", blocEdit="",
config=db.datas['config'], config=db.datas['config'],
menu=menu, menu=menu,
theme=theme, theme=theme,
pagesList=mypages,
page=page,
myblocs=myblocs myblocs=myblocs
) )
@app.route('/edit/images/<bloc>/<image>', methods=['GET', 'POST']) @app.route('/edit/images/<bloc>/<image>', methods=['GET', 'POST'], defaults={'page': ''})
@app.route('/<page>/edit/images/<bloc>/<image>', methods=['GET', 'POST'])
@login_required @login_required
def editPhoto(image, bloc): def editPhoto(page, image, bloc):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
image = '%s' % escape(image)
bloc = '%s' % escape(bloc)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
theme = db.datas['config']['theme'] theme = db.datas['config']['theme']
form = ImageForm() form = ImageForm()
mesimages = db.datas['blocs'][bloc] myblocs = db.getBlocs(page)['blocs']
if form.validate_on_submit(): if form.validate_on_submit():
if request.method == 'POST': if request.method == 'POST':
db.updateImg(bloc, mesimages[image], form) myblocs[bloc][image] = db.updateImg(image, form)
db.saveJson() db.saveBlocs(page, myblocs)
form = MyBlocs.makeForm(db.datas['blocs'][bloc]) form = MyBlocs.makeForm(db.datas['blocs'][bloc])
if "hx-request" in request.headers: if "hx-request" in request.headers:
return render_template(theme+'/blocs/edit/images_list.html', return make_response(('Image modified', 200))
theme=theme,
bloc=mesimages,
form=form)
else: else:
return redirect ('/edit#'+bloc) return redirect ('/#'+bloc)
else: else:
return render_template(theme+'/blocs/edit/_image-edit'+'.html', form=form, image=mesimages[image], bloc=mesimages) return render_template(THEMES_FOLDER+theme+'/blocs/edit/_image-edit'+'.html', form=form, image=myblocs[bloc][image], bloc=myblocs[bloc])
@app.route('/upload-gallery/<bloc>', methods=['POST'])
@app.route('/upload-gallery/<bloc>', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/upload-gallery/<bloc>', methods=['POST'])
@login_required @login_required
def drop_upload(bloc): def drop_upload(page, bloc):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
datas = dict()
file = request.files['file'] file = request.files['file']
filename = secure_filename(file.filename) filename = secure_filename(file.filename)
myblocs = db.getBlocs(page)['blocs']
ext = os.path.splitext(filename)[-1] ext = os.path.splitext(filename)[-1]
save_path = "./app/static/img/portfolio" save_path = os.path.join(USERS_FOLDER, user, 'public', 'img' )
if ext not in EXT_IMG : if ext not in EXT_IMG :
return make_response(('Le fichier n\'est pas une image', 400)) return make_response(('Le fichier n\'est pas une image', 400))
current_chunk = int(request.form['dzchunkindex']) current_chunk = int(request.form['dzchunkindex'])
print (current_chunk)
if (os.path.isfile(os.path.join(save_path, 'fullsize', filename ))) and current_chunk == 0: if (os.path.isfile(os.path.join(save_path, filename ))) and current_chunk == 0:
return make_response(('Un fichier avec le même nom existe déjà', 400)) return make_response(('Un fichier avec le même nom existe déjà', 400))
try: try:
with open(os.path.join(save_path, 'fullsize', filename ), 'ab') as f: with open(os.path.join(save_path, filename ), 'ab') as f:
f.seek(int(request.form['dzchunkbyteoffset'])) f.seek(int(request.form['dzchunkbyteoffset']))
f.write(file.stream.read()) f.write(file.stream.read())
except OSError: except OSError:
@@ -159,117 +143,231 @@ def drop_upload(bloc):
if current_chunk + 1 == total_chunks: if current_chunk + 1 == total_chunks:
# This was the last chunk, the file should be complete and the size we expect # This was the last chunk, the file should be complete and the size we expect
if os.path.getsize(os.path.join(save_path, 'fullsize', filename )) != int(request.form['dztotalfilesize']): if os.path.getsize(os.path.join(save_path, filename )) != int(request.form['dztotalfilesize']):
return make_response(('La taille du fichier source est différentes', 500)) return make_response(('La taille du fichier source est différentes', 500))
else: else:
with Image.open(os.path.join(save_path, 'fullsize', filename )) as img : with Image.open(os.path.join(save_path, filename )) as img :
img.thumbnail((300,300)) img.thumbnail((640,480))
img.save(os.path.join(save_path, 'thumbnails', filename ) ) img.save(os.path.join(save_path, 'thumbnails', filename ) )
db.addImg(bloc, filename)
db.saveJson() myblocs[bloc][filename] = db.addImg(filename)
db.saveBlocs(page, myblocs)
return make_response(('Chunk upload succesfull', 200)) return make_response(('Chunk upload succesfull', 200))
@app.route('/edit/images/<bloc>/del/<image>', methods=['GET'])
@login_required
def delImage(bloc, image):
user = '%s'% escape(session['username'])
db = DataSite(user, USERS_FOLDER)
db.delImg(image)
db.saveJson()
return redirect ('/edit#'+bloc)
@app.route('/edit/<blocname>', methods=['GET', 'POST']) @app.route('/edit/images/<bloc>/del/<image>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/edit/images/<bloc>/del/<image>', methods=['GET'])
@login_required @login_required
def editBloc(blocname): def delImage(page, bloc, image):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER)
myblocs = db.getBlocs(page)['blocs']
myblocs[bloc] = db.delImg(myblocs[bloc], image)
db.saveBlocs(page, myblocs)
return make_response(('Image removed', 200))
@app.route('/imgs_list/<blocname>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/imgs_list/<blocname>', methods=['GET'])
@login_required
def imagesList(page, blocname):
user = '%s'% escape(session['username'])
page = '%s' % escape(page)
blocname = '%s' % escape(blocname)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
theme = db.datas['config']['theme'] theme = db.datas['config']['theme']
bloc = db.datas['blocs'][blocname] myblocs = db.getBlocs(page)['blocs']
bloc = myblocs[blocname]
return render_template(THEMES_FOLDER+theme+'/blocs/edit/images_list.html',page=page,
bloc=bloc)
@app.route('/edit/<blocname>', methods=['GET', 'POST'], defaults={'page': ''})
@app.route('/<page>/edit/<blocname>', methods=['GET', 'POST'])
@login_required
def editBloc(page, blocname):
user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER)
theme = db.datas['config']['theme']
myblocs = db.getBlocs(page)['blocs']
bloc = myblocs[blocname]
form = MyBlocs.makeForm(bloc) form = MyBlocs.makeForm(bloc)
if request.method == 'POST': if request.method == 'POST':
datas=dict()
if form.validate_on_submit(): if form.validate_on_submit():
print(form.name.data)
db.datas['blocs'][form.name.data]=DataSite.updateBloc(bloc, form) for field, value in request.form.items():
db.saveJson() print (field)
if field != 'csrf_token' and field != 'image':
myblocs[blocname][field] = value
if 'image' in request.files.keys():
file = request.files['image']
print ('filename: '+ file.filename)
filename = secure_filename(file.filename)
bg_custom = 'bg_custom_'+bloc['name']+filename
print('bg_custom : '+ bg_custom)
file.save(os.path.join(USERS_FOLDER, user, 'public', 'img', bg_custom ))
if 'bg_img' in bloc.keys():
db.delImg(bloc, bloc['bg_img'])
myblocs[blocname]['bg_img'] = bg_custom
db.saveBlocs(page, myblocs)
flash(u'Vos changements on été pris en compte et enregistré', 'success') flash(u'Vos changements on été pris en compte et enregistré', 'success')
else: else:
print(form.errors) print(form.errors)
if "hx-request" in request.headers: if "hx-request" in request.headers:
return render_template(theme+'/blocs/_'+bloc['type']+'.html', return render_template(THEMES_FOLDER+theme+'/blocs/_'+bloc['type']+'.html',
theme=theme, theme=theme,
bloc=db.datas['blocs'][blocname]) page=page,
bloc=myblocs[blocname])
else: else:
return redirect(url_for('edit')) return redirect(url_for('edit'))
else: else:
if "hx-request" in request.headers: if "hx-request" in request.headers:
return render_template(theme+'/blocs/edit/_'+bloc['type']+'.html', return render_template(THEMES_FOLDER+theme+'/blocs/edit/_'+bloc['type']+'.html',
theme=theme, theme=theme,
bloc=bloc, bloc=bloc,
page=page,
form=form) form=form)
else: else:
return render_template('/edit.html.tpl', return render_template('/edit.html.tpl',
config=db.datas['config'], config=db.datas['config'],
menu=db.datas['menu'], menu=db.datas['menu'],
myblocs=db.datas['blocs'], myblocs=myblocs,
page=page,
theme=theme, theme=theme,
blocEdit=bloc['name'], blocEdit=bloc['name'],
form=form) form=form)
@app.route('/view/<bloc>', methods=['GET']) @app.route('/view/<bloc>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/view/<bloc>', methods=['GET'])
@login_required @login_required
def viewBloc(bloc): def viewBloc(page, bloc):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
theme = db.datas['config']['theme'] theme = db.datas['config']['theme']
myblocs = db.datas['blocs'] myblocs = db.getBlocs(page)['blocs']
form = MyBlocs.makeForm(db.datas['blocs'][bloc]) bloc = myblocs[bloc]
bloc = db.datas['blocs'][bloc]
return render_template(theme+'/blocs/_'+bloc['type']+'.html', return render_template(THEMES_FOLDER+theme+'/blocs/_'+bloc['type']+'.html',
bloc=bloc) bloc=bloc)
@app.route('/addBloc', methods=['POST']) @app.route('/addBloc', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/addBloc', methods=['POST'])
@login_required @login_required
def addBloc(): def addBloc(page):
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
for key in request.form: datas = dict()
print (key +':'+ request.form[key]) myblocs = db.getBlocs(page)['blocs']
blocName = request.form['blocName']
blocName = '%s' % escape(request.form['blocName'])
blocType = request.form['blocType'] blocType = request.form['blocType']
bloc = MyBlocs.new(blocName, blocType) bloc = MyBlocs.new(blocName, blocType, db.datas['config']['theme'])
test_name = "^[A-Za-z0-9_-]*$"
if bloc != None: if bloc != None:
db.datas['blocs'][blocName] = { 'name': blocName, 'type': blocType} if bool(re.match(test_name, blocName)) :
myblocs[blocName] = { 'name': blocName, 'type': blocType}
flash (u'Bloc ajouté avec succès', 'succes'); flash (u'Bloc ajouté avec succès', 'succes');
else: else:
return make_response(('Invalid type of bloc ', 500)) return make_response(('Invalid bloc Name', 500))
db.saveBlocs(page, myblocs)
db.saveJson()
if "hx-request" in request.headers: if "hx-request" in request.headers:
return render_template('/admin/_blocs-list.html', return render_template('/admin/_blocs-list.html',
myblocs=db.datas['blocs']) myblocs=db.datas['blocs'])
else: else:
return redirect(url_for('edit')) return redirect(url_for('edit'))
@app.route('/sortblocs', methods=['POST'])
@app.route('/addPage', methods=['POST'])
@login_required @login_required
def sortblocs(): def addPage():
user = '%s'% escape(session['username']) user = '%s'% escape(session['username'])
db = DataSite(user, USERS_FOLDER)
datas = dict()
pageName = '%s' % escape(request.form['pageName'])
pageType = request.form['pageType']
test_name = "^[A-Za-z0-9_-]*$"
if bool(re.match(test_name, pageName)) :
try :
page = db.new_page(pageName, pageType)
except FileExistsError:
return make_response(('Page already exist', 500))
else:
flash (u'Page créé avec succès', 'success');
else:
return make_response(('Invalid type ', 500))
mypages = db.getPages()
return redirect(url_for('edit', page=pageName))
@app.route('/rmPage/<pageName>')
@login_required
def rmPage(pageName):
pageName = '%s' % escape(pageName)
user = '%s'% escape(session['username'])
db = DataSite(user, USERS_FOLDER)
try:
db.rm_page(pageName)
flash (u'Page supprimé avec succès', 'success');
except OSError as e:
flash (u"La page n'a pa pu être supprimer", 'error');
return redirect(url_for('edit'))
@app.route('/sortblocs', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/sortblocs', methods=['POST'])
@login_required
def sortblocs(page):
user = '%s'% escape(session['username'])
page = '%s' % escape(page)
db = DataSite(user, USERS_FOLDER) db = DataSite(user, USERS_FOLDER)
config = db.datas['config'] config = db.datas['config']
menu = db.datas['menu'] menu = db.datas['menu']
myblocs = deepcopy(TMP.datas['blocs']) datas = dict()
db.datas['blocs']=dict() myblocs = dict()
myblocs_orig = db.getBlocs(page)['blocs']
for bloc in request.form: for bloc in request.form:
if bloc != 'csrf_token': if bloc != 'csrf_token':
bloc_name = bloc.replace("bloc-", "", 2) bloc_name = bloc.replace("bloc-", "", 2)
db.datas['blocs'][bloc_name] = myblocs[bloc_name] myblocs[bloc_name] = myblocs_orig[bloc_name]
flash (u'Les blocs ont été réagencés', 'succes'); flash (u'Les blocs ont été réagencés', 'succes');
db.saveJson()
db.saveBlocs(page, myblocs)
return redirect(url_for('edit')) return redirect(url_for('edit'))
@app.route('/editmenu', methods=['POST']) @app.route('/editmenu', methods=['POST'])
@@ -291,7 +389,59 @@ def editMenu():
else: else:
db.datas['menu'][menu] = str(request.form[menu]) db.datas['menu'][menu] = str(request.form[menu])
db.saveJson() db.saveMenu(db.datas['menu'])
flash (u'Le menu a bien été modifier', 'success'); flash (u'Le menu a bien été modifier', 'success');
return redirect(url_for('edit')) return redirect(url_for('edit'))
@app.route('/public/img/<filename>')
@login_required
def myimgs(filename):
user = '%s' % escape(session['username'])
return send_from_directory(
os.path.join(USERS_FOLDER, user, 'public', 'img'), filename )
@app.route('/public/img/thumbnails/<filename>')
@login_required
def mythumbnails(filename):
user = '%s' % escape(session['username'])
return send_from_directory(
os.path.join(USERS_FOLDER, user, 'public', 'img', 'thumbnails'), filename )
@app.route('/favicon.ico', methods=['GET'])
@app.route('/favicon.ico/', methods=['GET'])
def favicon():
return make_response(('Ok! ', 200))
@app.route('/publish', methods=['GET'])
@login_required
def publish():
user = '%s' % escape(session['username'])
db = DataSite(user, USERS_FOLDER)
listPages = db.getPages()
theme = db.datas['config']['theme']
menu = db.datas['menu']
for page in listPages:
myblocs = db.getBlocs(page)['blocs']
htmlExport = render_template('/index.html.tpl',
config=db.datas['config'],
menu=menu,
theme=theme,
page=page,
myblocs=myblocs)
db.writeHTML(theme, page, htmlExport)
# Export index.html landing page
myblocs = db.getBlocs('')['blocs']
htmlExport = render_template('/index.html.tpl',
config=db.datas['config'],
menu=menu,
theme=theme,
page='/',
myblocs=myblocs)
db.writeHTML(theme, 'index', htmlExport)
return redirect(url_for('edit'))

View File

@@ -63,6 +63,92 @@ h6 {
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
} }
.ql-align-center {
text-align: center;
}
.ql-align-left {
text-align: left;
}
.ql-align-right {
text-align: right;
}
blockquote {
padding: 60px 80px 40px;
position: relative;
}
blockquote p {
font-size: 35px;
font-weight: 700px;
text-align: center;
}
/*blockquote p::before {
content: "\f095";
font-family: FontAwesome;
display: inline-block;
padding-right: 6px;
vertical-align: middle;
font-size: 180px;
}*/
blockquote:before {
position: absolute;
font-family: 'FontAwesome';
top: 0;
content:"\f10d";
font-size: 200px;
color: rgba(0,0,0,0.1);
}
blockquote::after {
content: "";
top: 20px;
left: 50%;
margin-left: -100px;
position: absolute;
height: 3px;
width: 200px;
}
blockquote{
font-size: 1.4em;
width:60%;
margin:50px auto;
font-style:italic;
color: #555555;
padding:1.2em 30px 1.2em 75px;
border-left:8px solid #f05f40;
line-height:1.6;
position: relative;
background:#EDEDED;
}
blockquote::before{
font-family:Arial;
content: "\201C";
color:#f05f40;
font-size:4em;
position: absolute;
left: 10px;
top:-10px;
}
blockquote::after{
content: '';
}
blockquote span{
display:block;
color:#333333;
font-style: normal;
font-weight: bold;
margin-top:1em;
}
section { section {
padding: 8rem 0; padding: 8rem 0;
z-index: -1; z-index: -1;
@@ -141,7 +227,7 @@ img::-moz-selection {
@media (min-width: 992px) { @media (min-width: 992px) {
#mainNav { #mainNav {
border-color: transparent; border-color: transparent;
background-color: transparent; background-color: rgba(0,0,0,0.7);
} }
#mainNav .navbar-brand { #mainNav .navbar-brand {
color: rgba(255, 255, 255, 0.7); color: rgba(255, 255, 255, 0.7);
@@ -190,16 +276,27 @@ header.masthead {
background-size: cover; background-size: cover;
} }
header.masthead hr { header.article-img {
padding-top: 10rem;
padding-bottom: calc(10rem - 56px);
background-position: center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
header.article-img header.masthead hr {
margin-top: 30px; margin-top: 30px;
margin-bottom: 30px; margin-bottom: 30px;
} }
header.masthead h1 { header.article-img header.masthead h1 {
font-size: 2rem; font-size: 2rem;
} }
header.masthead p { header.article-img header.masthead p {
font-weight: 300; font-weight: 300;
} }
@@ -210,19 +307,27 @@ header.masthead p {
} }
@media (min-width: 992px) { @media (min-width: 992px) {
header.article-img {
height: 50vh;
min-height: 300px;
padding-top: 0;
padding-bottom: 0;
}
header.masthead { header.masthead {
height: 100vh; height: 100vh;
min-height: 650px; min-height: 650px;
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
} }
header.masthead h1 {
header.article-img header.masthead h1 {
font-size: 3rem; font-size: 3rem;
} }
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
header.masthead h1 { header.article-img header.masthead h1 {
font-size: 4rem; font-size: 4rem;
} }
.btn-success { .btn-success {

View File

@@ -4,3 +4,37 @@
border-top-color: currentcolor; border-top-color: currentcolor;
border-color: black; border-color: black;
} }
.visible {
display: block;
}
.hidden {
display: none;
}
.msginfo
{
color: white;
font-weight:bold;
}
.msginfo .success
{
background-color: green;
}
.msginfo .error{
background-color: red;
}
.ql-toolbar {
background-color: #fff;
color: #000;
}
.ql-editor {
background-color: rgba(0, 0, 0, 0.3);
}

3
app/static/css/prism.min.css vendored Normal file
View File

@@ -0,0 +1,3 @@
/* PrismJS 1.30.0
https://prismjs.com/download#themes=prism-okaidia&languages=markup+css+clike+javascript+bash+c+cpp+markdown+markup-templating+nginx+php+python */
code[class*=language-],pre[data-language*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8292a2}.token.punctuation{color:#f8f8f2}.token.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

15
app/static/js/prism.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -12,7 +12,7 @@
dictDefaultMessage: 'Déposez vos fichiers ici!', dictDefaultMessage: 'Déposez vos fichiers ici!',
paramName: "file", paramName: "file",
maxFilesize: 1024, // MB maxFilesize: 1024, // MB
url: "/upload-dropzone", url: "{% if page %}/{{ page }}{% endif %}/upload-gallery/{{bloc.name}}",
chunking: true, chunking: true,
forceChunking: true, forceChunking: true,
chunkSize: 1000000, chunkSize: 1000000,

View File

@@ -0,0 +1,4 @@
{% if bloc.fg_color != bloc.bg_color %}
color: {{ bloc.fg_color }}; background-color: {{ bloc.bg_color }};
{% endif %}

View File

@@ -9,25 +9,37 @@
<button class="btn btn-outline-success" onclick="printForm()">+</button> <button class="btn btn-outline-success" onclick="printForm()">+</button>
<div class="formAddBloc" id="addBloc"> <div class="formAddBloc" id="addBloc">
<form action="/addBloc" method="POST" class="" > <form action="/addBloc" id="addBloc" method="POST" class="" >
<input id="csrf_token" name="csrf_token" type="hidden" value="{{ csrf_token() }}"> <input id="csrf_token" name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<h3>Créé un nouveau bloc</H3> <h3>Créé un nouveau bloc</H3>
<br> <br>
<label for="blocName">Nom du bloc (caractères sans accents uniquement) </label>
<label for="nom"><b>Nom du bloc </b></label><br/> <div class="input-group">
<input type="text" name="blocName" placeholder="Nom" id="blocName" required=""> <input type="text"
name="blocName"
class="form-control"
id="blocName"
placeholder="Bloc1"
required>
<div class="invalid-feedback">
Entrez le nom du bloc sans espaces ni caractères spéciaux
</div>
</div>
<br> <br>
<label for="link"><b>Type de bloc</b></label> <label for="link"><b>Type de bloc</b></label>
<br/> <br/>
<select id="blocType" name="blocType" size="8"> <select id="blocType" name="blocType" size="8" required>
<option value="presentation">Présentation</option> <option value="presentation"> Ecran d'Accueil </option>
<option value="presentation"> Image En-tête d'article </option>
<option value="text">Texte libre</option> <option value="text">Texte libre</option>
<option value="simple_article">Article Simple</option>
<option value="gallery">Galerie</option> <option value="gallery">Galerie</option>
<option value="external_link">Lien externe</option> <option value="external_link">Lien externe</option>
<option value="contact">Contact</option> <option value="contact">Contact</option>
</select> </select>
<br/> <br/>
<button type="submit" class="btn btn-success" <button type="submit" class="btn btn-success"
onclick="valid_nameBloc()"
hx-post="/addBloc" hx-post="/addBloc"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-target="#myblocs"> Créer le bloc </button> hx-target="#myblocs"> Créer le bloc </button>
@@ -39,9 +51,20 @@
</div> </div>
<script>
function valid_nameBloc()
{
var blocName = document.getElementById("blocName");
let name_field = blocName.value.trim();
let Valid = /^[a-z0-9]+$/i.test(name_field);
console.log(Valid);
if (!Valid) {
document.getElementById("blocName").setAttribute("class", "form-control is-invalid");
}
}
</script>

View File

@@ -2,7 +2,7 @@
<form class="sortblocs" action="/sortblocs" method="POST" id="myblocs"> <form class="sortblocs" action="/sortblocs" method="POST" id="myblocs">
<div id="myblocs" class="myblocs"> <div id="myblocs" class="myblocs">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{% for item in myblocs.keys() %} {% for item in myblocs.keys() %}
<div class="bloc" id="bloc-{{ item }}" style="margin-top: 10px;"> <div class="bloc" id="bloc-{{ item }}" style="margin-top: 10px;">
@@ -30,4 +30,4 @@
swapThreshold: 0.65, swapThreshold: 0.65,
})}; })};
</script> </script>

View File

@@ -1,9 +0,0 @@
{% for item in myblocs.keys() %}
<div class="bloc" id="bloc-{{ item }}" style="margin-top: 10px;">
<button class="btn"> {{ item }} - {{ myblocs[item]['type'] }} </button>
<input class="input-menu" id="bloc-{{ item }}" name="{{ item }}" value="" style="display:none;">
<button class="btn btn-danger" onclick="deleteItem(this)"> x </button>
</div>
{% endfor %}

View File

@@ -6,14 +6,14 @@
type="button" type="button"
data-toggle="modal" data-toggle="modal"
data-target="#addBloc"> data-target="#addBloc">
Agencer es blocs Blocs de cette page
</button> </button>
<button class="btn btn-info nav-brand js-scroll-trigger" <button class="btn btn-info nav-brand js-scroll-trigger"
type="button" type="button"
data-toggle="modal" data-toggle="modal"
data-target="#mymenu"> data-target="#mymenu">
Editer le menu Mon menu
</button> </button>
@@ -21,7 +21,7 @@
type="button" type="button"
data-toggle="modal" data-toggle="modal"
data-target="#mypages"> data-target="#mypages">
Gérer mes pages Mes pages
</button> </button>
<button class="btn btn-secondary" <button class="btn btn-secondary"
@@ -46,39 +46,19 @@
<div class="modal-content"> <div class="modal-content">
<!-- Modal Header --> <!-- Modal Header -->
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title"> Agencer mes blocs </h4> <h4 class="modal-title"> Mes blocs </h4>
<button type="button" class="close" data-dismiss="modal">&times;</button> <button type="button" class="close" data-dismiss="modal">&times;</button>
</div> </div>
<div class="modal-body text-center"> <div class="modal-body text-center">
{% include 'admin/_bloc-edit.html' %} {% include 'admin/_bloc-edit.html' %}
</div> </div>
<div class="modal-footer mx-auto"> <div class="modal-footer mx-auto">
<a class="btn btn-danger" href="/edit" > Annuler </a> <a class="btn btn-danger" href="/" > Annuler </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal" id="mypages">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title"> Mes pages </h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body text-center">
{% include 'admin/_pages-edit.html' %}
</div>
<div class="modal-footer mx-auto">
<a class="btn btn-danger" href="/edit" > Annuler </a>
</div>
</div>
</div>
</div>
<div class="modal" id="mymenu"> <div class="modal" id="mymenu">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
@@ -91,12 +71,32 @@
{% include 'admin/_menu-edit.html' %} {% include 'admin/_menu-edit.html' %}
</div> </div>
<div class="modal-footer mx-auto"> <div class="modal-footer mx-auto">
<a class="btn btn-danger" href="/edit" > Annuler </a> <a class="btn btn-danger" href="/" > Annuler </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="modal" id="mypages">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title"> Mes pages </h4>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body text-center">
{% include 'admin/_pages-edit.html' %}
</div>
<div class="modal-footer mx-auto">
<a class="btn btn-danger" href="/" > Annuler </a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" id="parameters"> <div class="modal" id="parameters">
<div class="modal-dialog modal-lg"> <div class="modal-dialog modal-lg">
@@ -134,8 +134,9 @@
<!-- Modal footer --> <!-- Modal footer -->
<div class="modal-footer mx-auto"> <div class="modal-footer mx-auto">
<a class="btn btn-danger" href="/edit" > Annuler </a> <a class="btn btn-danger" href="/" > Annuler </a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>

View File

@@ -52,7 +52,6 @@
</form> </form>
</div> </div>
<script src="/static/vendors/Sortable/Sortable.min.js"></script> <script src="/static/vendors/Sortable/Sortable.min.js"></script>
<script type="text/javascript"> <script type="text/javascript">

View File

@@ -1,35 +0,0 @@
<!--
<form class="sortblocs" action="/createpage" method="POST" id="mypages">
<div id="myblocs" class="myblocs">
{% for item in mypages %}
<div class="page" id="page" style="margin-top: 10px;">
<button class="btn"> {{ }} - {{ myblocs[item]['type'] }} </button>
<input class="input-menu" id="bloc-{{ item }}" name="{{ item }}" value="" style="display:none;">
<button class="btn btn-danger" onclick="deleteItem(this)"> x </button>
</div>
{% endfor %}
</div>
</form>
-->
<script src="/static/vendors/Sortable/Sortable.min.js"></script>
<script type="text/javascript">
var elements = document.getElementsByClassName('page');
for (var i = 0; i < elements.length; i++) {
new Sortable(elements[i], {
group: 'shared',
animation: 150,
invertSwap: true,
fallbackOnBody: true,
swapThreshold: 0.65,
})};
</script>

View File

@@ -1,24 +1,47 @@
<div id="mypages">
<ul style="list-style:none;" id="mypages" class="mypages">
<li><a href="/"> <button class="btn"> Page d'accueil </button> </a> </li>
<!-- {% for item in pagesList %}
<form class="sortblocs" action="/createpage" method="POST" id="mypages">
<div id="myblocs" class="myblocs"> <li style="padding-top: 10px;">
<a href="/{{ item }}/">
<button class="btn"> {{ item }} </button>
</a>
<a href="/rmPage/{{ item }}"><button class="btn btn-danger"> X </button></a>
</li>
{% endfor %}
</ul>
<div class="formAddPage" id="addPage">
<form action="/addPage" id="addPage" method="POST" class="" >
<input id="csrf_token" name="csrf_token" type="hidden" value="{{ csrf_token() }}">
<h3>Créé un nouveau bloc</H3>
<br>
<label for="blocName">Nom du bloc (caractères sans accents uniquement) </label>
<div class="input-group">
<input type="text"
name="pageName"
class="form-control"
id="pageName"
placeholder="Ma_Page"
required>
<div class="invalid-feedback">
Entrez le nom de la page sans espaces ni caractères spéciaux
</div> </div>
</form> </div>
<br>
--> <label for="link"><b>Type de bloc</b></label>
<script src="/static/vendors/Sortable/Sortable.min.js"></script> <br/>
<script type="text/javascript"> <select id="pageType" name="pageType" size="8" required>
<option value="article_image"> Article avec une image d'en tête </option>
var elements = document.getElementsByClassName('page'); <option value="simple_article"> Page blanche Texte libre </option>
</select>
for (var i = 0; i < elements.length; i++) { <br/>
<button type="submit" class="btn btn-success"
new Sortable(elements[i], { onclick="valid_nameBloc()"> Créer la page </button>
group: 'shared', </form>
animation: 150, </div>
invertSwap: true, </div>
fallbackOnBody: true,
swapThreshold: 0.65,
})};
</script>

View File

@@ -1,41 +0,0 @@
<header class="masthead text-center text-white d-flex" style="background-image: url('/static/img/{{ bloc.bg_img }}');">
<div class="container my-auto">
<form method='POST' action='/edit/accueil' enctype="multipart/form-data">
{{ form.csrf_token }}
<div class="row">
<div class="col-lg-10 form-group mx-auto">
{{ form.titre(value=bloc.title,
class="form-control form-control-lg text-center text-uppercase")
}}
{% for error in form.titre.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
<hr>
</div>
<div class="col-lg-8 form-group mx-auto">
{% set f = form.presentation.process_data(bloc.text) %}
{{ form.presentation(
class="form-control text-center",
rows="7")
}}
{% for error in form.presentation.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
<br/>
<div class="col-lg-4 form-group mx-auto">
{{ form.texte_boutton(value=bloc.text_button,
class="form-control text-center") }}
{% for error in form.texte_boutton.errors %}
<span style="color: red;">
{{ error }}</span>
{% endfor %}
</div>
<div class="col-lg-3 form-group mx-auto">
{{ form.image(class="form-control" ) }}
</div>
{{ form.submit() }}
</div>
</div>
</form>
</div>
</header>

View File

@@ -1,16 +0,0 @@
<header class="masthead text-center text-white d-flex" style="background-image: url('/static/img/{{ mesinfos.presentation.bg_img }}');" >
<div class="container my-auto">
<div class="row">
<div class="col-lg-10 mx-auto">
<h1 class="text-uppercase">
<strong> {{ mesinfos.presentation.title }} </strong>
</h1>
<hr>
</div>
<div class="col-lg-8 mx-auto">
<p class="text-faded mb-5"> {{ mesinfos.presentation.text }} </p>
<a class="btn btn-primary btn-xl js-scroll-trigger" href="#about">{{ mesinfos.presentation.text_button }} </a>
</div>
</div>
</div>
</header>

View File

@@ -1,34 +0,0 @@
<div class="container-fluid p-0">
<form method='POST' action='/edit/images/{{ bloc.name }}/{{ image.file }}' hx-swap="outerHTML" hx-target="#{{ bloc.name }}">
{{ form.csrf_token }}
<div class="row">
<div class="col-lg-2 mx-auto my-4 text-center">
<img class="img-fluid" src="/static/img/portfolio/thumbnails/{{ image.file }}" alt="{{ image.description }}">
</div>
<div class="col-lg-4 mx-auto my-4 text-center">
<h3> Titre </h3>
{{ form.title(value=image.title,
class="form-control form-control-lg text-center") }}
<hr class="light my-4">
<h3>Sous-titre</h3>
{{ form.subtitle(value=image.subtitle, class="form-control text-center") }}
<br/>
<h3> Catégorie </h3>
{{ form.category(value=image.category, class="form-control text-center") }}
<hr class="light my-4">
<h3> Description de votre image </h3>
<p> Une description de l'image est utile pour les personnes mal-voyantes et au référecement de votre site </p>
{{ form.description(value=image.description, class="form-control text-center") }}
<br/>
<button class="btn btn-success" type="submit" data-dismiss="modal"
hx-post="/edit/images/{{ bloc.name }}/{{ image.file }}"
hx-swap="outerHTML"
hx-target="#{{ bloc.name }}"> Valider </button>
<a class="btn btn-danger mx-auto" href="/edit/images/del/{{ image.nom_fichier }}">Supprimer l'image</a>
</div>
</div>
</form>
</div>

View File

@@ -1,11 +0,0 @@
<section class="bg-primary" id="about">
<div class="container">
<div class="row">
<div class="col-lg-8 mx-auto text-center">
<h2 class="section-heading text-white">{{ mesinfos.text.title }}</h2>
<hr class="light my-4">
<p class="text-faded mb-4"> {{ mesinfos.text.text }} </p>
</div>
</div>
</div>
</section>

View File

@@ -1,20 +0,0 @@
<header class="masthead text-center text-white d-flex" style="background-image: url('/static/img/{{ bloc.bg_img }}');" id="{{ bloc.name }}" >
<div class="container my-auto">
<div class="row">
<div class="col-lg-10 mx-auto">
<h1 class="text-uppercase">
<strong> {{ bloc.title }} </strong>
</h1>
<hr>
</div>
<div class="col-lg-8 mx-auto">
<p class="text-faded mb-5"> {{ bloc.text }} </p>
<a class="btn btn-primary btn-xl js-scroll-trigger" href="#">{{ bloc.text_button }}
</a>
</div>
</div>
</div>
</header>

View File

@@ -1,6 +0,0 @@
<form id="uploader" methods="POST" class="dropzone dz-clickable"></form>
{% include theme+'/blocs/edit/images_list.html' %}
{% include '_js_dropzone.html' %}

View File

@@ -1,30 +0,0 @@
{% extends 'creative/_layout.html' %}
{% block body %}
{% include theme+'/_nav.html' %}
{% include theme+'/_navtool.html' %}
{% for bloc in myblocs.values() %}
{% if blocEdit == bloc.name %}
{% include theme+'/_'+bloc.type+'-edit.html' %}
{% else %}
{% include theme+'/_'+bloc.type+'.html' %}
<button class="btn btn-success btn-xl pull-right"
style="margin-top:-7rem; margin-right:1rem;"
hx-get="/edit/{{ bloc.name }}"
hx-target="#{{bloc.name}}"
hw-swap="none"
> Éditer </button>
{% endif %}
{% endfor %}
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "creative/layout.html" %}
{% block body %}
<!-- Navigation -->
{% include 'creative/_nav.html' %}
{% for bloc in myblocs.values() %}
{% include 'creative/_'+bloc.type+'.html' %}
{% endfor %}
{% endblock %}

View File

@@ -1,15 +0,0 @@
{% extends "creative/layout.html" %}
{% block body %}
<!-- Navigation -->
{% include 'creative/_nav.html' %}
{% for bloc in myblocs.values() %}
{% include 'creative/_'+bloc.type+'.html' %}
{% endfor %}
{% endblock %}

View File

@@ -1,19 +0,0 @@
{# creative/layout.html #}
<!DOCTYPE html>
<html lang="fr">
<head>
<!-- Style CSS -->
{% include 'creative/_head.html' %}
</head>
<body id="page-top">
{% block body %}
{% endblock %}
</body>
{% include 'creative/javascript.html' %}
</html>

View File

@@ -1,10 +1,31 @@
<div class="text-center mx-auto">
<button class="btn btn-success" <button class="btn btn-success"
hx-post='/edit/{{ bloc.name }}' onclick="saveText()"
hx-swap="outerHTML" hx-post='{% if page %}/{{ page }}{% endif %}/edit/{{ bloc.name }}'
hx-swap="outerHTML swap:100ms"
hx-target="#{{ bloc.name }}" > Enregistrer </button> hx-target="#{{ bloc.name }}" > Enregistrer </button>
<button class="btn btn-danger" <button class="btn btn-danger"
hx-get="/view/{{ bloc.name }}" onclick="enableModBtn()"
hx-get="{% if page %}/{{ page }}{% endif %}/view/{{ bloc.name }}"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-target="#{{ bloc.name }}" > Annuler </button> hx-target="#{{ bloc.name }}" > Annuler </button>
</div>
<script>
function enableModBtn(){
document.getElementById("modify-{{ bloc.name }}").disabled = false;
}
function saveText(){
editor_content = document.getElementById("editor");
if (editor_content != null)
{
document.getElementById("text").innerHTML = quill.getSemanticHTML(0);
delete quill;
}
enableModBtn();
}
</script>

View File

@@ -0,0 +1,4 @@
<script>
document.getElementById("modify-{{ bloc.name }}").disabled = true;
</script>

View File

@@ -0,0 +1,52 @@
<div id="editor">
{{ bloc.text|safe }}
</div>
<br/>
<textarea style="display :none;" id="text" name="text"> </textarea>
<!-- Initialize Quill editor -->
<script>
var toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
['link', 'image'],
[{ 'header': 2 }, { 'header': 3 }, { 'header': 4},], // custom button values
[{ 'list': 'ordered'}, { 'list': 'bullet' }, { 'list': 'check' }],
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
[{ 'header': [ 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['clean'] // remove formatting button
];
var quill = new Quill('#editor', {
modules: {
syntax: true, // Include syntax module
toolbar: toolbarOptions // Include button in toolbar
},
theme: 'snow'
});
</script>
<style>
.ql-container {
height: fit-content;
}
.ql-editor {
height: fit-content;
}
</style>

View File

@@ -1,18 +1,22 @@
<h3> Nom du bloc : {{ bloc.name }} </h3>
<div class="row mx-md-5"> <div class="row mx-md-5">
<div class="col-auto"> <div class="col-auto">
<label> Nom du bloc </label> <label> Classes CSS </label>
{{ form.name(col=4,value=bloc.name, {{ form.property(col=4,value=bloc.property,
class="form-control text-center") }} class="form-control text-center") }}
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label> Couleur de l'arrière plan </label> <label> Couleur de l'arrière plan </label>
{{ form.bg_color(col=4, {{ form.bg_color(col=4,
class="form-control form-control-color text-center") }} class="form-control form-control-color text-center",
value=bloc.bg_color) }}
</div> </div>
<div class="col-auto"> <div class="col-auto">
<label> Couleur du texte</label> <label> Couleur du texte</label>
{{ form.fg_color(col=4, {{ form.fg_color(col=4,
class="form-control form-control-color text-center") }} class="form-control form-control-color text-center",
value=bloc.fg_color) }}
</div> </div>
</div> </div>
<br/> <br/>

View File

@@ -15,4 +15,9 @@
.msginfo .error{ .msginfo .error{
background-color: red; background-color: red;
} }
.ql-toolbar {
background-color: #fff;
color: #000;
}
</style> </style>

View File

@@ -1,18 +0,0 @@
<style>
.msginfo
{
color: white;
font-weight:bold;
}
.msginfo .success
{
background-color: green;
}
.msginfo .error{
background-color: red;
}
</style>

View File

@@ -2,25 +2,36 @@
{% block css %} {% block css %}
{% include 'edit-tools/css/dropzone' %} {% include 'edit-tools/css/dropzone' %}
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" rel="stylesheet">
<link href="/static/css/edit-toolbar.css" rel="stylesheet"> <link href="/static/css/edit-toolbar.css" rel="stylesheet">
<link href="/static/css/prism.min.css" rel="stylesheet">
<!-- Include the highlight.js library -->
<link href="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.snow.css" rel="stylesheet" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% include config.theme+'/_nav.html' %} {% include 'themes/'+config.theme+'/_nav.html' %}
{% include config.theme+'/_navtool.html' %} {% include 'admin/_menu-admin.html' %}
{% macro print_colors() %}
{% endmacro %}
{% for bloc in myblocs.values() %} {% for bloc in myblocs.values() %}
{% if blocEdit == bloc.name %} {% if blocEdit == bloc.name %}
{% include config.theme+'/blocs/edit/_'+bloc.type+'.html' %}
{% else %}
{% include config.theme+'/blocs/_'+bloc.type+'.html' %}
<button class="btn btn-success btn-xl pull-right" {% include 'themes/'+config.theme+'/blocs/edit/_'+bloc.type+'.html' %}
{% else %}
{% include 'themes/'+config.theme+'/blocs/_'+bloc.type+'.html' %}
<button class="btn btn-success btn-xl pull-right"
id="modify-{{ bloc.name }}"
style="margin-top:-7rem; margin-right:1rem;" style="margin-top:-7rem; margin-right:1rem;"
hx-get="/edit/{{ bloc.name }}" hx-get="{% if page %}/{{ page }}{% endif %}/edit/{{ bloc.name }}"
hx-target="#{{bloc.name}}" hx-target="#{{bloc.name}}"
hx-swap="oob"> Modifier </button> hx-swap="oob"> Modifier </button>
@@ -31,5 +42,13 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<!-- Include the Quill library -->
<script src="/static/vendors/htmx/htmx.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/cpp.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quill@2.0.3/dist/quill.js"></script>
<script src="/static/js/prism.js"></script>
{% endblock %} {% endblock %}

View File

@@ -3,7 +3,7 @@
<head> <head>
<!-- Style CSS --> <!-- Style CSS -->
{% include config.theme+'/_head.html' %} {% include 'themes/'+config.theme+'/_head.html' %}
{% block css %} {% block css %}
{% endblock %} {% endblock %}
</head> </head>
@@ -11,17 +11,17 @@
<body id="page-top"> <body id="page-top">
{% block body %} {% block body %}
{% include config.theme+'/_nav.html' %} {% include 'themes/'+config.theme+'/_nav.html' %}
{% for bloc in myblocs.values() %} {% for bloc in myblocs.values() %}
{% include config.theme+'/blocs/_'+bloc.type+'.html' %} {% include 'themes/'+config.theme+'/blocs/_'+bloc.type+'.html' %}
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}
</body> </body>
{% include config.theme+'/javascript.html' %} {% include 'themes/'+config.theme+'/javascript.html' %}
{% block js %} {% block js %}
{% endblock js %} {% endblock js %}
</html> </html>

View File

@@ -15,7 +15,7 @@
</a> </a>
<div class="dropdown-menu" aria-labelledby="{{ item }}"> <div class="dropdown-menu" aria-labelledby="{{ item }}">
{% for subitem in menu[item].keys() %} {% for subitem in menu[item].keys() %}
<a class="dropdown-item" href="{{item[subitem]}}">{{ subitem }}</a> <a class="dropdown-item" href="{{menu[item][subitem]}}">{{ subitem }}</a>
{% endfor %} {% endfor %}
</div> </div>
</li> </li>

View File

@@ -1,11 +1,10 @@
<section class="bg-primary" id="{{ bloc.name }}"> <section class="bg-primary {{ bloc.property }}" id="about">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-lg-8 mx-auto text-center"> <div class="col-lg-8 mx-auto text-center">
<h2 class="section-heading text-white">{{ bloc.title }}</h2> <h2 class="section-heading text-white">{{ bloc.title }}</h2>
<hr class="light my-4"> <hr class="light my-4">
<p class="text-faded mb-4"> {{ bloc.text }} </p> <p class="text-faded mb-4"> {{ bloc.text }} </p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,4 @@
<section id="contact" > <section id="contact" class="{{ bloc.property }}" style="{% include '_print_colors.css' %}">
<div class="container" id="{{ bloc.name }}"> <div class="container" id="{{ bloc.name }}">
<div class="row"> <div class="row">
<div class="col-lg-8 mx-auto text-center"> <div class="col-lg-8 mx-auto text-center">

View File

@@ -1,7 +1,7 @@
<section class="bg-dark text-white" id="{{ bloc.name }}" hx-swap-oob="true" > <section class="{{ bloc.property }}" style="{% include '_print_colors.css' %}" id="{{ bloc.name }}" >
<div class="container text-center"> <div class="container text-center">
<h2 class="mb-4">{{ bloc.title }}</h2> <h2 class="mb-4">{{ bloc.title }}</h2>
<p> {{ bloc.text }} </p> {{ bloc.text|safe }}
<a class="btn btn-light btn-xl sr-button" href="{{ bloc.link }}"> <a class="btn btn-light btn-xl sr-button" href="{{ bloc.link }}">
{{ bloc.text_button }} {{ bloc.text_button }}
</a> </a>

View File

@@ -1,12 +1,14 @@
<section class="p-0" id="{{ bloc.name }}"> <section class="p-0 {{ bloc.property }}" id="{{ bloc.name }}">
<div class="container-fluid p-0"> <div class="container-fluid p-0">
<div class="row no-gutters popup-gallery"> <div class="row no-gutters popup-gallery">
{% for image in bloc.values() %} {% for image in bloc.values() %}
{% if image.file %} {% if image.file %}
<div class="col-lg-4 col-sm-6 text-center"> <div class="col-lg-4 col-sm-6 text-center">
<a class="portfolio-box" href="/static/img/portfolio/fullsize/{{ image.file }}" alt="{{ image.description }}"> <a class="portfolio-box" href="/public/img/{{ image.file }}"
<img class="img-fluid" src="/static/img/portfolio/thumbnails/{{ image.file }}" alt="{{ image.description }}"> alt="{{ image.description }}">
<img class="img-fluid" src="/public/img/thumbnails/{{ image.file }}"
alt="{{ image.description }}">
<div class="portfolio-box-caption"> <div class="portfolio-box-caption">
<div class="portfolio-box-caption-content"> <div class="portfolio-box-caption-content">
<div class="project-category text-faded"> <div class="project-category text-faded">

View File

@@ -0,0 +1,14 @@
<header class="article-img text-center text-white d-flex" style="{% include '_print_colors.css' %} background-image: url('/public/img/{{ bloc.bg_img }}');" id="{{ bloc.name }}" >
<div class="container my-auto">
<div class="row" style="mix-blend-mode: hard-light;background-color: rgba(0,0,0,0.3);">
<div class="col-lg-10 mx-auto">
<h1 class="text-uppercase">
<strong> {{ bloc.title }} </strong>
</h1>
<hr>
<h3> {{ bloc.subtitle }} </h3>
</div>
</div>
</div>
</header>

View File

@@ -0,0 +1,25 @@
<header class="masthead text-center text-white d-flex" style="{% include '_print_colors.css' %} background-image: url('/public/img/{{ bloc.bg_img }}');" id="{{ bloc.name }}" >
<div class="container my-auto">
<div class="row" style="mix-blend-mode: hard-light;background-color: rgba(0,0,0,0.3);">
<div class="col-lg-10 mx-auto">
<h1 class="text-uppercase">
<strong> {{ bloc.title }} </strong>
</h1>
<hr>
</div>
<div class="col-lg-10 mx-auto">
<p class="text-faded mb-5"> {{ bloc.text|safe }} </p>
</div>
</div>
<div class="center mx-auto mt-5">
<a class="btn btn-primary btn-xl js-scroll-trigger" href="{{ bloc.link }}">
{{ bloc.text_button }}
</a>
</div>
</div>
</header>

View File

@@ -0,0 +1,11 @@
<section class="{{ bloc.property }}" id="{{ bloc.name }}" style="{{print_colors()}}" >
<h2 class="section-heading text-center">{{ bloc.title }}</h2>
<hr class="my-4">
<div class="container">
<div class="col-lg-8 mx-auto">
<p class="mb-4"> {{ bloc.text|safe }} </p>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,8 @@
<section class="{{ bloc.property }}" id="{{ bloc.name }}" style="{% include '_print_colors.css' %}">
<div class="container">
<div class="px-3 mx-auto">
{{ bloc.text|safe }}
</div>
</div>
</section>

View File

@@ -50,3 +50,4 @@
</form> </form>
</div> </div>
</section> </section>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -7,8 +7,8 @@
{{ form.title(value=bloc.title, {{ form.title(value=bloc.title,
class="form-control form-control-lg text-center") }} class="form-control form-control-lg text-center") }}
<h3> Description du lien </h3> <h3> Description du lien </h3>
{{ form.text(value=bloc.text, {% include 'edit-tools/_editor-wysiwyg.html' %}
class="form-control text-center") }}
<h3> Votre lien (https://exemple.com) </h3> <h3> Votre lien (https://exemple.com) </h3>
{{ form.link(value=bloc.link, class="form-control text-center") }} {{ form.link(value=bloc.link, class="form-control text-center") }}
<br /> <br />
@@ -23,4 +23,4 @@
{% include 'edit-tools/_btn-save-cancel.html' %} {% include 'edit-tools/_btn-save-cancel.html' %}
</form> </form>
</div> </div>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -0,0 +1,12 @@
<form id="uploader" methods="POST" class="dropzone dz-clickable">
{{ form.csrf_token }}
</form>
<div hx-get="{{ url_for('imagesList', page=page, blocname=bloc.name) }}" hx-trigger="load, every 7s">
</div>
<div id="editImage">
</div>
{% include '_js_dropzone.html' %}
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -0,0 +1,43 @@
<div class="container my-auto pt-5" style="background-color: rgba(0,0,0,0.3);">
<form method='POST' action='/edit/{{ bloc.name }}' enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#{{ bloc.name }}" hx-encoding="multipart/form-data">
{{ form.csrf_token }}
<div class="container my-auto">
<div class="row">
<div class="col-lg-10 form-group mx-auto">
{% include 'edit-tools/_form-base.html' %}
{{ form.title(value=bloc.title,
class="form-control form-control-lg text-center text-uppercase")
}}
{% for error in form.title.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
<hr>
</div>
<div class="col-lg-10 form-group mx-auto">
{{ form.subtitle(value=bloc.subtitle,
class="form-control form-control-lg text-center text-uppercase")
}}
{% for error in form.subtitle.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
</div>
<div class="col-lg-8 form-group mx-auto">
{{ form.image(class="form-control" ) }}
</div>
</div>
{% include 'edit-tools/_btn-save-cancel.html' %}
</form>
</div>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -1,10 +1,10 @@
<div class="container-fluid p-0"> <div class="container-fluid p-0" id="editImageInfo">
<form method='POST' action='/edit/images/{{ bloc.name }}/{{ image.file }}' hx-swap="outerHTML" hx-target="#editImg"> <form method='POST' action='/edit/images/{{ bloc.name }}/{{ image.file }}' hx-swap="outerHTML" hx-target="#editImg">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="row"> <div class="row">
<div class="col-lg-2 mx-auto my-4 text-center"> <div class="col-lg-2 mx-auto my-4 text-center">
<img class="img-fluid" src="/static/img/portfolio/thumbnails/{{ image.file }}" alt="{{ image.description }}"> <img class="img-fluid" src="/public/img/thumbnails/{{ image.file }}" alt="{{ image.description }}">
</div> </div>
<div class="col-lg-4 mx-auto my-4 text-center"> <div class="col-lg-4 mx-auto my-4 text-center">
<h3> Titre </h3> <h3> Titre </h3>
@@ -24,8 +24,11 @@
<button class="btn btn-success" type="submit" data-dismiss="modal" <button class="btn btn-success" type="submit" data-dismiss="modal"
hx-post="/edit/images/{{ bloc.name }}/{{ image.file }}" hx-post="/edit/images/{{ bloc.name }}/{{ image.file }}"
hx-swap="outerHTML" hx-swap="outerHTML"
hx-target="#editImg"> Valider </button> hx-target="#editImageInfo"
<a class="btn btn-danger mx-auto" href="/edit/images/del/{{ image.nom_fichier }}">Supprimer l'image</a> hx-swap="delete"> Valider </button>
<button class="btn btn-danger mx-auto" hx-get="{{ url_for('delImage', page=page, bloc=bloc.name, image=image.file) }}"
hx-swap="delete"
hx-target="#editImageInfo"> Supprimer l'image</button>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -1,5 +1,5 @@
<div class="container my-auto"> <div class="container mt-5 py-5 pb-5" style="width:100%;">
<form method='POST' action='/edit/{{ bloc.name }}' hx-swap="outerHTML" hx-target="#{{ bloc.name }}" enctype="multipart/form-data"> <form method='POST' action='/edit/{{ bloc.name }}' enctype="multipart/form-data" hx-swap="outerHTML" hx-target="#{{ bloc.name }}" hx-encoding="multipart/form-data">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="row"> <div class="row">
@@ -15,7 +15,8 @@
</div> </div>
<div class="col-lg-8 form-group mx-auto"> <div class="col-lg-8 form-group mx-auto">
{% set f = form.text.process_data(bloc.text) %} {% set f = form.text.process_data(bloc.text) %}
<textarea class="form-control text-center" id="text" name="text"> {{ bloc.text }} </textarea> {% include 'edit-tools/_editor-wysiwyg.html' %}
{% for error in form.text.errors %} {% for error in form.text.errors %}
<span style="color: red;">{{ error }}</span> <span style="color: red;">{{ error }}</span>
{% endfor %} {% endfor %}
@@ -27,11 +28,11 @@
<span style="color: red;"> <span style="color: red;">
{{ error }}</span> {{ error }}</span>
{% endfor %} {% endfor %}
<h4> cible du bouton </h4> <h4> cible du bouton #bloc ou un lien (https://exemple.com) </h4>
{{ form.link(value=bloc.link, class="form-control text-center") }} {{ form.link(value=bloc.link, class="form-control text-center") }}
<br /> <br />
</div>
<div class="col-lg-3 form-group mx-auto"> <div class="col-lg-8 form-group mx-auto">
{{ form.image(class="form-control" ) }} {{ form.image(class="form-control" ) }}
</div> </div>
{% include 'edit-tools/_btn-save-cancel.html' %} {% include 'edit-tools/_btn-save-cancel.html' %}
@@ -39,4 +40,4 @@
</div> </div>
</form> </form>
</div> </div>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -1,4 +1,4 @@
<section>
<div class="container"> <div class="container">
<form method='POST' action='/edit/{{ bloc.name }}' > <form method='POST' action='/edit/{{ bloc.name }}' >
{{ form.csrf_token }} {{ form.csrf_token }}
@@ -9,12 +9,12 @@
{{ form.title(col=5,value=bloc.title, {{ form.title(col=5,value=bloc.title,
class="form-control form-control-lg text-center") }} class="form-control form-control-lg text-center") }}
<hr class="light my-4"> <hr class="light my-4">
<textarea class="form-control text-center" id="text" name="text"> {{ bloc.text }} </textarea> {% include 'edit-tools/_editor-wysiwyg.html' %}
<br/>
{% include 'edit-tools/_btn-save-cancel.html' %} {% include 'edit-tools/_btn-save-cancel.html' %}
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</section> </section>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -0,0 +1,16 @@
<div class="container">
<form method='POST' action='/edit/{{ bloc.name }}' >
{{ form.csrf_token }}
<div class="row">
<div class="col-lg-8 mx-auto text-center">
{% include 'edit-tools/_form-base.html' %}
{% include 'edit-tools/_editor-wysiwyg.html' %}
</div>
</div>
{% include 'edit-tools/_btn-save-cancel.html' %}
</form>
</div>
</section>
{% include 'edit-tools/_disable-modify-button.html' %}

View File

@@ -1,11 +1,11 @@
<section class="p-0"> <section class="p-0" id="{{bloc.name}}">
<div class="container-fluid p-0" id="editImg"> <div class="container-fluid p-0" id="editImg">
<div class="row no-gutters "> <div class="row no-gutters ">
{% for image in bloc.values() %} {% for image in bloc.values() %}
{% if image.file %} {% if image.file %}
<div class="col-lg-4 col-sm-6 text-center"> <div class="col-lg-4 col-sm-6 text-center">
<a class="portfolio-box"> <a class="portfolio-box">
<img class="img-fluid" src="/static/img/portfolio/thumbnails/{{ image.file }}" alt="{{ image.description }}"> <img class="img-fluid" src="/public/img/thumbnails/{{ image.file }}" alt="{{ image.description }}">
<div class="portfolio-box-caption"> <div class="portfolio-box-caption">
<div class="portfolio-box-caption-content"> <div class="portfolio-box-caption-content">
<div class="project-category text-faded"> <div class="project-category text-faded">
@@ -20,7 +20,7 @@
</a> </a>
<button type="button" class="btn btn-success m-4" <button type="button" class="btn btn-success m-4"
hx-get="/edit/images/{{ bloc.name }}/{{ image.file }}" hx-get="/edit/images/{{ bloc.name }}/{{ image.file }}"
hx-target="#editImg" hx-target="#editImage"
hw-swap="none"> hw-swap="none">
<i class="fa fa-pencil-square-o"></i> <i class="fa fa-pencil-square-o"></i>
</button> </button>

View File

@@ -1,4 +1,9 @@
<script>
function rm_node(idName){
const element = document.getElementById(idName);
element.remove();
}
</script>
<!-- Bootstrap core JavaScript --> <!-- Bootstrap core JavaScript -->
<script src="/static/vendors/jquery/jquery.min.js"></script> <script src="/static/vendors/jquery/jquery.min.js"></script>

View File

@@ -0,0 +1,22 @@
{
"blocs": {
"bloc0": {
"type": "head_image",
"name": "bloc0",
"bg_color": "#000000",
"fg_color": "#000000",
"title": "PAge avec une image de presentation",
"bg_img": "bg_custom_bloc0image_entete_exemple.jpg",
"property": "",
"subtitle": "Votre sous-titre"
},
"bloc1": {
"name": "bloc1",
"type": "text",
"text": "<h3>Votre\u00a0texte\u00a0libre\u00a0!\u00a0</h3><p></p><p>Vous\u00a0pouvez\u00a0\u00e9crire\u00a0ce\u00a0que\u00a0vous\u00a0voulez\u00a0ici</p>",
"bg_color": "#000000",
"fg_color": "#000000",
"property": ""
}
}
}

View File

@@ -0,0 +1,20 @@
{
"blocs": {
"test_bloc": {
"name": "test_bloc",
"type": "text",
"title": "Salut c'est moi et ... voila quoi !!!!",
"text": "<h2 class=\"ql-align-center\">Votre\u00a0titre\u00a0</h2><pre data-language=\"bash\">\n#!/bin/bash\necho \"Un exemple de code\"\n</pre><p></p><h2>Titre\u00a02</h2><h3>Titre\u00a03</h3><h4>Titre\u00a04\u00a0</h4><p></p><p class=\"ql-align-right\"><span style=\"background-color: rgb(255, 153, 0);\">Youpi\u00a0!!!!!!!!!!!!!!!</span></p><p class=\"ql-align-center\"></p><blockquote>Une\u00a0citation</blockquote><p></p><p></p><p>Vous\u00a0pouvez\u00a0aussi\u00a0empiler\u00a0les\u00a0blocs\u00a0de\u00a0texte\u00a0poiur\u00a0les\u00a0mettre\u00a0de\u00a0couleur\u00a0diff\u00e9rentes\u00a0par\u00a0exemple\u00a0</p><p></p><p></p>",
"bg_color": "#000000",
"fg_color": "#000000",
"text_button": null,
"link": null,
"email": null,
"address": null,
"phone": null,
"postal_code": null,
"city": null,
"property": ""
}
}
}

View File

@@ -0,0 +1,451 @@
body,
html {
width: 100%;
height: 100%;
}
body {
font-family: 'Merriweather', 'Helvetica Neue', Arial, sans-serif;
}
hr {
max-width: 50px;
border-width: 3px;
border-color: #F05F40;
}
hr.light {
border-color: #fff;
}
a {
color: #F05F40;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
}
.btn-success{
z-index: 999;
position: relative;
}
.img-fluid {
position: relative;
z-index:-30;
}
a:hover {
color: #f05f40;
}
.modal-backdrop{
z-index:auto
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
}
.bg-primary {
background-color: #F05F40 !important;
}
.bg-dark {
background-color: #212529 !important;
}
.text-faded {
color: rgba(255, 255, 255, 0.7);
}
.ql-align-center {
text-align: center;
}
.ql-align-left {
text-align: left;
}
.ql-align-right {
text-align: right;
}
blockquote {
padding: 60px 80px 40px;
position: relative;
}
blockquote p {
font-size: 35px;
font-weight: 700px;
text-align: center;
}
/*blockquote p::before {
content: "\f095";
font-family: FontAwesome;
display: inline-block;
padding-right: 6px;
vertical-align: middle;
font-size: 180px;
}*/
blockquote:before {
position: absolute;
font-family: 'FontAwesome';
top: 0;
content:"\f10d";
font-size: 200px;
color: rgba(0,0,0,0.1);
}
blockquote::after {
content: "";
top: 20px;
left: 50%;
margin-left: -100px;
position: absolute;
height: 3px;
width: 200px;
}
blockquote{
font-size: 1.4em;
width:60%;
margin:50px auto;
font-style:italic;
color: #555555;
padding:1.2em 30px 1.2em 75px;
border-left:8px solid #f05f40;
line-height:1.6;
position: relative;
background:#EDEDED;
}
blockquote::before{
font-family:Arial;
content: "\201C";
color:#f05f40;
font-size:4em;
position: absolute;
left: 10px;
top:-10px;
}
blockquote::after{
content: '';
}
blockquote span{
display:block;
color:#333333;
font-style: normal;
font-weight: bold;
margin-top:1em;
}
section {
padding: 8rem 0;
z-index: -1;
}
.section-heading {
margin-top: 0;
}
::-moz-selection {
color: #fff;
background: #212529;
text-shadow: none;
}
::selection {
color: #fff;
background: #212529;
text-shadow: none;
}
img::selection {
color: #fff;
background: transparent;
}
img::-moz-selection {
color: #fff;
background: transparent;
}
#mainNav {
border-bottom: 1px solid rgba(33, 37, 41, 0.1);
background-color: #fff;
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
}
#mainNav .navbar-brand {
font-weight: 700;
text-transform: uppercase;
color: #F05F40;
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
}
#mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover {
color: #f05f40;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus {
font-size: .9rem;
font-weight: 700;
text-transform: uppercase;
color: #212529;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link:hover,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus:hover {
color: #F05F40;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link.active,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus.active {
color: #F05F40 !important;
background-color: transparent;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link.active:hover,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus.active:hover {
background-color: transparent;
}
@media (min-width: 992px) {
#mainNav {
border-color: transparent;
background-color: rgba(0,0,0,0.7);
}
#mainNav .navbar-brand {
color: rgba(255, 255, 255, 0.7);
}
#mainNav .navbar-brand:focus, #mainNav .navbar-brand:hover {
color: #fff;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link {
padding: 0.5rem 1rem;
}
#mainNav .navbar-nav > li.nav-item > a.nav-link,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus {
color: rgba(255, 255, 255, 0.7);
}
#mainNav .navbar-nav > li.nav-item > a.nav-link:hover,
#mainNav .navbar-nav > li.nav-item > a.nav-link:focus:hover {
color: #fff;
}
#mainNav.navbar-shrink {
border-bottom: 1px solid rgba(33, 37, 41, 0.1);
background-color: #fff;
}
#mainNav.navbar-shrink .navbar-brand {
color: #F05F40;
}
#mainNav.navbar-shrink .navbar-brand:focus, #mainNav.navbar-shrink .navbar-brand:hover {
color: #f05f40;
}
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link,
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:focus {
color: #212529;
}
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:hover,
#mainNav.navbar-shrink .navbar-nav > li.nav-item > a.nav-link:focus:hover {
color: #F05F40;
}
}
header.masthead {
padding-top: 10rem;
padding-bottom: calc(10rem - 56px);
background-position: center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
header.article-img {
padding-top: 10rem;
padding-bottom: calc(10rem - 56px);
background-position: center center;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
header.article-img header.masthead hr {
margin-top: 30px;
margin-bottom: 30px;
}
header.article-img header.masthead h1 {
font-size: 2rem;
}
header.article-img header.masthead p {
font-weight: 300;
}
@media (min-width: 768px) {
header.masthead p {
font-size: 1.15rem;
}
}
@media (min-width: 992px) {
header.article-img {
height: 50vh;
min-height: 300px;
padding-top: 0;
padding-bottom: 0;
}
header.masthead {
height: 100vh;
min-height: 650px;
padding-top: 0;
padding-bottom: 0;
}
header.article-img header.masthead h1 {
font-size: 3rem;
}
}
@media (min-width: 1200px) {
header.article-img header.masthead h1 {
font-size: 4rem;
}
.btn-success {
position: relative;
}
}
.service-box {
max-width: 400px;
}
.portfolio-box {
position: relative;
display: block;
max-width: 650px;
margin: 0 auto;
z-index: 0;
}
.portfolio-box .portfolio-box-caption {
position: absolute;
bottom: 0;
display: block;
width: 100%;
height: 100%;
text-align: center;
opacity: 0;
color: #fff;
background: rgba(240, 95, 64, 0.9);
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
transition: all 0.2s;
z-index: 0;
}
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content {
position: absolute;
top: 50%;
width: 100%;
transform: translateY(-50%);
text-align: center;
z-index:-10;
}
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-category,
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-name {
padding: 0 15px;
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
}
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-category {
font-size: 14px;
font-weight: 600;
text-transform: uppercase;
}
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-name {
font-size: 18px;
}
.portfolio-box:hover .portfolio-box-caption {
opacity: 1;
}
.portfolio-box:focus {
outline: none;
z-index: 0;
}
@media (min-width: 768px) {
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-category {
font-size: 16px;
}
.portfolio-box .portfolio-box-caption .portfolio-box-caption-content .project-name {
font-size: 22px;
}
}
.text-primary {
color: #F05F40 !important;
}
.btn {
font-weight: 700;
text-transform: uppercase;
border: none;
border-radius: 300px;
font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
z-index: 100;
}
.btn-xl {
padding: 1rem 2rem;
}
.btn-primary {
background-color: #F05F40;
border-color: #F05F40;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
color: #fff;
background-color: #ee4b28 !important;
}
.btn-primary:active, .btn-primary:focus {
box-shadow: 0 0 0 0.2rem rgba(240, 95, 64, 0.5) !important;
}
section.bg-dark p {
padding-bottom: 3rem;
}
section.bg-dark {
padding-top: 3rem;
padding-bottom: 3rem;
}

View File

@@ -0,0 +1,73 @@
@font-face {
font-family: 'Merryweather';
src:url("../fontes/Merryweather/Merryweather-Regular.woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Merryweather';
src:url("../fontes/Merryweather/Merryweather-Bold.woff");
font-weight: bold;
font-style: bold;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-regular.woff2");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-italic.woff2");
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-300.woff2");
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-300italic.woff2");
font-weight: 300;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-600.woff2");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-600italic.woff2");
font-weight: 600;
font-style: italic;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-700.woff2");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Open Sans';
src:url("../fontes/OpenSans/open-sans-v15-latin-700italic.woff2");
font-weight: 700;
font-style: italic;
}

View File

@@ -0,0 +1,31 @@
/*Import de fontes trouver sur font2u.com Bon site !!!! */
@font-face {
font-family: 'Lora';
src:url("../fonts/Lora-Regular.woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Raleway';
src:url("../fonts/Raleway-Regular.woff");
font-weight: normal;
}
@font-face {
font-family: 'Raleway';
src:url("../fonts/Raleway-Bold.woff");
font-weight: bold;
}
@font-face {
font-family:'Lora-Italic';
src:url("../fonts/Lora-Italic.woff");
}
@font-face {
font-family:'Roboto';
src:url("../fonts/Roboto-Regular.woff");
}

View File

@@ -0,0 +1,3 @@
/* PrismJS 1.30.0
https://prismjs.com/download#themes=prism-okaidia&languages=markup+css+clike+javascript+bash+c+cpp+markdown+markup-templating+nginx+php+python */
code[class*=language-],pre[data-language*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#272822}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#8292a2}.token.punctuation{color:#f8f8f2}.token.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#f92672}.token.boolean,.token.number{color:#ae81ff}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#a6e22e}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#e6db74}.token.keyword{color:#66d9ef}.token.important,.token.regex{color:#fd971f}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}

View File

@@ -0,0 +1,106 @@
(function($) {
"use strict"; // Start of use strict
// Smooth scrolling using jQuery easing
$('a.js-scroll-trigger[href*="#"]:not([href="#"])').click(function() {
if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
if (target.length) {
$('html, body').animate({
scrollTop: (target.offset().top - 57)
}, 1000, "easeInOutExpo");
return false;
}
}
});
// Closes responsive menu when a scroll trigger link is clicked
$('.js-scroll-trigger').click(function() {
$('.navbar-collapse').collapse('hide');
});
// Activate scrollspy to add active class to navbar items on scroll
$('body').scrollspy({
target: '#mainNav',
offset: 57
});
// Collapse Navbar
var navbarCollapse = function() {
if ($("#mainNav").offset().top > 100) {
$("#mainNav").addClass("navbar-shrink");
} else {
$("#mainNav").removeClass("navbar-shrink");
}
};
// Collapse now if page is not at top
navbarCollapse();
// Collapse the navbar when page is scrolled
$(window).scroll(navbarCollapse);
// Scroll reveal calls
window.sr = ScrollReveal();
sr.reveal('.sr-icons', {
duration: 600,
scale: 0.3,
distance: '0px'
}, 200);
sr.reveal('.sr-button', {
duration: 1000,
delay: 200
});
sr.reveal('.sr-contact', {
duration: 600,
scale: 0.3,
distance: '0px'
}, 300);
// Magnific popup calls
$('.popup-gallery').magnificPopup({
delegate: 'a',
type: 'image',
tLoading: 'Loading image #%curr%...',
mainClass: 'mfp-img-mobile',
gallery: {
enabled: true,
navigateByImgClick: true,
preload: [0, 1]
},
image: {
tError: '<a href="%url%">The image #%curr%</a> could not be loaded.'
}
});
$(".obfuscate").each(function () {
$(this).html($(this).html()
.replace("__chez__", "@").replace(/\.\.\./g, ".")
.replace(/Un/g, "1")
.replace(/Deux/g, "2")
.replace(/Trois/g, "3")
.replace(/Quatre/g, "4")
.replace(/Cinq/g, "5")
.replace(/Six/g, "6")
.replace(/Sept/g, "7")
.replace(/Huit/g, "8")
.replace(/Neuf/g, "9")
.replace(/Zero/g, "0"))
$(this).attr("href", $(this).attr("href")
.replace("__chez__", "@").replace(/\.\.\./g, ".")
.replace(/Un/g, "1")
.replace(/Deux/g, "2")
.replace(/Trois/g, "3")
.replace(/Quatre/g, "4")
.replace(/Cinq/g, "5")
.replace(/Six/g, "6")
.replace(/Sept/g, "7")
.replace(/Huit/g, "8")
.replace(/Neuf/g, "9")
.replace(/Zero/g, "0"))
});
})(jQuery); // End of use strict

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,331 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@-ms-viewport {
width: device-width;
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 0.5rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
}
address {
margin-bottom: 1rem;
font-style: normal;
line-height: inherit;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: .5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
}
sup {
top: -.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
pre {
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
svg {
overflow: hidden;
vertical-align: middle;
}
table {
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
input,
button,
select,
optgroup,
textarea {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
fieldset {
min-width: 0;
padding: 0;
margin: 0;
border: 0;
}
legend {
display: block;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
line-height: inherit;
color: inherit;
white-space: normal;
}
progress {
vertical-align: baseline;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
outline-offset: -2px;
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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