]> git.lizzy.rs Git - cheatdb.git/commitdiff
Add Content Warnings
authorrubenwardy <rw@rubenwardy.com>
Fri, 17 Jul 2020 19:48:51 +0000 (20:48 +0100)
committerrubenwardy <rw@rubenwardy.com>
Fri, 17 Jul 2020 19:48:51 +0000 (20:48 +0100)
app/blueprints/admin/__init__.py
app/blueprints/admin/warningseditor.py [new file with mode: 0644]
app/blueprints/packages/packages.py
app/models.py
app/templates/admin/list.html
app/templates/admin/warnings/edit.html [new file with mode: 0644]
app/templates/admin/warnings/list.html [new file with mode: 0644]
app/templates/packages/create_edit.html
app/templates/packages/view.html
migrations/versions/b370c3eb4227_.py [new file with mode: 0644]

index c48f97c4844704c2d46c38e7c796cbc8364ed65d..1aa06c3037743956366dc42ce2d50ac9e63829ab 100644 (file)
@@ -19,4 +19,4 @@ from flask import Blueprint
 
 bp = Blueprint("admin", __name__)
 
-from . import admin, licenseseditor, tagseditor, versioneditor, audit
+from . import admin, audit, licenseseditor, tagseditor, versioneditor, warningseditor
diff --git a/app/blueprints/admin/warningseditor.py b/app/blueprints/admin/warningseditor.py
new file mode 100644 (file)
index 0000000..418d052
--- /dev/null
@@ -0,0 +1,59 @@
+# ContentDB
+# Copyright (C) 2020  rubenwardy
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+
+from flask import *
+from flask_user import *
+from . import bp
+from app.models import *
+from flask_wtf import FlaskForm
+from wtforms import *
+from wtforms.validators import *
+from app.utils import rank_required
+
+@bp.route("/admin/warnings/")
+@rank_required(UserRank.ADMIN)
+def warning_list():
+       return render_template("admin/warnings/list.html", warnings=ContentWarning.query.order_by(db.asc(ContentWarning.title)).all())
+
+class WarningForm(FlaskForm):
+       title       = StringField("Title", [InputRequired(), Length(3,100)])
+       name        = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
+       description = TextAreaField("Description", [InputRequired(), Length(0, 500)])
+       submit      = SubmitField("Save")
+
+@bp.route("/admin/warnings/new/", methods=["GET", "POST"])
+@bp.route("/admin/warnings/<name>/edit/", methods=["GET", "POST"])
+@rank_required(UserRank.ADMIN)
+def create_edit_warning(name=None):
+       warning = None
+       if name is not None:
+               warning = ContentWarning.query.filter_by(name=name).first()
+               if warning is None:
+                       abort(404)
+
+       form = WarningForm(formdata=request.form, obj=warning)
+       if request.method == "POST" and form.validate():
+               if warning is None:
+                       warning = ContentWarning(form.title.data, form.description.data)
+                       db.session.add(warning)
+               else:
+                       form.populate_obj(warning)
+               db.session.commit()
+
+               return redirect(url_for("admin.warning_list"))
+
+       return render_template("admin/warnings/edit.html", warning=warning, form=form)
index 716e79ec28443a62ce285c810efe09990d47619d..5a775b2c8aaed9d23b27c4a9849f83e0426b4f6d 100644 (file)
@@ -198,22 +198,24 @@ def download(package):
 
 
 class PackageForm(FlaskForm):
-       name          = StringField("Name (Technical)", [InputRequired(), Length(1, 100), Regexp("^[a-z0-9_]+$", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
-       title         = StringField("Title (Human-readable)", [InputRequired(), Length(3, 100)])
-       short_desc     = StringField("Short Description (Plaintext)", [InputRequired(), Length(1,200)])
-       desc          = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
-       type          = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
-       license       = QuerySelectField("License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
-       media_license = QuerySelectField("Media License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
-       provides_str  = StringField("Provides (mods included in package)", [Optional()])
-       tags          = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
-       harddep_str   = StringField("Hard Dependencies", [Optional()])
-       softdep_str   = StringField("Soft Dependencies", [Optional()])
-       repo          = StringField("VCS Repository URL", [Optional(), URL()], filters = [lambda x: x or None])
-       website       = StringField("Website URL", [Optional(), URL()], filters = [lambda x: x or None])
-       issueTracker  = StringField("Issue Tracker URL", [Optional(), URL()], filters = [lambda x: x or None])
-       forums        = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
-       submit        = SubmitField("Save")
+       name             = StringField("Name (Technical)", [InputRequired(), Length(1, 100), Regexp("^[a-z0-9_]+$", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")])
+       title            = StringField("Title (Human-readable)", [InputRequired(), Length(3, 100)])
+       short_desc       = StringField("Short Description (Plaintext)", [InputRequired(), Length(1,200)])
+       desc             = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
+       type             = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
+       license          = QuerySelectField("License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
+       media_license    = QuerySelectField("Media License", [DataRequired()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
+       provides_str     = StringField("Provides (mods included in package)", [Optional()])
+       tags             = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
+       content_warnings = QuerySelectMultipleField('Content Warnings', query_factory=lambda: ContentWarning.query.order_by(db.asc(ContentWarning.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
+       harddep_str      = StringField("Hard Dependencies", [Optional()])
+       softdep_str      = StringField("Soft Dependencies", [Optional()])
+       repo             = StringField("VCS Repository URL", [Optional(), URL()], filters = [lambda x: x or None])
+       website          = StringField("Website URL", [Optional(), URL()], filters = [lambda x: x or None])
+       issueTracker     = StringField("Issue Tracker URL", [Optional(), URL()], filters = [lambda x: x or None])
+       forums           = IntegerField("Forum Topic ID", [Optional(), NumberRange(0,999999)])
+       submit           = SubmitField("Save")
+
 
 @bp.route("/packages/new/", methods=["GET", "POST"])
 @bp.route("/packages/<author>/<name>/edit/", methods=["GET", "POST"])
@@ -259,6 +261,7 @@ def create_edit(author=None, name=None):
                        form.softdep_str.data  = ",".join([str(x) for x in package.getSortedOptionalDependencies() ])
                        form.provides_str.data = MetaPackage.ListToSpec(package.provides)
                        form.tags.data         = list(package.tags)
+                       form.content_warnings.data = list(package.content_warnings)
 
        if request.method == "POST" and form.validate():
                wasNew = False
@@ -320,6 +323,10 @@ def create_edit(author=None, name=None):
                for tag in form.tags.raw_data:
                        package.tags.append(Tag.query.get(tag))
 
+               package.content_warnings.clear()
+               for warning in form.content_warnings.raw_data:
+                       package.content_warnings.append(ContentWarning.query.get(warning))
+
                db.session.commit() # save
 
                next_url = package.getDetailsURL()
index e6ff6f094c19e639f44886651166eb7f825d5b88..b2dbcc044f5e08ac0e66519d1bd009a74382ec73 100644 (file)
@@ -358,6 +358,11 @@ Tags = db.Table("tags",
     db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
 )
 
+ContentWarnings = db.Table("content_warnings",
+    db.Column("content_warning_id", db.Integer, db.ForeignKey("content_warning.id"), primary_key=True),
+    db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
+)
+
 maintainers = db.Table("maintainers",
     db.Column("user_id", db.Integer, db.ForeignKey("user.id"), primary_key=True),
     db.Column("package_id", db.Integer, db.ForeignKey("package.id"), primary_key=True)
@@ -488,6 +493,9 @@ class Package(db.Model):
        tags = db.relationship("Tag", secondary=Tags, lazy="select",
                        backref=db.backref("packages", lazy=True))
 
+       content_warnings = db.relationship("ContentWarning", secondary=ContentWarnings, lazy="select",
+                       backref=db.backref("packages", lazy=True))
+
        releases = db.relationship("PackageRelease", backref="package",
                        lazy="dynamic", order_by=db.desc("package_release_releaseDate"))
 
@@ -816,6 +824,23 @@ class MetaPackage(db.Model):
 
                return retval
 
+
+class ContentWarning(db.Model):
+       id              = db.Column(db.Integer, primary_key=True)
+       name            = db.Column(db.String(100), unique=True, nullable=False)
+       title           = db.Column(db.String(100), nullable=False)
+       description     = db.Column(db.String(500), nullable=False)
+
+       def __init__(self, title, description=""):
+               self.title       = title
+               self.description = description
+
+               import re
+               regex = re.compile("[^a-z_]")
+               self.name = regex.sub("", self.title.lower().replace(" ", "_"))
+
+
+
 class Tag(db.Model):
        id              = db.Column(db.Integer, primary_key=True)
        name            = db.Column(db.String(100), unique=True, nullable=False)
index 75fb61e6b5680e93184c0d76d3f3ed36c07eb687..211c86ddb81299d01c8941e8a69fbe189f3f2385 100644 (file)
@@ -12,6 +12,7 @@
                                <a class="list-group-item list-group-item-action" href="{{ url_for('admin.tag_list') }}">Tag Editor</a>
                                <a class="list-group-item list-group-item-action" href="{{ url_for('admin.license_list') }}">License Editor</a>
                                <a class="list-group-item list-group-item-action" href="{{ url_for('admin.version_list') }}">Version Editor</a>
+                               <a class="list-group-item list-group-item-action" href="{{ url_for('admin.warning_list') }}">Warning Editor</a>
                                <a class="list-group-item list-group-item-action" href="{{ url_for('admin.switch_user') }}">Sign in as another user</a>
                        </div>
                </div>
diff --git a/app/templates/admin/warnings/edit.html b/app/templates/admin/warnings/edit.html
new file mode 100644 (file)
index 0000000..2e6e71a
--- /dev/null
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+
+{% block title %}
+       {% if warning %}
+               Edit {{ warning.title }}
+       {% else %}
+               New warning
+       {% endif %}
+{% endblock %}
+
+{% block content %}
+       <a class="btn btn-primary float-right" href="{{ url_for('admin.create_edit_warning') }}">New Warning</a>
+       <a class="btn btn-secondary mb-4" href="{{ url_for('admin.warning_list') }}">Back to list</a>
+
+       {% from "macros/forms.html" import render_field, render_submit_field %}
+       <form method="POST" action="" enctype="multipart/form-data">
+               {{ form.hidden_tag() }}
+
+               {{ render_field(form.title) }}
+               {{ render_field(form.description) }}
+               {% if warning %}
+                       {{ render_field(form.name) }}
+               {% endif %}
+               {{ render_submit_field(form.submit) }}
+       </form>
+{% endblock %}
diff --git a/app/templates/admin/warnings/list.html b/app/templates/admin/warnings/list.html
new file mode 100644 (file)
index 0000000..96a299c
--- /dev/null
@@ -0,0 +1,52 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{ _("Warnings") }}
+{% endblock %}
+
+{% block content %}
+       <a class="btn btn-primary float-right" href="{{ url_for('admin.create_edit_warning') }}">{{ _("New Warning") }}</a>
+
+       <h1>{{ _("Warnings") }}</h1>
+
+       <p>
+               Also see <a href="/help/content_flags/">Package Flags</a>.
+       </p>
+
+       <div class="list-group">
+               <div class="list-group-item">
+                       <div class="row text-muted">
+                               <div class="col-sm-2">
+                                       {{ _("Name") }}
+                               </div>
+
+                               <div class="col-sm">
+                                       {{ _("Description") }}
+                               </div>
+
+                               <div class="col-sm-1 text-center">
+                                       {{ _("Packages") }}
+                               </div>
+                       </div>
+               </div>
+
+               {% for t in warnings %}
+                       <a class="list-group-item list-group-item-action"
+                                       href="{{ url_for('admin.create_edit_warning', name=t.name) }}">
+                               <div class="row">
+                                       <div class="col-sm-2">
+                                               {{ t.title }}
+                                       </div>
+
+                                       <div class="col-sm">
+                                               {{ t.description }}
+                                       </div>
+
+                                       <div class="col-sm-1 text-center">
+                                               {{ t.packages | count }}
+                                       </div>
+                               </div>
+                       </a>
+               {% endfor %}
+       </div>
+{% endblock %}
index 3e9277eba18a75f5776eb9639df5428f66dcfa47..44280bce0c36248c81c9397f11164efbd8d14c20 100644 (file)
@@ -66,6 +66,7 @@
                        </div>
                        {{ render_field(form.short_desc, class_="pkg_meta") }}
                        {{ render_multiselect_field(form.tags, class_="pkg_meta") }}
+                       {{ render_multiselect_field(form.content_warnings, class_="pkg_meta") }}
                        <div class="pkg_meta row">
                                {{ render_field(form.license, class_="not_txp col-sm-6") }}
                                {{ render_field(form.media_license, class_="col-sm-6") }}
index 00eea2ee0b193d167b61cdb6ca38de22d879db8e..f2c22e3aaf2ade5ac0b7bfaa82b50af540fa370a 100644 (file)
                                                {{ package_warning }}
                                        </a>
                                {% endif %}
+                               {% for warning in package.content_warnings %}
+                                       <a class="badge badge-warning" rel="nofollow" href="/help/content_flags/"
+                                                       title="{{ warning.description }}">
+                                               <i class="fas fa-exclamation-circle" style="margin-right: 0.3em;"></i>
+                                               {{ warning.title }}
+                                       </a>
+                               {% endfor %}
                                {% for t in package.tags %}
                                        <a class="badge badge-primary" rel="nofollow"
                                                href="{{ url_for('packages.list_all', tag=t.name) }}">{{ t.title }}</a>
diff --git a/migrations/versions/b370c3eb4227_.py b/migrations/versions/b370c3eb4227_.py
new file mode 100644 (file)
index 0000000..4377db5
--- /dev/null
@@ -0,0 +1,56 @@
+"""empty message
+
+Revision ID: b370c3eb4227
+Revises: c5e4213721dd
+Create Date: 2020-07-17 19:22:15.267179
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy import orm
+from app.models import ContentWarning
+
+
+# revision identifiers, used by Alembic.
+revision = 'b370c3eb4227'
+down_revision = 'c5e4213721dd'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+       # ### commands auto generated by Alembic - please adjust! ###
+       op.create_table('content_warning',
+       sa.Column('id', sa.Integer(), nullable=False),
+       sa.Column('name', sa.String(length=100), nullable=False),
+       sa.Column('title', sa.String(length=100), nullable=False),
+       sa.Column('description', sa.String(length=500), nullable=False),
+       sa.PrimaryKeyConstraint('id'),
+       sa.UniqueConstraint('name')
+       )
+       op.create_table('content_warnings',
+       sa.Column('content_warning_id', sa.Integer(), nullable=False),
+       sa.Column('package_id', sa.Integer(), nullable=False),
+       sa.ForeignKeyConstraint(['content_warning_id'], ['content_warning.id'], ),
+       sa.ForeignKeyConstraint(['package_id'], ['package.id'], ),
+       sa.PrimaryKeyConstraint('content_warning_id', 'package_id')
+       )
+
+       bind = op.get_bind()
+       session = orm.Session(bind=bind)
+
+       session.add(ContentWarning("Violence", "Non-cartoon violence"))
+       session.add(ContentWarning("Drugs", "Drugs or alcohol"))
+       session.add(ContentWarning("Bad Language"))
+       session.add(ContentWarning("Gambling"))
+       session.add(ContentWarning("Horror"))
+       session.commit()
+
+       # ### end Alembic commands ###
+
+
+def downgrade():
+       # ### commands auto generated by Alembic - please adjust! ###
+       op.drop_table('content_warnings')
+       op.drop_table('content_warning')
+       # ### end Alembic commands ###