author = db.relationship("User")
wip = db.Column(db.Boolean, server_default="0")
+ discarded = db.Column(db.Boolean, server_default="0")
type = db.Column(db.Enum(PackageType), nullable=False)
title = db.Column(db.String(200), nullable=False)
"posts": self.posts,
"views": self.views,
"is_wip": self.wip,
+ "discarded": self.discarded,
"created_at": self.created_at.isoformat(),
}
color: #7ac;
}
+.discardtopic {
+ text-decoration: line-through;
+ a {
+ color: #7ac;
+ }
+ filter: brightness(0.5);
+}
+
.editor-toolbar, .editor-toolbar.fullscreen {
margin-bottom: 0 !important;
background-color: #444 !important;
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
<link rel="stylesheet" type="text/css" href="/static/bootstrap.css">
- <link rel="stylesheet" type="text/css" href="/static/custom.css?v=2">
+ <link rel="stylesheet" type="text/css" href="/static/custom.css?v=3">
{% block headextra %}{% endblock %}
</head>
-{% macro render_topics_table(topics, show_author=True) -%}
+{% macro render_topics_table(topics, show_author=True, show_discard=False) -%}
<table class="table">
<tr>
- <th>Id</th>
<th></th>
<th>Title</th>
{% if show_author %}<th>Author</th>{% endif %}
<th>Actions</th>
</tr>
{% for topic in topics %}
- <tr{% if topic.wip %} class="wiptopic"{% endif %}>
- <td>{{ topic.topic_id }}</td>
+ <tr class="{% if topic.wip %}wiptopic{% endif %}{% if topic.discarded %}discardtopic{% endif %}">
<td>
[{{ topic.type.value }}]
</td>
{% endif %}
<td>{{ topic.name or ""}}</td>
<td>{% if topic.link %}<a href="{{ topic.link }}">{{ topic.link | domain }}</a>{% endif %}</td>
- <td>
- <a href="{{ url_for('create_edit_package_page', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">Create</a>
+ <td class="btn-group">
+ <a class="btn btn-primary"
+ href="{{ url_for('create_edit_package_page', author=topic.author.username, repo=topic.getRepoURL(), forums=topic.topic_id, title=topic.title, bname=topic.name) }}">
+ Create
+ </a>
+ {% if show_discard %}
+ <a class="btn btn-{% if topic.discarded %}success{% else %}danger{% endif %} topic-discard" data-tid={{ topic.topic_id }}>
+ {% if topic.discarded %}
+ Show
+ {% else %}
+ Discard
+ {% endif %}
+ </a>
+ {% endif %}
</td>
</tr>
{% endfor %}
{% endblock %}
{% block content %}
+ <a class="btn btn-primary float-right" href="{{ url_for('todo_topics_page', q=query, show_discarded=not show_discarded) }}">
+ {% if not show_discarded %}
+ Show
+ {% else %}
+ Hide
+ {% endif %}
+
+ Discarded Topics
+ </a>
+
<h1>Topics to be Added</h1>
<p>
- {{ total - (topic_count) }} / {{ total }} packages have been added.
- {{ topics | count }} remaining.
+ {{ total - (topic_count) }} / {{ total }} topics have been added as packages to CDB.
+ {{ topic_count }} remaining.
</p>
<form method="GET" action="{{ url_for('todo_topics_page') }}" class="my-4">
</form>
{% from "macros/topics.html" import render_topics_table %}
- {{ render_topics_table(topics) }}
+ {{ render_topics_table(topics, show_discard=True) }}
<ul class="pagination mt-4">
<li class="page-item {% if not prev_url %}disabled{% endif %}">
</li>
</ul>
{% endblock %}
+
+{% block scriptextra %}
+ <script>
+ var csrf_token = "{{ csrf_token() }}";
+ </script>
+ <script>
+ $(".topic-discard").click(function() {
+ var ele = $(this);
+ var tid = ele.attr("data-tid");
+ var discard = !ele.parent().parent().hasClass("discardtopic");
+ fetch(new Request("{{ url_for('topic_set_discard') }}?tid=" + tid +
+ "&discard=" + (discard ? "true" : "false"), {
+ method: "post",
+ credentials: "same-origin",
+ headers: {
+ "Accept": "application/json",
+ "X-CSRFToken": csrf_token,
+ },
+ })).then(function(response) {
+ response.text().then(function(txt) {
+ console.log(JSON.parse(txt));
+ if (JSON.parse(txt).discarded) {
+ ele.parent().parent().addClass("discardtopic");
+ ele.removeClass("btn-danger");
+ ele.addClass("btn-success");
+ ele.text("Show");
+ } else {
+ ele.parent().parent().removeClass("discardtopic");
+ ele.removeClass("btn-success");
+ ele.addClass("btn-danger");
+ ele.text("Discard");
+ }
+ }).catch(console.log)
+ }).catch(console.log)
+ });
+ </script>
+{% endblock %}
import bcrypt
plaintext = plaintext_str.encode("UTF-8")
password = bcrypt.hashpw(plaintext, bcrypt.gensalt())
- return password.decode("UTF-8")
+ if isinstance(password, str):
+ return password
+ else:
+ return password.decode("UTF-8")
def _do_login_user(user, remember_me=False):
def _call_or_get(v):
from flask_user import *
from app import app
from app.models import *
-from app.utils import is_package_page
+from app.utils import is_package_page, rank_required
from .packages import QueryBuilder
@app.route("/api/packages/")
.order_by(db.asc(ForumTopic.wip), db.asc(ForumTopic.name), db.asc(ForumTopic.title))
pkgs = [t.getAsDictionary() for t in query.all()]
return jsonify(pkgs)
+
+
+@app.route("/api/topic_discard/", methods=["POST"])
+@rank_required(UserRank.EDITOR)
+def topic_set_discard():
+ tid = request.args.get("tid")
+ discard = request.args.get("discard")
+ if tid is None or discard is None:
+ abort(400)
+
+ topic = ForumTopic.query.get(tid)
+ topic.discarded = discard == "true"
+ db.session.commit()
+
+ return jsonify(topic.getAsDictionary())
@app.route("/todo/topics/")
@login_required
def todo_topics_page():
- total = ForumTopic.query.count()
+ query = ForumTopic.query
- query = ForumTopic.query \
- .filter(~ db.exists().where(Package.forums==ForumTopic.topic_id)) \
- .order_by(db.asc(ForumTopic.wip), db.asc(ForumTopic.name), db.asc(ForumTopic.title))
+ show_discarded = request.args.get("show_discarded") == "True"
+ if not show_discarded:
+ query = query.filter_by(discarded=False)
+
+ total = query.count()
+
+ query = query.filter(~ db.exists().where(Package.forums==ForumTopic.topic_id)) \
+ .order_by(db.asc(ForumTopic.wip), db.asc(ForumTopic.name), db.asc(ForumTopic.title))
topic_count = query.count()
if query.has_prev else None
return render_template("todo/topics.html", topics=query.items, total=total, \
- topic_count=topic_count, query=search, \
+ topic_count=topic_count, query=search, show_discarded=show_discarded, \
next_url=next_url, prev_url=prev_url, page=page, page_max=query.pages)
--- /dev/null
+"""empty message
+
+Revision ID: a791b9b74a4c
+Revises: 44e138485931
+Create Date: 2018-12-23 23:52:02.010281
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'a791b9b74a4c'
+down_revision = '44e138485931'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column('forum_topic', sa.Column('discarded', sa.Boolean(), server_default='0', nullable=True))
+
+def downgrade():
+ op.drop_column('forum_topic', 'discarded')