def comment_on_article(): # Obtain the username of the currently logged in user. username = session['username'] # Create form. The form maintains state, e.g. when this method is called with a HTTP GET request and populates # the form with an article id, when subsequently called with a HTTP POST request, the article id remains in the # form. form = CommentForm() if form.validate_on_submit(): # Successful POST, i.e. the comment text has passed data validation. # Extract the article id, representing the commented article, from the form. article_id = int(form.article_id.data) # Use the service layer to store the new comment. services.add_comment(article_id, form.comment.data, username, repo.repo_instance) # Retrieve the article in dict form. article = services.get_article(article_id, repo.repo_instance) # Cause the web browser to display the page of all articles that have the same date as the commented article, # and display all comments, including the new comment. return redirect(url_for('news_bp.articles_by_date', date=article['date'], view_comments_for=article_id)) if request.method == 'GET': # Request is a HTTP GET to display the form. # Extract the article id, representing the article to comment, from a query parameter of the GET request. article_id = int(request.args.get('article')) # Store the article id in the form. form.article_id.data = article_id else: # Request is a HTTP POST where form validation has failed. # Extract the article id of the article being commented from the form. article_id = int(form.article_id.data) # For a GET or an unsuccessful POST, retrieve the article to comment in dict form, and return a Web page that allows # the user to enter a comment. The generated Web page includes a form object. article = services.get_article(article_id, repo.repo_instance) return render_template( 'articles/comment_on_article.html', title='Edit article', article=article, form=form, handler_url=url_for('news_bp.comment_on_article'), selected_articles=utilities.get_selected_articles(), tag_urls=utilities.get_tags_and_urls() )
def login(): form = LoginForm() username_not_recognised = None password_does_not_match_username = None if form.validate_on_submit(): # Successful POST, i.e. the username and password have passed validation checking. # Use the service layer to lookup the user. try: user = services.get_user(form.username.data, repo.repo_instance) # Authenticate user. services.authenticate_user(user['username'], form.password.data, repo.repo_instance) # Initialise session and redirect the user to the home page. session.clear() session['username'] = user['username'] return redirect(url_for('news_bp.articles_by_date')) except services.UnknownUserException: # Username not known to the system, set a suitable error message. username_not_recognised = 'Username not recognised - please supply another' except services.AuthenticationException: # Authentication failed, set a suitable error message. password_does_not_match_username = '******' # For a GET or a failed POST, return the Login Web page. return render_template( 'authentication/credentials.html', title='Login', username_error_message=username_not_recognised, password_error_message=password_does_not_match_username, form=form, selected_articles=utilities.get_selected_articles(), tag_urls=utilities.get_tags_and_urls())
def register(): form = RegistrationForm() username_not_unique = None if form.validate_on_submit(): # Successful POST, i.e. the username and password have passed validation checking. # Use the service layer to attempt to add the new user. try: services.add_user(form.username.data, form.password.data, repo.repo_instance) # All is well, redirect the user to the login page. return redirect(url_for('authentication_bp.login')) except services.NameNotUniqueException: username_not_unique = 'Your username is already taken - please supply another' # For a GET or a failed POST request, return the Registration Web page. return render_template('authentication/credentials.html', title='Register', form=form, username_error_message=username_not_unique, handler_url=url_for('authentication_bp.register'), selected_articles=utilities.get_selected_articles(), tag_urls=utilities.get_tags_and_urls())
def articles_by_tag(): articles_per_page = 3 # Read query parameters. tag_name = request.args.get('tag') cursor = request.args.get('cursor') article_to_show_comments = request.args.get('view_comments_for') if article_to_show_comments is None: # No view-comments query parameter, so set to a non-existent article id. article_to_show_comments = -1 else: # Convert article_to_show_comments from string to int. article_to_show_comments = int(article_to_show_comments) if cursor is None: # No cursor query parameter, so initialise cursor to start at the beginning. cursor = 0 else: # Convert cursor from string to int. cursor = int(cursor) # Retrieve article ids for articles that are tagged with tag_name. article_ids = services.get_article_ids_for_tag(tag_name, repo.repo_instance) # Retrieve the batch of articles to display on the Web page. articles = services.get_articles_by_id(article_ids[cursor:cursor + articles_per_page], repo.repo_instance) first_article_url = None last_article_url = None next_article_url = None prev_article_url = None if cursor > 0: # There are preceding articles, so generate URLs for the 'previous' and 'first' navigation buttons. prev_article_url = url_for('news_bp.articles_by_tag', tag=tag_name, cursor=cursor - articles_per_page) first_article_url = url_for('news_bp.articles_by_tag', tag=tag_name) if cursor + articles_per_page < len(article_ids): # There are further articles, so generate URLs for the 'next' and 'last' navigation buttons. next_article_url = url_for('news_bp.articles_by_tag', tag=tag_name, cursor=cursor + articles_per_page) last_cursor = articles_per_page * int(len(article_ids) / articles_per_page) if len(article_ids) % articles_per_page == 0: last_cursor -= articles_per_page last_article_url = url_for('news_bp.articles_by_tag', tag=tag_name, cursor=last_cursor) # Construct urls for viewing article comments and adding comments. for article in articles: article['view_comment_url'] = url_for('news_bp.articles_by_tag', tag=tag_name, cursor=cursor, view_comments_for=article['id']) article['add_comment_url'] = url_for('news_bp.comment_on_article', article=article['id']) # Generate the webpage to display the articles. return render_template( 'articles/articles.html', title='Articles', articles_title='Articles tagged by ' + tag_name, articles=articles, selected_articles=utilities.get_selected_articles(len(articles) * 2), tag_urls=utilities.get_tags_and_urls(), first_article_url=first_article_url, last_article_url=last_article_url, prev_article_url=prev_article_url, next_article_url=next_article_url, show_comments_for_article=article_to_show_comments )
def articles_by_date(): # Read query parameters. target_date = request.args.get('date') article_to_show_comments = request.args.get('view_comments_for') # Fetch the first and last articles in the series. first_article = services.get_first_article(repo.repo_instance) last_article = services.get_last_article(repo.repo_instance) if target_date is None: # No date query parameter, so return articles from day 1 of the series. target_date = first_article['date'] else: # Convert target_date from string to date. target_date = date.fromisoformat(target_date) if article_to_show_comments is None: # No view-comments query parameter, so set to a non-existent article id. article_to_show_comments = -1 else: # Convert article_to_show_comments from string to int. article_to_show_comments = int(article_to_show_comments) # Fetch article(s) for the target date. This call also returns the previous and next dates for articles immediately # before and after the target date. articles, previous_date, next_date = services.get_articles_by_date(target_date, repo.repo_instance) first_article_url = None last_article_url = None next_article_url = None prev_article_url = None if len(articles) > 0: # There's at least one article for the target date. if previous_date is not None: # There are articles on a previous date, so generate URLs for the 'previous' and 'first' navigation buttons. prev_article_url = url_for('news_bp.articles_by_date', date=previous_date.isoformat()) first_article_url = url_for('news_bp.articles_by_date', date=first_article['date'].isoformat()) # There are articles on a subsequent date, so generate URLs for the 'next' and 'last' navigation buttons. if next_date is not None: next_article_url = url_for('news_bp.articles_by_date', date=next_date.isoformat()) last_article_url = url_for('news_bp.articles_by_date', date=last_article['date'].isoformat()) # Construct urls for viewing article comments and adding comments. for article in articles: article['view_comment_url'] = url_for('news_bp.articles_by_date', date=target_date, view_comments_for=article['id']) article['add_comment_url'] = url_for('news_bp.comment_on_article', article=article['id']) # Generate the webpage to display the articles. return render_template( 'articles/articles.html', title='Articles', articles_title=target_date.strftime('%A %B %e %Y'), articles=articles, selected_articles=utilities.get_selected_articles(len(articles) * 2), tag_urls=utilities.get_tags_and_urls(), first_article_url=first_article_url, last_article_url=last_article_url, prev_article_url=prev_article_url, next_article_url=next_article_url, show_comments_for_article=article_to_show_comments ) # No articles to show, so return the homepage. return redirect(url_for('home_bp.home'))