def test_topic_replying_and_answering(self): """Replies to topics and answering""" user = self.make_test_user() topic = models.Topic('en', 'This is a test topic', 'Foobar', user) session.commit() topic_id = topic.id self.assertEqual(topic.last_change, topic.question.created) self.assertEqual(topic.is_answered, False) self.assertEqual(len(topic.posts), 1) models.Post(topic, user, 'This is more text') topic.accept_answer(models.Post(topic, user, 'And this is another answer')) self.assertEqual(topic.answer.is_answer, True) self.assertEqual(topic.answer.is_question, False) session.commit() def internal_test(): self.assertEqual(len(topic.posts), 3) self.assertEqual(topic.answer_date, topic.answer.created) self.assertEqual(topic.answer_author, topic.answer.author) self.assertEqual(topic.last_change, topic.answer.created) self.assertEqual(topic.is_answered, True) # do the test now internal_test() topic = None session.remove() # and a second time with the queried data from the database topic = models.Topic.query.get(topic_id) internal_test() self.assertEqual(topic.reply_count, 2)
def restore_post(request, id): post, revision = _load_post_and_revision(request, id) # sanity checks if revision is None: if not request.user.is_moderator: raise Forbidden() elif not post.is_deleted: return redirect(url_for(post)) elif not request.user.can_edit(post): raise Forbidden() form = EmptyForm() if request.method == 'POST' and form.validate(): if 'yes' in request.form: if revision is None: request.flash(_(u'The post was restored')) post.restore() else: request.flash(_(u'The revision was restored')) revision.restore() session.commit() return form.redirect(post) return render_template('kb/restore_post.html', form=form.as_widget(), post=post, revision=revision)
def topic(request, id, slug=None): """Shows a topic.""" topic = Topic.query.eagerposts().get(id) # if the topic id does not exist or the topic is from a different # language, we abort with 404 early if topic is None or topic.locale != request.view_lang: raise NotFound() # make sure the slug is okay, otherwise redirect to the real one # to ensure URLs are unique. if slug is None or topic.slug != slug: return redirect(url_for(topic)) # deleted posts cannot be seen by people without privilegs if topic.is_deleted and not (request.user and request.user.is_moderator): raise Forbidden() # a form for the replies. form = ReplyForm(topic) if request.method == 'POST' and form.validate(): reply = form.create_reply() session.commit() request.flash(_(u'Your reply was posted.')) return redirect(url_for(reply)) # pull in the votes in a single query for all the posts related to the # topic so that we only have to fire the database once. if request.is_logged_in: request.user.pull_votes(topic.posts) return render_template('kb/topic.html', topic=topic, reply_form=form.as_widget())
def test_pages(self): """Make sure that all pages are valid HTML5""" settings.LANGUAGE_SECTIONS = ['en'] user = models.User('user1', '*****@*****.**', 'default') user.active = True topic = models.Topic('en', 'This is a test topic', 'Foobar', user) post1 = models.Post(topic, user, 'meh1') post2 = models.Post(topic, user, 'meh2') topic.accept_answer(post1) session.commit() visited_links = set() def visit(url): url = urljoin(BASE_URL, url).split('#', 1)[0] if not url.startswith(BASE_URL) or url in visited_links: return visited_links.add(url) path = url.split('/', 3)[-1] response = self.client.get(path, follow_redirects=True) content_type = response.headers['Content-Type'] if content_type.split(';')[0].strip() == 'text/html': self.doExternalValidation(url, response.data, content_type) for link in html_xpath(response.html, '//html:a[@href]'): visit(link.attrib['href']) self.login('user1', 'default') visit('/')
def test_reset_password(self): """Reset password.""" settings.RECAPTCHA_ENABLE = False user = models.User('A_USER', '*****@*****.**', 'default') session.commit() self.submit_form('/_reset_password', { 'username': '******', 'email': '' }) mails = self.get_mails() self.assert_(mails) for link in _link_re.findall(mails[0].get_payload()): if 'reset_password' in link: response = self.client.get('/' + link.split('/', 3)[-1]) break else: self.assert_(False, 'Did not find password reset link') match = re.compile(r'password was reset to <code>(.*?)</code>') \ .search(response.data) self.assert_(match) self.login('A_USER', match.group(1)) response = self.client.get('/en/') self.assert_('A_USER' in response.data)
def submit_comment(request, post): """Used by the form on `get_comments` to submit the form data to the database. Returns partial data for the remote side. """ if not request.is_xhr: raise BadRequest() post = Post.query.get(post) if post is None: raise NotFound() # not even moderators can submit comments for deleted posts. if post.is_deleted: message = _(u'You cannot submit comments for deleted posts') return json_response(success=False, form_errors=[message]) form = _get_comment_form(post) if form.validate(): comment = form.create_comment() session.commit() comment_box = get_macro('kb/_boxes.html', 'render_comment') comment_link = get_macro('kb/_boxes.html', 'render_comment_link') return json_response(html=comment_box(comment), link=comment_link(post), success=True) return json_response(success=False, form_errors=form.as_widget().all_errors)
def test_basic_reputation_changes(self): """Basic reputation changes""" user1 = self.make_test_user('user1') user2 = self.make_test_user('user2') user3 = self.make_test_user('user3') user4 = self.make_test_user('user4') topic = models.Topic('en', 'This is a test topic', 'Foobar', user1) session.commit() user2.upvote(topic) user3.upvote(topic) session.commit() self.assertEqual(user1.reputation, 2) user4.downvote(topic) session.commit() self.assertEqual(user1.reputation, 0) self.assertEqual(user4.reputation, -1) topic.accept_answer(models.Post(topic, user4, 'blafasel')) session.commit() self.assertEqual(user4.reputation, 49) topic.accept_answer(models.Post(topic, user1, 'another answer')) user1.upvote(topic.answer) session.commit() self.assertEqual(user4.reputation, -1) self.assertEqual(user1.reputation, 60)
def edit_post(request, id): post, revision = _load_post_and_revision(request, id) if not request.user.can_edit(post): raise Forbidden() if post.is_question: form = QuestionForm(post.topic, revision=revision) else: form = ReplyForm(post=post, revision=revision) if request.method == 'POST' and form.validate(): form.save_changes() session.commit() request.flash(_('The post was edited.')) return redirect(url_for(post)) def _format_entry(author, date, extra=u''): return _(u'%s (%s)') % (author, format_datetime(date)) + extra post_revisions = [(revision is None, '', _format_entry( (post.editor or post.author).display_name, post.updated, u' [%s]' % _(u'Current revision')))] + \ [(revision == entry, entry.id, _format_entry( entry.editor.display_name, entry.date)) for entry in post.revisions.order_by(PostRevision.date.desc())] return render_template('kb/edit_post.html', form=form.as_widget(), post=post, all_revisions=post_revisions)
def vote(request, post): """Votes on a post.""" # TODO: this is currently also fired as GET if JavaScript is # not available. Not very nice. post = Post.query.get(post) if post is None: raise NotFound() # you cannot cast votes on deleted shit if post.is_deleted: message = _(u"You cannot vote on deleted posts.") if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) # otherwise val = request.args.get("val", 0, type=int) if val == 0: request.user.unvote(post) elif val == 1: # users cannot upvote on their own stuff if post.author == request.user: message = _(u"You cannot upvote your own post.") if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) # also some reputation is needed if not request.user.is_admin and request.user.reputation < settings.REPUTATION_MAP["UPVOTE"]: message = _(u"In order to upvote you " u"need at least %d reputation") % settings.REPUTATION_MAP["UPVOTE"] if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) request.user.upvote(post) elif val == -1: # users need some reputation to downvote. Keep in mind that # you *can* downvote yourself. if not request.user.is_admin and request.user.reputation < settings.REPUTATION_MAP["DOWNVOTE"]: message = ( _(u"In order to downvote you " u"need at least %d reputation") % settings.REPUTATION_MAP["DOWNVOTE"] ) if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) request.user.downvote(post) else: raise BadRequest() session.commit() # standard requests are answered with a redirect back if not request.is_xhr: return redirect(url_for(post)) # others get a re-rendered vote box box = get_macro("kb/_boxes.html", "render_vote_box") return json_response(html=box(post, request.user))
def test_logout(self): """Logging a user out""" models.User('THE_USER', '*****@*****.**', 'default') session.commit() self.login('THE_USER', 'default') self.logout() response = self.client.get('/en/') self.assert_('THE_USER' not in response.data)
def run(self): from solace.database import session users = self.create_users() tags = self.create_tags() topics = self.create_topics(tags, users) posts = self.answer_and_vote(topics, users) self.create_comments(posts, users) self.rebase_dates(topics) session.commit()
def new(request): """The new-question form.""" form = QuestionForm() if request.method == 'POST' and form.validate(): topic = form.create_topic() session.commit() request.flash(_(u'Your question was posted.')) return redirect(url_for(topic)) return render_template('kb/new.html', form=form.as_widget())
def accept(request, post): """Accept a post as an answer.""" # TODO: this is currently also fired as GET if JavaScript is # not available. Not very nice. post = Post.query.get(post) if post is None: raise NotFound() # just for sanity. It makes no sense to accept the question # as answer. The UI does not allow that, so the user must have # tampered with the data here. if post.is_question: raise BadRequest() # likewise you cannot accept a deleted post as answer if post.is_deleted: message = _(u'You cannot accept deleted posts as answers') if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) topic = post.topic # if the post is already the accepted answer, we unaccept the # post as answer. if post.is_answer: if not request.user.can_unaccept_as_answer(post): message = _(u'You cannot unaccept this reply as an answer.') if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) topic.accept_answer(None, request.user) session.commit() if request.is_xhr: return json_response(accepted=False) return redirect(url_for(post)) # otherwise we try to accept the post as answer. if not request.user.can_accept_as_answer(post): message = _(u'You cannot accept this reply as answer.') if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) topic.accept_answer(post, request.user) session.commit() if request.is_xhr: return json_response(accepted=True) return redirect(url_for(post))
def test_new_topic(self): """Creating new topics and replying""" # create the user models.User('user1', '*****@*****.**', 'default') session.commit() # login and submit self.login('user1', 'default') response = self.submit_form( '/en/new', { 'title': 'Hello World', 'text': 'This is just a small test\n\n**test**', 'tags': 'foo, bar, baz' }) # we will need the topic URL later for commit submission, # capture it! topic_url = '/' + response.headers['Location'].split('/', 3)[-1] response = self.client.get(topic_url) q = lambda x: html_xpath(response.html, x) # we have a headline self.assertEqual(q('//html:h1')[0].text, 'Hello World') # and all the tags tags = sorted(x.text for x in q('//html:p[@class="tags"]/html:a')) self.assertEqual(tags, ['bar', 'baz', 'foo']) # and the text is present and parsed pars = q('//html:div[@class="text"]/html:p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, 'This is just a small test') self.assertEqual(pars[1][0].tag, '{http://www.w3.org/1999/xhtml}strong') self.assertEqual(pars[1][0].text, 'test') # now try to submit a reply response = self.submit_form( topic_url, {'text': 'This is a reply\n\nwith //text//'}, follow_redirects=True) q = lambda x: html_xpath(response.html, x) # do we have the text? pars = q( '//html:div[@class="replies"]//html:div[@class="text"]/html:p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, 'This is a reply') self.assertEqual(pars[1].text, 'with ') self.assertEqual(pars[1][0].tag, '{http://www.w3.org/1999/xhtml}em') self.assertEqual(pars[1][0].text, 'text')
def activate_user(request, email, key): """Activates the user.""" # the email is not unique on the database, we try all matching users. # Most likely it's only one, otherwise we activate the first matching. user = User.query.filter_by(email=email, activation_key=key).first() if user is not None: user.is_active = True session.commit() request.flash(_(u'Your account was activated. You can ' u'log in now.')) return redirect(url_for('core.login')) request.flash(_(u'User activation failed. The user is either already ' u'activated or you followed a wrong link.'), error=True) return redirect(url_for('kb.overview'))
def test_post_revisions(self): """Internal revisions for posts""" creator = self.make_test_user('creator') editor = self.make_test_user('editor') topic = models.Topic('en', 'Topic with revisions', 'Original text.', creator) session.commit() self.assertEqual(topic.question.revisions.count(), 0) topic.question.edit('New text with default params.') session.commit() self.assertEqual(topic.question.text, 'New text with default params.') rev = topic.question.revisions.first() self.assertEqual(rev.editor, creator) self.assertEqual(rev.date, topic.date) self.assertEqual(rev.text, 'Original text.') d = datetime.datetime.utcnow() topic.question.edit('From the editor', editor, d) session.commit() self.assertEqual(topic.question.author, creator) self.assertEqual(topic.question.editor, editor) self.assertEqual(topic.question.updated, d) self.assertEqual(topic.last_change, d) rev.restore() session.commit() self.assertEqual(topic.question.editor, rev.editor) self.assertEqual(topic.question.updated, rev.date) self.assertEqual(topic.question.text, rev.text) self.assertEqual(topic.question.edits, 3)
def create_test_data(self, topics=20): # don't put ourselves into the query. The user that logs in must not be # part of the generated content, otherwise we could end up with less # queries which would result in random failures me = models.User('me', '*****@*****.**', 'default') users = [] for x in xrange(5): username = '******' % x users.append(models.User(username, username + '@example.com')) for x in xrange(topics): t = models.Topic('en', 'Topic %d' % x, 'test contents', choice(users)) for x in xrange(4): models.Post(t, choice(users), 'test contents') session.commit()
def test_voting(self): """Voting from the web interface""" # create a bunch of users and let one of them create a topic users = [ models.User('user_%d' % x, '*****@*****.**' % x, 'default') for x in xrange(5) ] for user in users: user.reputation = 50 topic = models.Topic('en', 'Hello World', 'foo', users[0]) session.commit() tquid = topic.question.id def get_vote_count(response): el = html_xpath(response.html, '//html:div[@class="votebox"]/html:h4') return int(el[0].text) vote_url = '/_vote/%s?val=%%d&_xt=%s' % (tquid, self.get_exchange_token()) # the author should not be able to upvote self.login('user_0', 'default') response = self.client.get(vote_url % 1, follow_redirects=True) self.assert_('cannot upvote your own post' in response.data) # by default the user should not be able to downvote, because # he does not have enough reputation response = self.client.get(vote_url % -1, follow_redirects=True) self.assert_( 'to downvote you need at least 100 reputation' in response.data) # so give him and all other users reputation for user in models.User.query.all(): user.reputation = 10000 session.commit() # and let him downvote response = self.client.get(vote_url % -1, follow_redirects=True) self.assertEqual(get_vote_count(response), -1) # and now let *all* users vote up, including the author, but his # request will fail. for num in xrange(5): self.logout() self.login('user_%d' % num, 'default') response = self.client.get(vote_url % 1, follow_redirects=True) # we should be at 4, author -1 the other four +1 self.assertEqual(get_vote_count(response), 3)
def reset_password(request, email=None, key=None): """Resets the password if possible.""" auth = get_auth_system() if not auth.can_reset_password: raise NotFound() form = ResetPasswordForm() new_password = None # if the user is logged in, he goes straight back to the overview # page. Why would a user that is logged in (and does not anywhere # see a link to that page) reset the password? Of course that does # not give us anything security wise because he just has to logout. if request.is_logged_in: return redirect(url_for('kb.overview')) # we came back from the link in the mail, try to reset the password if email is not None: for user in User.query.filter_by(email=email).all(): if user.password_reset_key == key: break else: request.flash(_(u'The password-reset key expired or the link ' u'was invalid.'), error=True) return redirect(url_for('core.reset_password')) new_password = user.set_random_password() session.commit() # otherwise validate the form elif request.method == 'POST' and form.validate(request.form): user = form.user reset_url = url_for('core.reset_password', email=user.email, key=user.password_reset_key, _external=True) send_email( _(u'Reset Password'), render_template('mails/reset_password.txt', user=user, reset_url=reset_url), user.email) request.flash( _(u'A mail with a link to reset the password ' u'was sent to “%s”') % user.email) return redirect(url_for('kb.overview')) return render_template('core/reset_password.html', form=form.as_widget(), new_password=new_password)
def activate_user(request, email, key): """Activates the user.""" # the email is not unique on the database, we try all matching users. # Most likely it's only one, otherwise we activate the first matching. user = User.query.filter_by(email=email, activation_key=key).first() if user is not None: user.is_active = True session.commit() request.flash( _(u'Your account was activated. You can ' u'log in now.')) return redirect(url_for('core.login')) request.flash(_(u'User activation failed. The user is either already ' u'activated or you followed a wrong link.'), error=True) return redirect(url_for('kb.overview'))
def test_topic_tagging(self): """Topic tagging""" user = self.make_test_user() en_topic = models.Topic('en', 'This is a test topic', 'text', user) en_topic.bind_tags(['foo', 'bar', 'baz']) de_topic = models.Topic('de', 'This is a test topic', 'text', user) de_topic.bind_tags(['foo']) session.commit() foo = models.Tag.query.filter_by(locale=Locale('de'), name='foo').first() self.assertEqual(foo.name, 'foo') self.assertEqual(foo.tagged, 1) self.assertEqual(foo.topics.first(), de_topic) models.Topic('de', 'Another topic', 'text', user) \ .bind_tags(['foo', 'bar']) session.commit() self.assertEqual(foo.tagged, 2)
def test_new_topic(self): """Creating new topics and replying""" # create the user models.User('user1', '*****@*****.**', 'default') session.commit() # login and submit self.login('user1', 'default') response = self.submit_form('/en/new', { 'title': 'Hello World', 'text': 'This is just a small test\n\n**test**', 'tags': 'foo, bar, baz' }) # we will need the topic URL later for commit submission, # capture it! topic_url = '/' + response.headers['Location'].split('/', 3)[-1] response = self.client.get(topic_url) q = lambda x: html_xpath(response.html, x) # we have a headline self.assertEqual(q('//html:h1')[0].text, 'Hello World') # and all the tags tags = sorted(x.text for x in q('//html:p[@class="tags"]/html:a')) self.assertEqual(tags, ['bar', 'baz', 'foo']) # and the text is present and parsed pars = q('//html:div[@class="text"]/html:p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, 'This is just a small test') self.assertEqual(pars[1][0].tag, '{http://www.w3.org/1999/xhtml}strong') self.assertEqual(pars[1][0].text, 'test') # now try to submit a reply response = self.submit_form(topic_url, { 'text': 'This is a reply\n\nwith //text//' }, follow_redirects=True) q = lambda x: html_xpath(response.html, x) # do we have the text? pars = q('//html:div[@class="replies"]//html:div[@class="text"]/html:p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, 'This is a reply') self.assertEqual(pars[1].text, 'with ') self.assertEqual(pars[1][0].tag, '{http://www.w3.org/1999/xhtml}em') self.assertEqual(pars[1][0].text, 'text')
def test_voting(self): """Voting from the web interface""" # create a bunch of users and let one of them create a topic users = [models.User('user_%d' % x, '*****@*****.**' % x, 'default') for x in xrange(5)] for user in users: user.reputation = 50 topic = models.Topic('en', 'Hello World', 'foo', users[0]) session.commit() tquid = topic.question.id def get_vote_count(response): el = html_xpath(response.html, '//html:div[@class="votebox"]/html:h4') return int(el[0].text) vote_url = '/_vote/%s?val=%%d&_xt=%s' % (tquid, self.get_exchange_token()) # the author should not be able to upvote self.login('user_0', 'default') response = self.client.get(vote_url % 1, follow_redirects=True) self.assert_('cannot upvote your own post' in response.data) # by default the user should not be able to downvote, because # he does not have enough reputation response = self.client.get(vote_url % -1, follow_redirects=True) self.assert_('to downvote you need at least 100 reputation' in response.data) # so give him and all other users reputation for user in models.User.query.all(): user.reputation = 10000 session.commit() # and let him downvote response = self.client.get(vote_url % -1, follow_redirects=True) self.assertEqual(get_vote_count(response), -1) # and now let *all* users vote up, including the author, but his # request will fail. for num in xrange(5): self.logout() self.login('user_%d' % num, 'default') response = self.client.get(vote_url % 1, follow_redirects=True) # we should be at 4, author -1 the other four +1 self.assertEqual(get_vote_count(response), 3)
def test_new_topic(self): """Creating new topics and replying""" # create the user models.User("user1", "*****@*****.**", "default") session.commit() # login and submit self.login("user1", "default") response = self.submit_form( "/en/new", {"title": "Hello World", "text": "This is just a small test\n\n**test**", "tags": "foo, bar, baz"}, ) # we will need the topic URL later for commit submission, # capture it! topic_url = "/" + response.headers["Location"].split("/", 3)[-1] response = self.client.get(topic_url) # was: q = response.html.xpath q = html.fromstring(response.data).xpath # we have a headline self.assertEqual(q("//h1")[0].text, "Hello World") # and all the tags tags = sorted(x.text for x in q('//p[@class="tags"]/a')) self.assertEqual(tags, ["bar", "baz", "foo"]) # and the text is present and parsed pars = q('//div[@class="text"]/p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, "This is just a small test") self.assertEqual(pars[1][0].tag, "strong") self.assertEqual(pars[1][0].text, "test") # now try to submit a reply response = self.submit_form(topic_url, {"text": "This is a reply\n\nwith //text//"}, follow_redirects=True) q = html.fromstring(response.data).xpath # do we have the text? pars = q('//div[@class="replies"]//div[@class="text"]/p') self.assertEqual(len(pars), 2) self.assertEqual(pars[0].text, "This is a reply") self.assertEqual(pars[1].text, "with ") self.assertEqual(pars[1][0].tag, "em") self.assertEqual(pars[1][0].text, "text")
def delete_post(request, id): post = Post.query.get(id) # sanity checks if not request.user.is_moderator: raise Forbidden() elif post.is_deleted: return redirect(url_for(post)) form = EmptyForm() if request.method == "POST" and form.validate(): if "yes" in request.form: post.delete() session.commit() request.flash(_("The post was deleted")) return redirect(url_for(post)) return render_template("kb/delete_post.html", post=post, form=form.as_widget())
def reset_password(request, email=None, key=None): """Resets the password if possible.""" auth = get_auth_system() if not auth.can_reset_password: raise NotFound() form = ResetPasswordForm() new_password = None # if the user is logged in, he goes straight back to the overview # page. Why would a user that is logged in (and does not anywhere # see a link to that page) reset the password? Of course that does # not give us anything security wise because he just has to logout. if request.is_logged_in: return redirect(url_for('kb.overview')) # we came back from the link in the mail, try to reset the password if email is not None: for user in User.query.filter_by(email=email).all(): if user.password_reset_key == key: break else: request.flash(_(u'The password-reset key expired or the link ' u'was invalid.'), error=True) return redirect(url_for('core.reset_password')) new_password = user.set_random_password() session.commit() # otherwise validate the form elif request.method == 'POST' and form.validate(request.form): user = form.user reset_url = url_for('core.reset_password', email=user.email, key=user.password_reset_key, _external=True) send_email(_(u'Reset Password'), render_template('mails/reset_password.txt', user=user, reset_url=reset_url), user.email) request.flash(_(u'A mail with a link to reset the password ' u'was sent to “%s”') % user.email) return redirect(url_for('kb.overview')) return render_template('core/reset_password.html', form=form.as_widget(), new_password=new_password)
def delete_post(request, id): post = Post.query.get(id) # sanity checks if not request.user.is_moderator: raise Forbidden() elif post.is_deleted: return redirect(url_for(post)) form = EmptyForm() if request.method == 'POST' and form.validate(): if 'yes' in request.form: post.delete() session.commit() request.flash(_('The post was deleted')) return redirect(url_for(post)) return render_template('kb/delete_post.html', post=post, form=form.as_widget())
def first_login(self, request): """Until the openid information is removed from the session, this view will be use to create the user account based on the openid url. """ identity_url = request.session.get('openid') if identity_url is None: return redirect(url_for('core.login')) if request.is_logged_in: del request.session['openid'] return redirect(request.next_url or url_for('kb.overview')) form = OpenIDRegistrationForm() if request.method == 'POST' and form.validate(): user = User(form['username'], form['email']) user.openid_logins.add(identity_url) self.after_register(request, user) session.commit() del request.session['openid'] self.set_user_checked(request, user) return self.redirect_back(request) return render_template('core/register_openid.html', form=form.as_widget(), identity_url=identity_url)
def test_only_valid_links(self): """Make sure that all links are valid""" settings.LANGUAGE_SECTIONS = ['en'] user = models.User('user1', '*****@*****.**', 'default') user.is_admin = True banned_user = models.User('user2', '*****@*****.**', 'default') banned_user.is_banned = True topic = models.Topic('en', 'This is a test topic', 'Foobar', user) post1 = models.Post(topic, user, 'meh1') post2 = models.Post(topic, user, 'meh2') topic.accept_answer(post1) session.commit() visited_links = set() def visit(url): url = urljoin(BASE_URL, url).split('#', 1)[0] if not url.startswith(BASE_URL) or url in visited_links: return visited_links.add(url) path = '/' + url.split('/', 3)[-1] if path.startswith('/logout?'): return response = self.client.get(path, follow_redirects=True) self.assertEqual(response.status_code, 200) for link in html_xpath(response.html, '//html:a[@href]'): visit(link.attrib['href']) # logged out visit('/') self.assert_(len(visited_links) > MIN_VISITED) # logged in visited_links.clear() self.login('user1', 'default') visit('/') self.assert_(len(visited_links) > MIN_VISITED)
def test_only_valid_links(self): """Make sure that all links are valid""" settings.LANGUAGE_SECTIONS = ['en'] user = models.User('user1', '*****@*****.**', 'default') user.is_admin = True banned_user = models.User('user2', '*****@*****.**', 'default') banned_user.is_banned = True topic = models.Topic('en', 'This is a test topic', 'Foobar', user) post1 = models.Post(topic, user, 'meh1') post2 = models.Post(topic, user, 'meh2') topic.accept_answer(post1) session.commit() visited_links = set() def visit(url): url = urljoin(BASE_URL, url).split('#', 1)[0] if not url.startswith(BASE_URL) or url in visited_links: return visited_links.add(url) path = '/' + url.split('/', 3)[-1] if path.startswith('/logout?'): return response = self.client.get(path, follow_redirects=True) self.assertEqual(response.status_code, 200) for link in response.html.xpath('//a[@href]'): visit(link.attrib['href']) # logged out visit('/') self.assert_(len(visited_links) > MIN_VISITED) # logged in visited_links.clear() self.login('user1', 'default') visit('/') self.assert_(len(visited_links) > MIN_VISITED)
def test_post_commenting(self): """Post commenting""" user = self.make_test_user() topic = models.Topic('en', 'This is a test topic', 'text', user) session.commit() self.assertEqual(topic.question.comment_count, 0) a = models.Comment(topic.question, user, 'Blafasel') session.commit() self.assertEqual(topic.question.comment_count, 1) b = models.Comment(topic.question, user, 'woooza') session.commit() self.assertEqual(topic.question.comment_count, 2) self.assertEqual(topic.question.comments, [a, b])
def test_topic_voting(self): """Voting on topics""" user1 = self.make_test_user('user1') user2 = self.make_test_user('user2') topic = models.Topic('en', 'This is a test topic', 'Foobar', user1) session.commit() user1.upvote(topic) user2.upvote(topic) user2.upvote(topic) session.commit() self.assertEqual(topic.votes, 2) user2.downvote(topic.question) self.assertEqual(topic.votes, 0) user1.unvote(topic.question) self.assertEqual(topic.votes, -1) session.commit()
def make_test_user(self, username='******'): user = models.User(username, "*****@*****.**") session.commit() return user
def vote(request, post): """Votes on a post.""" # TODO: this is currently also fired as GET if JavaScript is # not available. Not very nice. post = Post.query.get(post) if post is None: raise NotFound() # you cannot cast votes on deleted shit if post.is_deleted: message = _(u'You cannot vote on deleted posts.') if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) # otherwise val = request.args.get('val', 0, type=int) if val == 0: request.user.unvote(post) elif val == 1: # users cannot upvote on their own stuff if post.author == request.user: message = _(u'You cannot upvote your own post.') if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) # also some reputation is needed if not request.user.is_admin and \ request.user.reputation < settings.REPUTATION_MAP['UPVOTE']: message = _(u'In order to upvote you ' u'need at least %d reputation') % \ settings.REPUTATION_MAP['UPVOTE'] if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) request.user.upvote(post) elif val == -1: # users need some reputation to downvote. Keep in mind that # you *can* downvote yourself. if not request.user.is_admin and \ request.user.reputation < settings.REPUTATION_MAP['DOWNVOTE']: message = _(u'In order to downvote you ' u'need at least %d reputation') % \ settings.REPUTATION_MAP['DOWNVOTE'] if request.is_xhr: return json_response(message=message, error=True) request.flash(message, error=True) return redirect(url_for(post)) request.user.downvote(post) else: raise BadRequest() session.commit() # standard requests are answered with a redirect back if not request.is_xhr: return redirect(url_for(post)) # others get a re-rendered vote box box = get_macro('kb/_boxes.html', 'render_vote_box') return json_response(html=box(post, request.user))