Skip to content

Commit 3f83b63

Browse files
committed
redirect to renamed wiki pages
1 parent acf8bb4 commit 3f83b63

6 files changed

Lines changed: 73 additions & 16 deletions

File tree

sopy/ext/sqlalchemy.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import current_app, g
22
from flask_sqlalchemy import SQLAlchemy as BaseSQLAlchemy, _camelcase_re, _QueryProperty, BaseQuery
3-
from sqlalchemy import inspect
3+
from sqlalchemy import inspect, MetaData
44
from sqlalchemy.ext.declarative import DeclarativeMeta as BaseDeclarativeMeta, declared_attr, declarative_base
55
from sqlalchemy.orm import was_deleted
66

@@ -109,7 +109,14 @@ def init_app(self, app):
109109
app.shell_context_processor(lambda: {'db': self})
110110

111111
def make_declarative_base(self):
112-
base = declarative_base(cls=self.BaseModel, name='Model', metaclass=self.DeclarativeMeta)
112+
metadata = MetaData(naming_convention={
113+
'pk': 'pk_%(table_name)s',
114+
'fk': 'fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s',
115+
'uq': 'uq_%(table_name)s_%(column_0_name)s',
116+
'ix': 'ix_%(table_name)s_%(column_0_name)s',
117+
'ck': 'ck_%(table_name)s_%(constraint_name)s',
118+
})
119+
base = declarative_base(metadata=metadata, cls=self.BaseModel, name='Model', metaclass=self.DeclarativeMeta)
113120
base.query = _QueryProperty(self)
114121
base.db = self
115122
return base
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
"""wiki redirect
2+
3+
Revision ID: a7cdfb24f1
4+
Revises: 54a3343a03b
5+
Create Date: 2015-05-16 09:43:45.455535
6+
"""
7+
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
revision = 'a7cdfb24f1'
13+
down_revision = '54a3343a03b'
14+
branch_labels = ()
15+
depends_on = None
16+
17+
18+
def upgrade():
19+
op.add_column('wiki_page', sa.Column('redirect_id', sa.Integer(), nullable=True))
20+
op.create_unique_constraint(op.f('uq_wiki_page_title'), 'wiki_page', ['title'])
21+
op.create_foreign_key(op.f('fk_wiki_page_redirect_id_wiki_page'), 'wiki_page', 'wiki_page', ['redirect_id'], ['id'])
22+
23+
24+
def downgrade():
25+
op.drop_constraint(op.f('fk_wiki_page_redirect_id_wiki_page'), 'wiki_page', type_='foreignkey')
26+
op.drop_constraint(op.f('uq_wiki_page_title'), 'wiki_page', type_='unique')
27+
op.drop_column('wiki_page', 'redirect_id')

sopy/templates/wiki/detail.html

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@ <h2 class="page-header">{% block title %}{{ page.title }}{% endblock %}</h2>
88
{% if page.community %}<div class="alert alert-info">This page is in community mode. Any user with 100 rep may edit it.</div>{% endif %}
99

1010
<ul class="list-inline pull-right">
11-
<li><small><i>Last edited {{ page.updated|datetimeformat }}</i></small></li>
11+
<li><small><i>Last edited on {{ page.updated|datetimeformat }} by {{ page.author }}</i></small></li>
1212
{% if has_group('editor') or page.draft or page.community %}<li><a href="{{ page.update_url }}" class="btn btn-success btn-sm"><i class="fa fa-edit"></i> Edit</a></li>{% endif %}
1313
{% if has_group('editor') %}<li><a href="{{ page.delete_url }}" class="btn btn-danger btn-sm"><i class="fa fa-minus"></i> Remove</a></li>{% endif %}
1414
</ul>
1515

1616
<div class="clearfix"></div>
1717

18+
{% if redirect_from %}
19+
<p>Redirected from <a href="{{ url_for('wiki.detail', title=redirect_from, no_redirect=1) }}">{{ redirect_from }}</a></p>
20+
{% endif %}
21+
22+
{% if page.redirect %}
23+
<p>Redirects to <a href="{{ page.redirect.detail_url }}">{{ page.redirect.title }}</a></p>
24+
{% endif %}
25+
1826
{% if page.body %}{{ page.body|markdown }}{% endif %}
1927
</div></div>
2028
{% endblock %}

sopy/wiki/forms.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from flask_wtf import Form
22
import re
3+
from markupsafe import Markup
34
from wtforms.fields import TextAreaField, BooleanField
45
from wtforms.validators import DataRequired, ValidationError
5-
from sopy import db
66
from sopy.ext.forms import StripStringField
77
from sopy.wiki.models import WikiPage
88

@@ -25,14 +25,12 @@ def validate_title(self, field):
2525
if match:
2626
raise ValidationError('Invalid characters: "{}"'.format(match.group(1)))
2727

28-
unique_title_q = db.session.query(db.func.count(WikiPage.id)).filter(WikiPage.title == title)
28+
existing = WikiPage.query.filter_by(title=title).first()
2929

30-
if self.obj is not None:
31-
# When editing an existing page, only check *other* pages for duplicate titles.
32-
unique_title_q = unique_title_q.filter(WikiPage.id != self.obj.id)
33-
34-
if unique_title_q.scalar():
35-
raise ValidationError('A page with this title already exists.')
30+
# check existing title when creating new page
31+
# skip if existing page is current page when editing
32+
if existing is not None and (self.obj is None or self.obj.id != existing.id):
33+
raise ValidationError(Markup('<a href="{}">A page with this title</a> already exists. Rename or delete it before continuing.'.format(existing.detail_url)))
3634

3735

3836
class WikiPageEditorForm(WikiPageForm):

sopy/wiki/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ class WikiPage(IDModel):
1212
draft = db.Column(db.Boolean, nullable=False, default=False)
1313
community = db.Column(db.Boolean, nullable=False, default=False)
1414
author_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
15+
redirect_id = db.Column(db.Integer, db.ForeignKey('wiki_page.id'))
1516

1617
author = db.relationship(User)
18+
redirect = db.relationship(lambda: WikiPage, remote_side=lambda: (WikiPage.id,), backref='redirects')
1719

1820
def __str__(self):
1921
return self.title

sopy/wiki/views.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import redirect, render_template
1+
from flask import redirect, render_template, request, session, url_for
22
from flask_wtf import Form
33
from sopy import db
44
from sopy.auth.login import group_required, current_user, login_required, require_group, has_group
@@ -10,7 +10,7 @@
1010

1111
@bp.route('/')
1212
def index():
13-
pages = WikiPage.query.order_by(WikiPage.title)
13+
pages = WikiPage.query.filter(WikiPage.redirect_id.is_(None)).order_by(WikiPage.title)
1414

1515
if not has_group('editor'):
1616
pages = pages.filter(db.not_(WikiPage.draft))
@@ -20,11 +20,21 @@ def index():
2020
return render_template('wiki/index.html', pages=pages)
2121

2222

23-
@bp.route('/<wiki_title:title>/')
23+
@bp.route('/<wiki_title:title>')
2424
def detail(title):
25-
page = WikiPage.query.filter(WikiPage.title == title).first_or_404()
25+
page = WikiPage.query.filter(WikiPage.title == title).options(db.joinedload(WikiPage.redirect)).first_or_404()
26+
27+
if page.redirect and 'no_redirect' not in request.args:
28+
session['redirect_from'] = title
29+
return redirect(page.redirect.detail_url)
2630

27-
return render_template('wiki/detail.html', page=page)
31+
if 'redirect_from' in session:
32+
redirect_from = session['redirect_from']
33+
del session['redirect_from']
34+
else:
35+
redirect_from = None
36+
37+
return render_template('wiki/detail.html', page=page, redirect_from=redirect_from)
2838

2939

3040
@bp.route('/create', endpoint='create', methods=['GET', 'POST'])
@@ -42,6 +52,11 @@ def update(title=None):
4252
if page is None:
4353
page = WikiPage()
4454
db.session.add(page)
55+
else:
56+
page.redirect = None
57+
58+
if page.title != form.title.data:
59+
db.session.add(WikiPage(title=page.title, body='', redirect=page, author=current_user))
4560

4661
page.author = current_user
4762
form.populate_obj(page)

0 commit comments

Comments
 (0)