~cedric/newspipe

1c020cb13ca54d42a47c8f418921194f9044dea5 — Cédric Bonhomme 1 year, 1 month ago d2a569b
new: [categories] when creating or editing a category it is now possible to select feeds via a select multiple field.
M newspipe/templates/edit_category.html => newspipe/templates/edit_category.html +12 -0
@@ 13,6 13,13 @@
                {% for error in form.name.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
            </div>
            <div class="form-group">
                <label for="{{ form.name.id }}" class="col-sm-3 control-label">{{ form.feeds.label }}</label>
                <div class="col-sm-9">
                    {{ form.feeds(class_="selectpicker", **{'data-live-search':'true', 'data-width':'auto'}) }}
                </div>
                {% for error in form.feeds.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
            </div>
            <div class="form-group">
                <div class="col-sm-offset-3 col-sm-9">
                    {{ form.submit(class_="btn btn-primary") }}
                </div>


@@ 20,4 27,9 @@
        </form>
    </div>
</div><!-- /.container -->
<script>
$(document).ready(function() {
    $('.selectpicker').selectpicker();
});
</script>
{% endblock %}

M newspipe/templates/layout.html => newspipe/templates/layout.html +5 -2
@@ 10,12 10,15 @@
    <link rel="shortcut icon" href="{{ url_for("static", filename="img/favicon.ico") }}" />
    <!-- CSS -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/bootstrap/dist/css/bootstrap.min.css') }}" media="screen" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/bootstrap-select/dist/css/bootstrap-select.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='npm_components/fork-awesome/css/fork-awesome.min.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/custom.css') }}" />
    <!-- JavaScript -->
    <script type="text/javascript" src="{{ url_for('static', filename = 'npm_components/jquery/dist/jquery.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename = 'npm_components/bootstrap/dist/js/bootstrap.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/jquery/dist/jquery.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/popper.js/dist/umd/popper.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/bootstrap/dist/js/bootstrap.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/bootstrap-select/dist/js/bootstrap-select.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net/js/jquery.dataTables.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='npm_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js') }}"></script>
    {% endblock %}

M newspipe/web/forms.py => newspipe/web/forms.py +6 -0
@@ 36,6 36,7 @@ from wtforms import (
    IntegerField,
    PasswordField,
    SelectField,
    SelectMultipleField,
    SubmitField,
    TextAreaField,
    TextField,


@@ 238,8 239,13 @@ class AddFeedForm(FlaskForm):

class CategoryForm(FlaskForm):
    name = TextField(lazy_gettext("Category name"))
    feeds = SelectMultipleField(lazy_gettext("Feeds in this category"), coerce=int)
    submit = SubmitField(lazy_gettext("Save"))

    def set_feed_choices(self, feeds):
        self.feeds.choices = []
        self.feeds.choices += [(feed.id, feed.title) for feed in feeds]


class BookmarkForm(FlaskForm):
    href = TextField(

M newspipe/web/views/category.py => newspipe/web/views/category.py +34 -17
@@ 33,39 33,30 @@ def list_():
def form(category_id=None):
    action = gettext("Add a category")
    head_titles = [action]
    form = CategoryForm()
    form.set_feed_choices(FeedController(current_user.id).read())
    if category_id is None:
        return render_template(
            "edit_category.html",
            action=action,
            head_titles=head_titles,
            form=CategoryForm(),
            form=form,
        )
    category = CategoryController(current_user.id).get(id=category_id)
    action = gettext("Edit category")
    head_titles = [action]
    if category.name:
        head_titles.append(category.name)
    form = CategoryForm(obj=category)
    form.set_feed_choices(FeedController(current_user.id).read())
    form.feeds.data = [feed.id for feed in category.feeds]
    return render_template(
        "edit_category.html",
        action=action,
        head_titles=head_titles,
        category=category,
        form=CategoryForm(obj=category),
    )


@category_bp.route("/delete/<int:category_id>", methods=["GET"])
@login_required
def delete(category_id=None):
    category = CategoryController(current_user.id).delete(category_id)
    flash(
        gettext(
            "Category %(category_name)s successfully deleted.",
            category_name=category.name,
        ),
        "success",
        form=form,
    )
    return redirect(redirect_url())


@category_bp.route("/create", methods=["POST"])


@@ 73,7 64,9 @@ def delete(category_id=None):
@login_required
def process_form(category_id=None):
    form = CategoryForm()
    form.set_feed_choices(FeedController(current_user.id).read())
    cat_contr = CategoryController(current_user.id)
    feed_contr = FeedController(current_user.id)

    if not form.validate():
        return render_template("edit_category.html", form=form)


@@ 81,11 74,18 @@ def process_form(category_id=None):
    if existing_cats and category_id is None:
        flash(gettext("Couldn't add category: already exists."), "warning")
        return redirect(url_for("category.form", category_id=existing_cats[0].id))

    # Edit an existing category
    category_attr = {"name": form.name.data}

    if category_id is not None:
        cat_contr.update({"id": category_id}, category_attr)
        # Update the relation with feeds
        for feed_id in form.feeds.data:
            feed_contr.update({"id": feed_id}, {"category_id": category_id})
        for feed in current_user.feeds:
            if feed.category_id == category_id and feed.id not in form.feeds.data:
                feed_contr.update({"id": feed.id}, {"category_id": None})

        flash(
            gettext(
                "Category %(cat_name)r successfully updated.",


@@ 97,6 97,9 @@ def process_form(category_id=None):

    # Create a new category
    new_category = cat_contr.create(**category_attr)
    # Update the relation with feeds
    for feed_id in form.feeds.data:
        feed_contr.update({"id": feed_id}, {"category_id": new_category.id})

    flash(
        gettext(


@@ 107,3 110,17 @@ def process_form(category_id=None):
    )

    return redirect(url_for("category.form", category_id=new_category.id))


@category_bp.route("/delete/<int:category_id>", methods=["GET"])
@login_required
def delete(category_id=None):
    category = CategoryController(current_user.id).delete(category_id)
    flash(
        gettext(
            "Category %(category_name)s successfully deleted.",
            category_name=category.name,
        ),
        "success",
    )
    return redirect(redirect_url())

M package-lock.json => package-lock.json +5 -0
@@ 9,6 9,11 @@
      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.0.tgz",
      "integrity": "sha512-Z93QoXvodoVslA+PWNdk23Hze4RBYIkpb5h8I2HY2Tu2h7A0LpAgLcyrhrSUyo2/Oxm2l1fRZPs1e5hnxnliXA=="
    },
    "bootstrap-select": {
      "version": "1.13.17",
      "resolved": "https://registry.npmjs.org/bootstrap-select/-/bootstrap-select-1.13.17.tgz",
      "integrity": "sha512-LbzSQumoZNYGuoYMpShKIHbJ2qUWFLI0qVHVeKqw5+nfhtMxz+Gre1+IuI3X74bTzQfalBqDKc8fS8tZMdciWg=="
    },
    "datatables": {
      "version": "1.10.18",
      "resolved": "https://registry.npmjs.org/datatables/-/datatables-1.10.18.tgz",

M package.json => package.json +1 -0
@@ 5,6 5,7 @@
  "private": true,
  "dependencies": {
    "bootstrap": "^4.5.0",
    "bootstrap-select": "^1.13.17",
    "datatables": "^1.10.18",
    "datatables.net-bs4": "^1.10.21",
    "fork-awesome": "^1.1.7",