Merge branch 'dev' of gitea.planet-casio.com:devs/PCv5 into dev
This commit is contained in:
commit
d2365a8444
|
@ -27,3 +27,7 @@ local_config.py
|
|||
## Wiki
|
||||
|
||||
wiki/
|
||||
|
||||
## Personal folder
|
||||
|
||||
exclude/
|
||||
|
|
|
@ -5,12 +5,15 @@ from flask_login import LoginManager
|
|||
from config import Config
|
||||
import time
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
|
||||
db = SQLAlchemy(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
from app.utils.converters import *
|
||||
app.url_map.converters['topicslug'] = TopicSlugConverter
|
||||
app.url_map.converters['forum'] = ForumConverter
|
||||
|
||||
@app.before_request
|
||||
def request_time():
|
||||
|
@ -26,9 +29,14 @@ from app import models # IDK why this is here, but it works
|
|||
from app.models.comment import Comment
|
||||
from app.models.thread import Thread
|
||||
from app.models.forum import Forum
|
||||
from app.models.topic import Topic
|
||||
from app.models.notification import Notification
|
||||
|
||||
from app.routes import index, search, users # To load routes at initialization
|
||||
from app.routes.account import login, account, notification
|
||||
from app.routes.admin import index, groups, account, trophies
|
||||
from app.routes.admin import index, groups, account, trophies, forums
|
||||
from app.routes.forum import index
|
||||
|
||||
from app.utils import pluralize # To use pluralize into the templates
|
||||
from app.utils import date
|
||||
from app.utils import is_title
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
# This file is a list of forums to create when setting up Planète Casio.
|
||||
#
|
||||
# * Keys are used as URLs paths and for unique identification.
|
||||
# * Prefixes represent the privilege category for a forum. Owning privileges
|
||||
# with this prefix allows the user to post in this forum and all its
|
||||
# sub-forum regardless of their settings ("forum-root-*" are hyper powerful).
|
||||
# * For open forums, use the prefix "open".
|
||||
|
||||
/:
|
||||
name: Forum de Planète Casio
|
||||
prefix: root
|
||||
|
||||
# News
|
||||
|
||||
/news:
|
||||
name: Actualités
|
||||
prefix: news
|
||||
|
||||
/news/projects:
|
||||
name: Actualités des projets
|
||||
prefix: projectnews
|
||||
descr: Nouveautés des projets de la communauté.
|
||||
|
||||
/news/calc:
|
||||
name: Actualités des constructeurs de calculatrices
|
||||
prefix: calcnews
|
||||
descr: Nouveautés CASIO, nouveaux modèles de calculatrices, mises à jour du
|
||||
système ou nouveautés d'autres constructeurs.
|
||||
|
||||
/news/events:
|
||||
name: Événements organisés par Planète Casio
|
||||
prefix: eventnews
|
||||
descr: Tous les événements organisés par Planète Casio ou la communauté.
|
||||
|
||||
/news/other:
|
||||
name: Autres nouveautés
|
||||
prefix: othernews
|
||||
descr: Actualités non catégorisées.
|
||||
|
||||
# Help
|
||||
|
||||
/help:
|
||||
name: Aide et questions
|
||||
prefix: help
|
||||
|
||||
/help/transfers:
|
||||
name: Questions sur les tranferts
|
||||
prefix: transferhelp
|
||||
descr: Questions sur le transfert de fichiers et l'installation de programmes
|
||||
sur la calculatrice.
|
||||
|
||||
/help/calc:
|
||||
name: Question sur l'utilisation des calculatrices
|
||||
prefix: calchelp
|
||||
descr: Questions sur l'utilisation des applications de la calculatrice,
|
||||
paramètres, formats de fichiers...
|
||||
|
||||
/help/prog:
|
||||
name: Questions de programmation
|
||||
prefix: proghelp
|
||||
descr: Questions sur le développement et le debuggage de programmes.
|
||||
|
||||
/help/other:
|
||||
name: Autres questions
|
||||
prefix: otherhelp
|
||||
descr: Questions non catégorisées.
|
||||
|
||||
# Projects
|
||||
|
||||
/projects:
|
||||
name: Forum des projets
|
||||
prefix: projects
|
||||
|
||||
/projects/games:
|
||||
name: Projets de jeux
|
||||
prefix: gameprojects
|
||||
descr: Projets de jeux pour calculatrices, tous langages confondus et tous
|
||||
modèles de calculatrices confondus.
|
||||
|
||||
/projects/apps:
|
||||
name: Projets d'applications, utilitaires, outils pour calculatrice
|
||||
prefix: appprojects
|
||||
descr: Projets d'applications (hors jeux) pour calculatrice, tous langages et
|
||||
modèles confondus.
|
||||
|
||||
/projects/tools:
|
||||
name: Projets pour d'autres plateformes
|
||||
prefix: toolprojetcs
|
||||
descr: Tous les projets tournant sur ordinateur, téléphone, ou toute autre
|
||||
plateforme que la calculatrice.
|
||||
|
||||
# Discussion
|
||||
|
||||
/discussion:
|
||||
name: Discussion
|
||||
prefix: discussion
|
||||
descr: Sujets hors-sujet et discussion libre.
|
|
@ -0,0 +1,15 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import TextAreaField
|
||||
|
||||
class EditorForm(FlaskForm):
|
||||
"""
|
||||
A text editor with formatting buttons and help. A rendering macro is
|
||||
defined in the template widgets/editor.html.
|
||||
"""
|
||||
|
||||
# TODO: How to set DataRequired() dynamically?
|
||||
contents = TextAreaField()
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.contents.data
|
|
@ -0,0 +1,9 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, FormField, SubmitField
|
||||
from wtforms.validators import DataRequired
|
||||
from app.forms.editor import EditorForm
|
||||
|
||||
class TopicCreationForm(FlaskForm):
|
||||
title = StringField('Nom du sujet', validators=[DataRequired()])
|
||||
message = FormField(EditorForm, 'Premier post')
|
||||
submit = SubmitField('Créer le sujet')
|
|
@ -5,22 +5,36 @@ class Comment(Post):
|
|||
__tablename__ = 'comment'
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
# ID of the associated Post object
|
||||
# ID of the underlying Post object
|
||||
id = db.Column(db.Integer, db.ForeignKey('post.id'), primary_key=True)
|
||||
|
||||
# Standalone properties
|
||||
# Comment contents
|
||||
text = db.Column(db.UnicodeText)
|
||||
|
||||
# Relations
|
||||
# Parent thread
|
||||
thread_id = db.Column(db.Integer, db.ForeignKey('thread.id'),
|
||||
nullable=False)
|
||||
thread = db.relationship('Thread', backref='comments',
|
||||
foreign_keys=thread_id)
|
||||
|
||||
# attachement = db.relationship('Attachement', backref='comment')
|
||||
|
||||
def __init__(self, author, text, thread):
|
||||
Post.__init__(author, text)
|
||||
if isinstance(thread, Thread):
|
||||
thread = thread.id
|
||||
self.thread_id = thread
|
||||
"""
|
||||
Create a new Comment in a thread.
|
||||
|
||||
Arguments:
|
||||
author -- comment poster (User)
|
||||
text -- contents (unicode string)
|
||||
thread -- parent discussion thread (Thread)
|
||||
"""
|
||||
|
||||
Post.__init__(self, author)
|
||||
self.thread = thread
|
||||
|
||||
def edit(self, new_text):
|
||||
"""Edit a Comment's contents."""
|
||||
|
||||
self.text = new_text
|
||||
self.touch()
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Comment: #{self.id}>'
|
||||
|
|
|
@ -5,22 +5,36 @@ class Forum(db.Model):
|
|||
__tablename__ = 'forum'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# Standalone properties
|
||||
# Forum name, as displayed on the site (eg. "Problèmes de transfert")
|
||||
name = db.Column(db.Unicode(64))
|
||||
slug = db.Column(db.Unicode(64))
|
||||
description = db.Column(db.UnicodeText)
|
||||
# Privilege prefix (sort of slug) for single-forum privileges (lowercase)
|
||||
prefix = db.Column(db.Unicode(64))
|
||||
# Forum description, as displayed on the site
|
||||
descr = db.Column(db.UnicodeText)
|
||||
# Forum URL, for dynamic routes
|
||||
url = db.Column(db.String(64))
|
||||
|
||||
# Relationships
|
||||
parent_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=True)
|
||||
parent = db.relationship('Forum', backref='sub_forums', remote_side=id,
|
||||
lazy=True)
|
||||
lazy=True, foreign_keys=parent_id)
|
||||
|
||||
# Also [topics] which is provided by a backref from the Topic class
|
||||
|
||||
def __init__(self, name, description, priv_prefix):
|
||||
def __init__(self, url, name, prefix, descr="", parent=None):
|
||||
self.url = url
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.priv_prefix = priv_prefix
|
||||
self.descr = descr
|
||||
self.prefix = prefix
|
||||
|
||||
if isinstance(parent, str):
|
||||
self.parent = Forum.query.filter_by(url=str).first()
|
||||
else:
|
||||
self.parent = parent
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Forum: {self.name}>'
|
||||
|
||||
def post_count(self):
|
||||
"""Number of posts in every topic of the forum, without subforums."""
|
||||
return sum(len(thread.comments) for thread in self.topics)
|
||||
|
|
|
@ -1,47 +1,58 @@
|
|||
from datetime import datetime
|
||||
from app import db
|
||||
from app.models.users import User
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
class Post(db.Model):
|
||||
""" Content a User can create and publish """
|
||||
__tablename__ = 'post'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
"""Contents created and published by Users."""
|
||||
|
||||
__tablename__ = 'post'
|
||||
|
||||
# Unique Post ID for the whole site
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
# Post type (polymorphic discriminator)
|
||||
type = db.Column(db.String(20))
|
||||
|
||||
# Creation and edition date
|
||||
date_created = db.Column(db.DateTime)
|
||||
date_modified = db.Column(db.DateTime)
|
||||
|
||||
# Post author
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
author = db.relationship('User', backref="posts",foreign_keys=author_id)
|
||||
|
||||
# TODO: Post attachments?
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
'polymorphic_on': type
|
||||
}
|
||||
|
||||
# Standalone properties
|
||||
date_created = db.Column(db.DateTime)
|
||||
date_modified = db.Column(db.DateTime)
|
||||
def __init__(self, author):
|
||||
"""
|
||||
Create a new Post.
|
||||
|
||||
# Relationships
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||
Arguments:
|
||||
author -- post author (User)
|
||||
"""
|
||||
|
||||
def __init__(self, author, text):
|
||||
""" Create a Post """
|
||||
self.text = text
|
||||
if isinstance(author, User):
|
||||
author = author.id
|
||||
self.author_id = author
|
||||
self.author = author
|
||||
self.date_created = datetime.now()
|
||||
self.date_modified = datetime.now()
|
||||
|
||||
def update(self, text):
|
||||
""" Update a post. Check whether the request sender has the right to do
|
||||
this! """
|
||||
self.text = text
|
||||
def touch(self):
|
||||
"""Touch a Post when it is edited."""
|
||||
|
||||
self.date_modified = datetime.now()
|
||||
|
||||
def change_ownership(self, new_author):
|
||||
""" Change ownership of a post. Check whether the request sender has the
|
||||
right to do this! """
|
||||
if isinstance(new_author, User):
|
||||
new_author = new_author.id
|
||||
self.author_id = new_author
|
||||
"""
|
||||
Change ownership of a Post. This is a privileged operation!
|
||||
|
||||
Arguments:
|
||||
new_author -- new post author (User)
|
||||
"""
|
||||
|
||||
self.author = new_author
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Post: #{self.id}>'
|
||||
|
|
|
@ -1,35 +1,40 @@
|
|||
from app import db
|
||||
from app.models.post import Post
|
||||
from app.models.comment import Comment
|
||||
from config import V5Config
|
||||
|
||||
class Thread(Post):
|
||||
""" Some thread, such as a topic, program, tutorial """
|
||||
class Thread(db.Model):
|
||||
"""Some thread, such as a topic, program, tutorial."""
|
||||
|
||||
# Foreign Post object ID
|
||||
__tablename__ = 'thread'
|
||||
id = db.Column(db.Integer, db.ForeignKey('post.id'), primary_key=True)
|
||||
|
||||
# Identify threads as a type of posts using the table name, and add a
|
||||
# column to further discriminate types of threads
|
||||
thread_type = db.Column(db.String(20))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
'polymorphic_on': thread_type
|
||||
}
|
||||
# Unique ID
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# Properties
|
||||
title = db.Column(db.Unicode(V5Config.THREAD_NAME_MAXLEN))
|
||||
# Also a relation [comments] populated from the Comment class.
|
||||
|
||||
# Relations
|
||||
# Top comment
|
||||
top_comment_id = db.Column(db.Integer, db.ForeignKey('comment.id'))
|
||||
top_comment = db.relationship('Comment', foreign_keys=top_comment_id)
|
||||
|
||||
def __init__(self, author, text, title):
|
||||
""" Create a Thread """
|
||||
Post.__init__(author, text)
|
||||
self.title = title
|
||||
# Also a relation [comments] populated from the Comment class.
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create a empty Thread. Normally threads are not meant to be empty, so
|
||||
you should create a Comment with this thread as parent, then assign it
|
||||
as top comment with a call to set_top_comment().
|
||||
"""
|
||||
self.top_comment_id = None
|
||||
|
||||
def set_top_comment(self, top_comment):
|
||||
"""
|
||||
Changes the top comment of the thread. The old top comment will become
|
||||
visible in the flow of posts?
|
||||
|
||||
Arguments:
|
||||
top_comment -- new top comment, must belong to this thread
|
||||
"""
|
||||
|
||||
if top_comment not in self.comments:
|
||||
raise Exception("Cannot set foreign comment as top thread comment")
|
||||
|
||||
self.top_comment = top_comment
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Thread #{self.id}'
|
||||
return f'<Thread: #{self.id}>'
|
||||
|
|
|
@ -1,22 +1,44 @@
|
|||
from app import db
|
||||
from app.models.thread import Thread
|
||||
from app.models.post import Post
|
||||
from config import V5Config
|
||||
|
||||
|
||||
class Topic(Thread):
|
||||
class Topic(Post):
|
||||
__tablename__ = 'topic'
|
||||
id = db.Column(db.Integer, db.ForeignKey('thread.id'), primary_key=True)
|
||||
__mapper_args__ = {'polymorphic_identity': __tablename__}
|
||||
|
||||
# Relationships
|
||||
# ID of the underlying [Post] object
|
||||
id = db.Column(db.Integer, db.ForeignKey('post.id'), primary_key=True)
|
||||
|
||||
# Topic title
|
||||
title = db.Column(db.Unicode(V5Config.THREAD_NAME_MAXLEN))
|
||||
|
||||
# Parent forum
|
||||
forum_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=False)
|
||||
forum = db.relationship('Forum', backref='topics',foreign_keys=forum_id)
|
||||
|
||||
def __init__(self, author, text, title, forum):
|
||||
""" Create a Topic """
|
||||
Post.__init__(author, text, title)
|
||||
if isinstance(forum, Forum):
|
||||
forum = forum.id
|
||||
self.forum_id = forum
|
||||
# Associated thread
|
||||
thread_id = db.Column(db.Integer,db.ForeignKey('thread.id'),nullable=False)
|
||||
thread = db.relationship('Thread', foreign_keys=thread_id)
|
||||
|
||||
# Number of views in the forum
|
||||
views = db.Column(db.Integer)
|
||||
|
||||
def __init__(self, forum, author, title, thread):
|
||||
"""
|
||||
Create a Topic.
|
||||
|
||||
Arguments:
|
||||
forum -- parent forum or sub-forum (Forum)
|
||||
author -- post author (User)
|
||||
title -- topic title (unicode string)
|
||||
thread -- discussion thread attached to the topic (Thread)
|
||||
"""
|
||||
|
||||
Post.__init__(self, author)
|
||||
self.title = title
|
||||
self.views = 0
|
||||
self.thread = thread
|
||||
self.forum = forum
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Topic #{self.id}'
|
||||
return f'<Topic: #{self.id}>'
|
||||
|
|
|
@ -27,8 +27,7 @@ class User(UserMixin, db.Model):
|
|||
# User type (polymorphic discriminator)
|
||||
type = db.Column(db.String(30))
|
||||
|
||||
# TODO: add good relation
|
||||
posts = db.relationship('Post', backref="author", lazy=True)
|
||||
# Also a [posts] relationship populated from the Post class.
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': __tablename__,
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
from app.utils.priv_required import priv_required
|
||||
from app.utils.render import render
|
||||
from app.models.forum import Forum
|
||||
from app import app, db
|
||||
|
||||
@app.route('/admin/forums', methods=['GET'])
|
||||
@priv_required('access-admin-panel')
|
||||
def adm_forums():
|
||||
main_forum = Forum.query.filter_by(parent=None).first()
|
||||
|
||||
return render('admin/forums.html', main_forum=main_forum)
|
|
@ -0,0 +1,42 @@
|
|||
from flask_login import current_user
|
||||
from flask import request
|
||||
|
||||
from app.utils.render import render
|
||||
from app.forms.forum import TopicCreationForm
|
||||
from app.models.forum import Forum
|
||||
from app.models.topic import Topic
|
||||
from app.models.thread import Thread
|
||||
from app.models.comment import Comment
|
||||
from app import app, db
|
||||
|
||||
@app.route('/forum/')
|
||||
def forum_index():
|
||||
main_forum = Forum.query.filter_by(parent=None).first()
|
||||
return render('/forum/index.html', main_forum=main_forum)
|
||||
|
||||
@app.route('/forum/<forum:f>/', methods=['GET', 'POST'])
|
||||
def forum_page(f):
|
||||
form = TopicCreationForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
# TODO: Check user privileges for this specific forum!
|
||||
|
||||
# First create the thread, then the comment, then the topic
|
||||
th = Thread()
|
||||
db.session.add(th)
|
||||
db.session.commit()
|
||||
|
||||
c = Comment(current_user, form.message.value, th)
|
||||
th.set_top_comment(c)
|
||||
t = Topic(f, current_user, form.title.data, th)
|
||||
|
||||
db.session.add(th)
|
||||
db.session.add(c)
|
||||
db.session.add(t)
|
||||
db.session.commit()
|
||||
|
||||
return render('/forum/forum.html', f=f, form=form)
|
||||
|
||||
@app.route('/forum/<forum:f>/<topicslug:t>')
|
||||
def forum_topic(f, t):
|
||||
return render('/forum/topic.html', f=f, t=t)
|
|
@ -1,12 +1,14 @@
|
|||
from flask import redirect, url_for
|
||||
from app import app
|
||||
from app.models.users import Member
|
||||
from app.utils import unicode_names
|
||||
from app.utils.render import render
|
||||
|
||||
|
||||
@app.route('/user/<username>')
|
||||
def user(username):
|
||||
member = Member.query.filter_by(name=username).first_or_404()
|
||||
norm = unicode_names.normalize(username)
|
||||
member = Member.query.filter_by(norm=norm).first_or_404()
|
||||
return render('user.html', member=member)
|
||||
|
||||
|
||||
|
|
|
@ -86,3 +86,10 @@
|
|||
.trophies-panel p label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Editor */
|
||||
|
||||
.editor textarea {
|
||||
font-family: monospace;
|
||||
height: 192px;
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ nav a:focus {
|
|||
margin: 8px 0; padding: 5px 2%;
|
||||
font-size: 14px; color: inherit;
|
||||
border: none; border-color: #141719;
|
||||
border-radius: 2px;
|
||||
}
|
||||
#menu form input[type="text"]:focus,
|
||||
#menu form input[type="password"]:focus {
|
||||
|
@ -201,7 +202,7 @@ nav a:focus {
|
|||
}
|
||||
#menu form input[type="submit"] {
|
||||
width: 100%;
|
||||
margin: 16px 0 5px 0;
|
||||
margin: 8px 0 5px 0;
|
||||
}
|
||||
#menu form label {
|
||||
font-size: 13px; color: #FFFFFF; opacity: .7;
|
||||
|
|
|
@ -17,3 +17,38 @@ table th {
|
|||
table td {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
/* Forum and sub-forum listings */
|
||||
|
||||
table.forumlist {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
|
||||
margin: 16px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* table.forumlist th {
|
||||
background: #d05950;
|
||||
border-color: #b04940;
|
||||
color: white;
|
||||
} */
|
||||
|
||||
table.forumlist tr {
|
||||
background: unset;
|
||||
}
|
||||
table.forumlist tr:nth-child(4n+2),
|
||||
table.forumlist tr:nth-child(4n+3) {
|
||||
background: rgba(0, 0, 0, .05);
|
||||
}
|
||||
|
||||
/* Topic table */
|
||||
|
||||
table.topiclist {
|
||||
width: 90%;
|
||||
margin: auto;
|
||||
}
|
||||
table.topiclist tr > *:nth-child(n+2) {
|
||||
/* This matches all children except the first column */
|
||||
text-align: center;
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,38 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{# This macro will allow us to perform recursive HTML generation #}
|
||||
{% macro forumtree(f, level) %}
|
||||
<tr>
|
||||
<td><code>{{ f.url }}</code></td>
|
||||
<td style='padding-left: {{ 6+24*level }}px'>
|
||||
<a href='/forum{{ f.url }}'>{{ f.name }}</a>
|
||||
</td>
|
||||
<td>{{ f.topics | length }}</td>
|
||||
<td>{{ f.post_count() }}</td>
|
||||
</tr>
|
||||
|
||||
{% for subf in f.sub_forums %}
|
||||
{{ forumtree(subf, level+1) }}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% block title %}
|
||||
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Forums</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<p>Cette page permet de gérer l'arbre des forums.</p>
|
||||
|
||||
<h2>Arbre des forums</h2>
|
||||
|
||||
{% if main_forum == None %}
|
||||
<p>Il n'y a aucun forum.</p>
|
||||
{% else %}
|
||||
<table style='width: 90%; margin: auto'>
|
||||
<tr><th>URL</th><th>Nom</th><th>Sujets</th><th>Messages</th></tr>
|
||||
{{ forumtree(main_forum, 0) }}
|
||||
</table>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -8,8 +8,9 @@
|
|||
<section>
|
||||
<p>Pages générales du panneau d'administration :</p>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
|
||||
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
|
||||
<li><a href="{{ url_for('adm_trophies') }}">Titres et trophées</a></li>
|
||||
<li><a href="{{ url_for('adm_forums') }}">Arbre des forums</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<nav>
|
||||
<ul id="light-menu">
|
||||
<a id="logo" href="{{ url_for('index') }}">
|
||||
<img src="{{ url_for('static',filename= 'images/logo_noshadow.png') }}" alt="logo"/>
|
||||
<img src="{{ url_for('static',filename= 'images/logo_noshadow-small.png') }}" alt="logo"/>
|
||||
</a>
|
||||
|
||||
<li>
|
||||
|
|
|
@ -48,9 +48,9 @@
|
|||
<form method="post" action="{{url_for('login')}}" class="login form">
|
||||
{{ login_form.hidden_tag() }}
|
||||
{{ login_form.username.label }}
|
||||
{{ login_form.username(size=32, placeholder="Identifiant") }}
|
||||
{{ login_form.username(size=32) }}
|
||||
{{ login_form.password.label }}
|
||||
{{ login_form.password(size=32, placeholder="Mot de passe") }}
|
||||
{{ login_form.password(size=32) }}
|
||||
{{ login_form.submit(class_="bg-green") }}
|
||||
{{ login_form.remember_me.label }} {{ login_form.remember_me() }}
|
||||
</form>
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
</svg>
|
||||
Forum
|
||||
</h2>
|
||||
<a href='/forum'>Index du forum</a>
|
||||
|
||||
<hr>
|
||||
|
||||
<a href="#">Vie communautaire</a>
|
||||
<a href="#">Projets de programmation</a>
|
||||
<a href="#">Questions et problèmes</a>
|
||||
|
@ -12,7 +16,7 @@
|
|||
<a href="#">Administration</a>
|
||||
<a href="#">CreativeCalc</a>
|
||||
|
||||
<hr />
|
||||
<hr>
|
||||
|
||||
<h3>Derniers commentaires</h3>
|
||||
<ul>
|
||||
|
|
|
@ -5,12 +5,16 @@
|
|||
</svg>
|
||||
Actualités
|
||||
</h2>
|
||||
<a href="#">Casio</a>
|
||||
<a href="#">Arduino</a>
|
||||
<a href="#">Projets communautaires</a>
|
||||
<a href="#">Divers</a>
|
||||
<a href='/forum/news'>Toutes les nouveautés</a>
|
||||
|
||||
<hr />
|
||||
<hr>
|
||||
|
||||
<a href='/forum/news/calc'>Nouveautés Casio</a>
|
||||
<a href='/forum/news/projects'>Projets communutaires</a>
|
||||
<a href='/forum/news/events'>Événements de Planète Casio</a>
|
||||
<a href='/forum/news/other'>Autres nouveautés</a>
|
||||
|
||||
<hr>
|
||||
|
||||
<h3>Derniers articles</h3>
|
||||
<ul>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<h1>403 - Accès non autorisé</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>403 - Accès non autorisé</h1>
|
||||
|
||||
<img src="{{url_for('static', filename = 'images/403.webp')}}" style="display:block;margin:auto"; />
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<h1>404 - Page non trouvée</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>404 - Page non trouvée</h1>
|
||||
|
||||
<img src="{{url_for('static', filename = 'images/404.webp')}}" style="display:block;margin:auto"; />
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{% extends "base/base.html" %}
|
||||
{% import "widgets/editor.html" as widget_editor %}
|
||||
|
||||
{% block title %}
|
||||
<a href='/forum'>Forum de Planète Casio</a> » <h1>{{ f.name }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<p>{{ f.descr }}</p>
|
||||
|
||||
{% if f.topics %}
|
||||
<table class=topiclist>
|
||||
<tr><th>Sujet</th><th>Auteur</th><th>Date de création</th>
|
||||
<th>Commentaires</th><th>Vues</th></tr>
|
||||
|
||||
{% for t in f.topics %}
|
||||
<tr><td>{{ t.title }}</td>
|
||||
<td><a href='/user/{{ t.author.norm }}'>{{ t.author.name }}</a></td>
|
||||
<td>{{ t.date_created | date }}</td>
|
||||
<td>{{ t.comments | length }}</td>
|
||||
<td>{{ t.views }} </td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p>Il n'y a aucun topic sur ce forum ! Animons-le vite !</p>
|
||||
{% endif %}
|
||||
<div class=form>
|
||||
|
||||
<h2>Créer un nouveau sujet</h2>
|
||||
|
||||
<form action="" method="post" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div>
|
||||
{{ form.title.label }}
|
||||
{{ form.title() }}
|
||||
{% for error in form.title.errors %}
|
||||
<span class="msgerror">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{ widget_editor.editor(form.message) }}
|
||||
|
||||
<div>{{ form.submit(class_='bg-green') }}</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{% block title %}
|
||||
<h1>Forum de Planète Casio</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<p>Bienvenue sur le forum de Planète Casio ! Vous pouvez créer des
|
||||
nouveaux sujets ou poster des réponses avec un compte ou en postant en
|
||||
tant qu'invité.</p>
|
||||
|
||||
{% if main_forum == None %}
|
||||
<p>Il n'y a aucun forum.</p>
|
||||
{% else %}
|
||||
|
||||
{% for l1 in main_forum.sub_forums %}
|
||||
<table class=forumlist>
|
||||
<tr><th>{{ l1.name }}</th><th>Nombre de sujets</th></tr>
|
||||
|
||||
{% if l1.sub_forums == [] %}
|
||||
<tr><td><a href='/forum{{ l1.url }}'>{{ l1.name }}</td>
|
||||
<td>{{ l1.topics | length }}</td></tr>
|
||||
<tr><td>{{ l1.descr }}</td><td></td></tr>
|
||||
{% endif %}
|
||||
|
||||
{% for l2 in l1.sub_forums %}
|
||||
<tr><td><a href='/forum{{ l2.url }}'>{{ l2.name }}</td>
|
||||
<td>{{ l2.topics | length }}</td></tr>
|
||||
<tr><td>{{ l2.descr }}</td><td></td></tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
|||
{% macro editor(form) %}
|
||||
<div class=editor>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ form.label }}
|
||||
{{ form.contents() }}
|
||||
{% for error in form.contents.errors %}
|
||||
<span class=msgerror>{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endmacro %}
|
|
@ -0,0 +1,57 @@
|
|||
"""
|
||||
utils.converter: Custom URL converters to match patterns in @app.route()
|
||||
|
||||
The Flask documentation is elusive on this topic. To add a new converter,
|
||||
proceed as follows:
|
||||
|
||||
1. Define a new converter class.
|
||||
2. Set the [regex] attribute to decide which portion of the URL will be
|
||||
considered for conversion (apparently the default is everything until next
|
||||
slash or end of string).
|
||||
3. Define the to_python() and to_url() methods to actually convert.
|
||||
4. Add the class to __all__ at the bottom of this file.
|
||||
5. In app/__init__.py, add a dictionary entry to [app.url_map.converters].
|
||||
|
||||
For more information, see the Werkzeug documentation:
|
||||
<https://werkzeug.palletsprojects.com/en/0.15.x/routing/#custom-converters>
|
||||
"""
|
||||
|
||||
from werkzeug.routing import BaseConverter, ValidationError
|
||||
from app.models.forum import Forum
|
||||
import re
|
||||
import sys
|
||||
|
||||
class ForumConverter(BaseConverter):
|
||||
|
||||
# This regex will decide which portion of the URL is matched by the curtom
|
||||
# converter. By default, slashes are not included, so we must add them.
|
||||
regex = r'[a-z/]+'
|
||||
|
||||
def to_python(self, url):
|
||||
url = '/' + url
|
||||
f = Forum.query.filter_by(url=url).first()
|
||||
if f is None:
|
||||
raise ValidationError(f"ForumConverter: no forum with url {url}")
|
||||
return f
|
||||
|
||||
def to_url(self, forum):
|
||||
return forum.url[1:]
|
||||
|
||||
class TopicSlugConverter(BaseConverter):
|
||||
|
||||
# Only catch integers followed by an optional slug string
|
||||
regex = r'(\d+)(?:-[\w-]*)?'
|
||||
|
||||
def to_python(self, url):
|
||||
"""Convert an URL pattern to a Python object, or raise an exception."""
|
||||
m = re.fullmatch(TopicSlugConverter.regex, url)
|
||||
if m is None:
|
||||
raise Exception(f"TopicSlugConverter: conversation failed")
|
||||
|
||||
return int(m[1], 10)
|
||||
|
||||
def to_url(self, topic_id):
|
||||
return str(topic_id)
|
||||
|
||||
# Export only the converter classes
|
||||
__all__ = "ForumConverter TopicSlugConverter".split()
|
|
@ -0,0 +1,9 @@
|
|||
from app import app
|
||||
|
||||
@app.template_filter('date')
|
||||
def filter_date(date):
|
||||
"""
|
||||
Print a date in a human-readable format.
|
||||
"""
|
||||
|
||||
return date.strftime("%d %b %Y à %H:%M")
|
49
master.py
49
master.py
|
@ -4,6 +4,7 @@ from app import app, db
|
|||
from app.models.users import Member, Group, GroupPrivilege
|
||||
from app.models.privs import SpecialPrivilege
|
||||
from app.models.trophies import Trophy, Title, TrophyMember
|
||||
from app.models.forum import Forum
|
||||
from app.utils import unicode_names
|
||||
import os
|
||||
import sys
|
||||
|
@ -19,6 +20,7 @@ Type a category name to see a list of elements. Available categories are:
|
|||
'groups' Privilege groups
|
||||
'trophies' Trophies
|
||||
'trophy-members' Trophies owned by members
|
||||
'forums' Forum tree
|
||||
|
||||
Type a category name followed by 'clear' to remove all entries in the category.
|
||||
|
||||
|
@ -36,6 +38,8 @@ the database.
|
|||
Type 'add-group <member> #<group-id>' to add a new member to a group.
|
||||
|
||||
Type 'create-trophies' to reset trophies and titles.
|
||||
|
||||
Type 'create-forums' to reset the forum tree.
|
||||
"""
|
||||
|
||||
#
|
||||
|
@ -84,6 +88,19 @@ def trophy_members(*args):
|
|||
for m in t.owners:
|
||||
print(f" {m}")
|
||||
|
||||
def forums(*args):
|
||||
if args == ("clear",):
|
||||
for f in Forum.query.all():
|
||||
db.session.delete(f)
|
||||
db.session.commit()
|
||||
print("Removed all forums.")
|
||||
return
|
||||
|
||||
for f in Forum.query.all():
|
||||
parent = f"in {f.parent.url}" if f.parent is not None else "root"
|
||||
print(f"{f.url} ({parent}) [{f.prefix}]: {f.name}")
|
||||
print(f" {f.descr}")
|
||||
|
||||
#
|
||||
# Creation and edition
|
||||
#
|
||||
|
@ -163,6 +180,36 @@ def create_trophies():
|
|||
|
||||
print(f"Created {len(tr)} trophies.")
|
||||
|
||||
def create_forums():
|
||||
# Clean up forums
|
||||
forums("clear")
|
||||
|
||||
# Create the forum tree
|
||||
fr = []
|
||||
success = 0
|
||||
with open(os.path.join(app.root_path, "data", "forums.yaml")) as fp:
|
||||
fr = yaml.safe_load(fp.read())
|
||||
|
||||
for url, f in fr.items():
|
||||
if url == "/":
|
||||
parent = None
|
||||
else:
|
||||
parent_url = url.rsplit('/', 1)[0]
|
||||
if parent_url == "":
|
||||
parent_url = "/"
|
||||
parent = Forum.query.filter_by(url=parent_url).first()
|
||||
|
||||
if parent is None:
|
||||
print(f"error: no parent with url {parent_url} for {url}")
|
||||
continue
|
||||
|
||||
f = Forum(url, f['name'], f['prefix'], f.get('descr', ''), parent)
|
||||
db.session.add(f)
|
||||
success += 1
|
||||
|
||||
db.session.commit()
|
||||
print(f"Created {success} forums.")
|
||||
|
||||
def add_group(member, group):
|
||||
if group[0] != "#":
|
||||
print(f"error: group id {group} should start with '#'")
|
||||
|
@ -194,8 +241,10 @@ commands = {
|
|||
"groups": groups,
|
||||
"trophies": trophies,
|
||||
"trophy-members": trophy_members,
|
||||
"forums": forums,
|
||||
"create-groups-and-privs": create_groups_and_privs,
|
||||
"create-trophies": create_trophies,
|
||||
"create-forums": create_forums,
|
||||
"add-group": add_group,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
"""restructure forum models
|
||||
|
||||
Revision ID: 2a1165f6ad0a
|
||||
Revises: a3fb8937ae16
|
||||
Create Date: 2019-09-07 19:38:57.472404
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2a1165f6ad0a'
|
||||
down_revision = 'a3fb8937ae16'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('thread_id_fkey', 'thread', type_='foreignkey')
|
||||
op.drop_column('thread', 'title')
|
||||
op.drop_column('thread', 'thread_type')
|
||||
op.add_column('topic', sa.Column('thread_id', sa.Integer(), nullable=False))
|
||||
op.add_column('topic', sa.Column('title', sa.Unicode(length=32), nullable=True))
|
||||
op.drop_constraint('topic_id_fkey', 'topic', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'topic', 'post', ['id'], ['id'])
|
||||
op.create_foreign_key(None, 'topic', 'thread', ['thread_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'topic', type_='foreignkey')
|
||||
op.drop_constraint(None, 'topic', type_='foreignkey')
|
||||
op.create_foreign_key('topic_id_fkey', 'topic', 'thread', ['id'], ['id'])
|
||||
op.drop_column('topic', 'title')
|
||||
op.drop_column('topic', 'thread_id')
|
||||
op.add_column('thread', sa.Column('thread_type', sa.VARCHAR(length=20), autoincrement=False, nullable=True))
|
||||
op.add_column('thread', sa.Column('title', sa.VARCHAR(length=32), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key('thread_id_fkey', 'thread', 'post', ['id'], ['id'])
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,32 @@
|
|||
"""Remove Thread table
|
||||
|
||||
Revision ID: 2dbb614a7236
|
||||
Revises: b890d9bb207b
|
||||
Create Date: 2019-09-08 12:32:58.869143
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2dbb614a7236'
|
||||
down_revision = 'b890d9bb207b'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('thread')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('thread',
|
||||
sa.Column('top_comment_id', sa.INTEGER(), autoincrement=False, nullable=True),
|
||||
sa.Column('id', sa.INTEGER(), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['top_comment_id'], ['comment.id'], name='thread_top_comment_id_fkey')
|
||||
)
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,28 @@
|
|||
"""add forum urls
|
||||
|
||||
Revision ID: 49427f8eb285
|
||||
Revises: a7aac1469393
|
||||
Create Date: 2019-09-02 21:16:06.971807
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '49427f8eb285'
|
||||
down_revision = 'a7aac1469393'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('url', sa.String(length=64), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('forum', 'url')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,33 @@
|
|||
"""Recreate Thread table
|
||||
|
||||
Revision ID: 4e05b43b18b1
|
||||
Revises: 2dbb614a7236
|
||||
Create Date: 2019-09-08 12:33:30.647277
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4e05b43b18b1'
|
||||
down_revision = '2dbb614a7236'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('thread',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('top_comment_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['top_comment_id'], ['comment.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('thread')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""add topics
|
||||
|
||||
Revision ID: a3fb8937ae16
|
||||
Revises: ebca7362eb22
|
||||
Create Date: 2019-09-05 21:35:48.260827
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a3fb8937ae16'
|
||||
down_revision = 'ebca7362eb22'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('topic',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('forum_id', sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['forum_id'], ['forum.id'], ),
|
||||
sa.ForeignKeyConstraint(['id'], ['thread.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('topic')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""forum editions
|
||||
|
||||
Revision ID: a7aac1469393
|
||||
Revises: e3b140752719
|
||||
Create Date: 2019-09-02 21:12:52.236043
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a7aac1469393'
|
||||
down_revision = 'e3b140752719'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('descr', sa.UnicodeText(), nullable=True))
|
||||
op.add_column('forum', sa.Column('prefix', sa.Unicode(length=64), nullable=True))
|
||||
op.drop_column('forum', 'slug')
|
||||
op.drop_column('forum', 'description')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True))
|
||||
op.add_column('forum', sa.Column('slug', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
|
||||
op.drop_column('forum', 'prefix')
|
||||
op.drop_column('forum', 'descr')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""Recreate Thread references
|
||||
|
||||
Revision ID: abd5f8de0106
|
||||
Revises: 4e05b43b18b1
|
||||
Create Date: 2019-09-08 12:34:12.574672
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'abd5f8de0106'
|
||||
down_revision = '4e05b43b18b1'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('comment', sa.Column('thread_id', sa.Integer(), nullable=False))
|
||||
op.create_foreign_key(None, 'comment', 'thread', ['thread_id'], ['id'])
|
||||
op.add_column('topic', sa.Column('thread_id', sa.Integer(), nullable=False))
|
||||
op.create_foreign_key(None, 'topic', 'thread', ['thread_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'topic', type_='foreignkey')
|
||||
op.drop_column('topic', 'thread_id')
|
||||
op.drop_constraint(None, 'comment', type_='foreignkey')
|
||||
op.drop_column('comment', 'thread_id')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""Remove Thread references
|
||||
|
||||
Revision ID: b890d9bb207b
|
||||
Revises: 2a1165f6ad0a
|
||||
Create Date: 2019-09-08 12:29:38.650491
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'b890d9bb207b'
|
||||
down_revision = '2a1165f6ad0a'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('comment_thread_id_fkey', 'comment', type_='foreignkey')
|
||||
op.drop_column('comment', 'thread_id')
|
||||
op.drop_constraint('topic_thread_id_fkey', 'topic', type_='foreignkey')
|
||||
op.drop_column('topic', 'thread_id')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('topic', sa.Column('thread_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.create_foreign_key('topic_thread_id_fkey', 'topic', 'thread', ['thread_id'], ['id'])
|
||||
op.add_column('comment', sa.Column('thread_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.create_foreign_key('comment_thread_id_fkey', 'comment', 'thread', ['thread_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,28 @@
|
|||
"""Add number of views to Topic
|
||||
|
||||
Revision ID: c665488fc26e
|
||||
Revises: abd5f8de0106
|
||||
Create Date: 2019-09-08 16:49:50.448779
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'c665488fc26e'
|
||||
down_revision = 'abd5f8de0106'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('topic', sa.Column('views', sa.Integer(), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('topic', 'views')
|
||||
# ### end Alembic commands ###
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
Revision ID: ebca7362eb22
|
||||
Revises: e3b140752719
|
||||
Rebased on: 49427f8eb285 (Lephenixnoir)
|
||||
Create Date: 2019-09-01 11:36:25.962212
|
||||
|
||||
"""
|
||||
|
@ -11,7 +12,7 @@ import sqlalchemy as sa
|
|||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'ebca7362eb22'
|
||||
down_revision = 'e3b140752719'
|
||||
down_revision = '49427f8eb285'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
|
Loading…
Reference in New Issue