view wikked/views/read.py @ 307:e4e13e1138b2

Frontend refactor and backend support for cached page lists. * Page lists like orphans or broken redirects are now cached in the DB, and invalidated when something changes. * Refactor the frontend code to make it easier to add lots of other such page lists. Reskin the admin dashboard a bit. * Add new "broken redirects" list for testing new code.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 04 Oct 2014 21:05:22 -0700
parents 19a377f4e962
children bc02d4925096
line wrap: on
line source

import time
import urllib
from flask import (render_template, request, g, jsonify, make_response,
                   abort)
from flask.ext.login import current_user
from wikked.views import (get_page_meta, get_page_or_404, get_page_or_none,
        is_page_readable, get_redirect_target,
        url_from_viewarg, split_url_from_viewarg,
        RedirectNotFound, CircularRedirectError,
        CHECK_FOR_READ)
from wikked.web import app
from wikked.scm.base import STATE_NAMES


@app.route('/')
def home():
    tpl_name = 'index.html'
    if app.config['DEV_ASSETS']:
        tpl_name = 'index-dev.html'
    return render_template(tpl_name, cache_bust=('?%d' % time.time()));


@app.route('/read/<path:url>')
def read():
    tpl_name = 'index.html'
    if app.config['DEV_ASSETS']:
        tpl_name = 'index-dev.html'
    return render_template(tpl_name, cache_bust=('?%d' % time.time()));


@app.route('/search')
def search():
    tpl_name = 'index.html'
    if app.config['DEV_ASSETS']:
        tpl_name = 'index-dev.html'
    return render_template(tpl_name, cache_bust=('?%d' % time.time()));


@app.route('/api/list')
def api_list_all_pages():
    return api_list_pages(None)


@app.route('/api/list/<path:url>')
def api_list_pages(url):
    pages = filter(is_page_readable, g.wiki.getPages(url_from_viewarg(url)))
    page_metas = [get_page_meta(page) for page in pages]
    result = {'path': url, 'pages': list(page_metas)}
    return jsonify(result)


@app.route('/api/read/')
def api_read_main_page():
    return api_read_page(g.wiki.main_page_url.lstrip('/'))


@app.route('/api/read/<path:url>')
def api_read_page(url):
    additional_info = {}
    if 'user' in request.args:
        user = current_user
        if user.is_authenticated():
            user_page_url = 'user:' + user.username.title()
            additional_info['user'] = {
                    'username': user.username,
                    'groups': user.groups,
                    'page_url': user_page_url
                    }
        else:
            additional_info['user'] = False

    no_redirect = ('no_redirect' in request.args)

    endpoint, path = split_url_from_viewarg(url)
    if endpoint is None:
        # Normal page.
        try:
            page, visited_paths = get_redirect_target(
                    path,
                    fields=['url', 'title', 'text', 'meta'],
                    convert_url=False,
                    check_perms=CHECK_FOR_READ,
                    first_only=no_redirect)
        except RedirectNotFound as e:
            app.logger.exception(e)
            abort(404)
        except CircularRedirectError as e:
            app.logger.exception(e)
            abort(409)
        if page is None:
            abort(404)

        if no_redirect:
            additional_info['redirects_to'] = visited_paths[-1]
        elif len(visited_paths) > 1:
            additional_info['redirected_from'] = visited_paths[:-1]

        result = {'meta': get_page_meta(page), 'text': page.text}
        result.update(additional_info)
        return jsonify(result)

    # Meta listing page or special endpoint.
    meta_page_url = '%s:%s' % (endpoint, path)
    info_page = get_page_or_none(
            meta_page_url,
            fields=['url', 'title', 'text', 'meta'],
            convert_url=False,
            check_perms=CHECK_FOR_READ)

    endpoint_info = g.wiki.endpoints.get(endpoint)
    if endpoint_info is not None:
        # We have some information about this endpoint...
        if endpoint_info.default and info_page is None:
            # Default page text.
            info_page = get_page_or_404(
                    endpoint_info.default,
                    fields=['url', 'title', 'text', 'meta'],
                    convert_url=False,
                    check_perms=CHECK_FOR_READ)

        if not endpoint_info.query:
            # Not a query-based endpoint (like categories). Let's just
            # return the text.
            result = {
                    'endpoint': endpoint,
                    'meta': get_page_meta(info_page),
                    'text': info_page.text}
            result.update(additional_info)
            return jsonify(result)

    # Get the list of pages to show here.
    value = path.lstrip('/')
    query = {endpoint: [value]}
    pages = g.wiki.getPages(meta_query=query,
            fields=['url', 'title', 'text', 'meta'])
    tpl_data = {
            'name': endpoint,
            'url': meta_page_url,
            'value': value,
            'safe_value': urllib.quote(value.encode('utf-8')),
            'pages': [get_page_meta(p) for p in pages]
            # TODO: skip pages that are forbidden for the current user
        }
    if info_page:
        tpl_data['info_text'] = info_page.text

    # Render the final page as the list of pages matching the query,
    # under either a default text, or the text from the meta page.
    text = render_template('meta_page.html', **tpl_data)
    result = {
            'endpoint': endpoint,
            'meta_query': endpoint,
            'meta_value': value,
            'query': query,
            'meta': {
                    'url': urllib.quote(meta_page_url.encode('utf-8')),
                    'title': value
                },
            'text': text
        }
    if info_page:
        result['meta'] = get_page_meta(info_page)

    result.update(additional_info)
    return jsonify(result)


@app.route('/api/raw/')
def api_read_main_page_raw():
    return api_read_page_raw(g.wiki.main_page_url.lstrip('/'))


@app.route('/api/raw/<path:url>')
def api_read_page_raw(url):
    page = get_page_or_404(url, check_perms=CHECK_FOR_READ,
            fields=['raw_text', 'meta'])
    resp = make_response(page.raw_text)
    resp.mimetype = 'text/plain'
    return resp


@app.route('/api/query')
def api_query():
    query = dict(request.args)
    pages = g.wiki.getPages(meta_query=query)
    result = {
            'query': query,
            'pages': [get_page_meta(p) for p in pages]
        }
    return jsonify(result)


@app.route('/api/state/')
def api_get_main_page_state():
    return api_get_state(g.wiki.main_page_url.lstrip('/'))


@app.route('/api/state/<path:url>')
def api_get_state(url):
    page = get_page_or_404(url, check_perms=CHECK_FOR_READ,
            fields=['url', 'title', 'path', 'meta'])
    state = page.getState()
    return jsonify({
        'meta': get_page_meta(page, True),
        'state': STATE_NAMES[state]
        })


@app.route('/api/outlinks/')
def api_get_main_page_outgoing_links():
    return api_get_outgoing_links(g.wiki.main_page_url.lstrip('/'))


@app.route('/api/outlinks/<path:url>')
def api_get_outgoing_links(url):
    page = get_page_or_404(url, check_perms=CHECK_FOR_READ,
            fields=['url', 'title', 'links'])
    links = []
    for link in page.links:
        other = get_page_or_none(link, convert_url=False,
                fields=['url', 'title', 'meta'])
        if other is not None and is_page_readable(other):
            links.append({
                'url': other.url,
                'title': other.title
                })
        else:
            links.append({'url': link, 'missing': True})

    result = {'meta': get_page_meta(page), 'out_links': links}
    return jsonify(result)


@app.route('/api/inlinks/')
def api_get_main_page_incoming_links():
    return api_get_incoming_links(g.wiki.main_page_url.lstrip('/'))


@app.route('/api/inlinks/<path:url>')
def api_get_incoming_links(url):
    page = get_page_or_404(url, check_perms=CHECK_FOR_READ,
            fields=['url', 'title', 'meta'])
    links = []
    for link in page.getIncomingLinks():
        other = get_page_or_none(link, convert_url=False,
                fields=['url', 'title', 'meta'])
        if other is not None and is_page_readable(other):
            links.append({
                'url': link,
                'title': other.title
                })
        else:
            links.append({'url': link, 'missing': True})

    result = {'meta': get_page_meta(page), 'in_links': links}
    return jsonify(result)