Example #1
0
def generate_file(f, blog):
    '''
    Returns the page text and the pathname for a file to generate.
    Used with build_file but can be used for other things as well.
    '''

    if f.page is None:

        if f.xref.template.template_type == template_type.index:
            tags = template_tags(blog_id=blog.id)
        else:

            archive_pages = generate_archive_context_from_fileinfo(
                f.xref.archive_xref, blog.published_pages(), f)

            tags = template_tags(blog_id=blog.id,
                                 archive=archive_pages,
                                 archive_context=f)

    else:
        tags = template_tags(page_id=f.page.id)

    page_text = generate_page_text(f, tags)
    pathname = blog.path + "/" + f.file_path

    return (page_text, pathname)
Example #2
0
def blog_create(site_id):

    user = auth.is_logged_in(request)
    site = Site.load(site_id)
    permission = auth.is_site_admin(user, site)

    new_blog = Blog(
        name="",
        description="",
        url="",
        path="")

    tags = template_tags(site_id=site.id,
        user=user)

    tags.blog = new_blog
    from core.libs import pytz

    themes = Theme.select()

    return template('ui/ui_blog_settings',
        section_title="Create new blog",
        # search_context=(search_context['sites'], None),
        menu=generate_menu('site_create_blog', site),
        nav_default='all',
        timezones=pytz.all_timezones,
        themes=themes,
        ** tags.__dict__
        )
Example #3
0
def media_edit(blog_id, media_id, status=None):
    '''
    UI for editing a given media entry
    '''
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    is_member = auth.is_blog_member(user, blog)
    media = Media.load(media_id, blog)
    permission = auth.is_media_owner(user, media)

    from core.ui import kv
    kv_ui_data = kv.ui(media.kv_list())

    tags = template_tags(
        blog=blog,
        media=media,
        status=status,
        user=user,
    )
    tags.sidebar = sidebar.render_sidebar(panel_set='edit_media',
                                          status_badge=status_badge,
                                          kv_object='Media',
                                          kv_objectid=media.id,
                                          kv_ui=kv_ui_data)

    return media_edit_output(tags)
Example #4
0
def page_media_upload(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    overwrite = []

    for n in request.files:
        x = request.files.get(n)
        media_path = _join(page.blog.path, page.blog.media_path_generated)
        file_path = _join(media_path, x.filename)
        if _exists(file_path):
            from core.error import FileExistsError
            raise FileExistsError(
                "File '{}' already exists on the server.".format(
                    utils.html_escape(x.filename)))
        else:
            Media.register_media(x.filename, file_path, user, page=page)
            if not _exists(media_path):
                makedirs(media_path)
            x.save(file_path)

    tags = template_tags(page=page)

    return template('edit/page_media_list.tpl', **tags.__dict__)
Example #5
0
def delete_category(blog_id, category_id, confirm='N'):
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_admin(user, blog)

    category = Category.load(category_id, blog_id=blog.id)
    auth.check_category_editing_lock(blog)

    tags = template_tags(blog=blog, user=user)

    from core.utils import Status

    if request.forms.getunicode('confirm') == user.logout_nonce:
        message = 'Category {} successfully deleted'.format(category.for_log)
        url = '{}/blog/{}/categories'.format(BASE_URL, blog.id)
        action = 'Return to the category listing'

        reparent_categories = Category.update(
            parent_category=category.parent_category).where(
                Category.parent_category == category)
        reparent_categories.execute()

        delete_category = PageCategory.delete().where(
            PageCategory.category == category.id)
        delete_category.execute()

        category.delete_instance()

        tags.status = Status(type='success',
                             message=message,
                             action=action,
                             url=url,
                             close=False)

    else:
        message = (
            'You are about to delete category <b>{}</b> from blog <b>{}</b>.'.
            format(category.for_display, blog.for_display))

        yes = {
            'label': 'Yes, delete this category',
            'id': 'delete',
            'name': 'confirm',
            'value': user.logout_nonce
        }
        no = {
            'label': 'No, return to category properties',
            'url': '{}/blog/{}/category/{}'.format(BASE_URL, blog.id,
                                                   category.id)
        }

        tags.status = Status(message=message,
                             type='warning',
                             close=False,
                             yes=yes,
                             no=no)

    tags.category = category

    return report(tags, 'blog_delete_category', category)
Example #6
0
def build_indexes_fileinfos(templates):
    '''
    Rebuilds a fileinfo entry for a given main index.

    This will need to be run every time we create a new index type,
    or change a mapping. (Most of these should have a 1:1 mapping)

    A control message should not be needed, since these are 1:1

    This will port the code currently found in build_blog_fileinfo, much as the above function did.

    '''
    n = 0

    for template in templates:
        n += 1

        index_mappings = TemplateMapping.select().where(
            TemplateMapping.template == template)

        blog = index_mappings[0].template.blog

        tags = template_tags(blog_id=blog.id)

        for i in index_mappings:
            path_string = tpl(tpl_oneline(i.path_string), **tags.__dict__)
            if path_string == '':
                continue
            master_path_string = path_string
            add_page_fileinfo(None, i, master_path_string,
                              blog.url + "/" + master_path_string,
                              blog.path + '/' + master_path_string)

    return n
Example #7
0
def blog_publish(blog_id):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)

    queue_length = Queue.job_counts(blog=blog)

    if queue_length > 0:
        start_message = template('queue/queue_run_include',
                                 queue=Queue.jobs(blog),
                                 percentage_complete=0,
                                 blog=blog,
                                 break_path='{}/blog/{}/publish/break'.format(
                                     BASE_URL, blog.id))
        Queue.start(blog, queue_length)
    else:
        start_message = "Queue empty."

    tags = template_tags(blog_id=blog.id, user=user)

    #
    return template(
        'queue/queue_run_ui',
        start=queue_length,
        start_message=start_message,
        action_url="../../blog/{}/publish/progress/{}".format(
            blog.id, queue_length),
        title='Publishing queue progress',
        # search_context=(search_context['blog_queue'], blog),
        menu=generate_menu('blog_queue', blog),
        **tags.__dict__)
Example #8
0
def media_edit(blog_id, media_id, status=None):
    '''
    UI for editing a given media entry
    '''
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    is_member = auth.is_blog_member(user, blog)
    media = Media.load(media_id, blog)
    permission = auth.is_media_owner(user, media)

    from core.ui import kv
    kv_ui_data = kv.ui(media.kv_list())

    tags = template_tags(blog=blog,
         media=media,
         status=status,
         user=user,
        )
    tags.sidebar = sidebar.render_sidebar(
            panel_set='edit_media',
            status_badge=status_badge,
            kv_object='Media',
            kv_objectid=media.id,
            kv_ui=kv_ui_data)

    return media_edit_output(tags)
Example #9
0
def main_ui():
    '''
    Top level UI
    This will eventually become a full-blown user dashboard.
    Right now it just returns a list of sites in the system.
    All users for the system can see this dashboard.
    '''
    user = auth.is_logged_in(request)

    # TODO: replace with actual user-centric setting
    try:
        from settings import MAX_RECENT_PAGES
    except ImportError:
        MAX_RECENT_PAGES = 10

    recent_pages = Page.select().where(Page.user == user).order_by(
        Page.modified_date.desc()).limit(MAX_RECENT_PAGES)

    your_blogs = user.blogs()

    tpl = template('ui/ui_dashboard',
                   search_context=(search_contexts['sites'], None),
                   menu=generate_menu('system_menu', None),
                   recent_pages=recent_pages,
                   your_blogs=your_blogs,
                   **template_tags(user=user).__dict__)

    return tpl
Example #10
0
def blog_delete_preview(blog_id, preview_id):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_admin(user, blog)

    f = lambda:None
    f.blog = blog

    if preview_id == 'all':
        previews_to_delete = blog.fileinfos.where(FileInfo.preview_path.is_null(False))
        message = 'All previews for blog {} deleted.'.format(blog.for_display)
        f.msg = 'Delete all'
    else:
        previews_to_delete = blog.fileinfos.where(FileInfo.id == preview_id)
        message = 'Preview for fileinfo {} deleted.'.format(preview_id)
        f.msg = 'Delete preview {}'.format(preview_id)

    for n in previews_to_delete:
        if n.page is not None:
            n.page.delete_preview()
        else:
            n.template_mapping.template.delete_preview()

    tags = template_tags(blog_id=blog.id, user=user)

    from core.utils import Status

    tags.status = Status(
        type='success',
        message=message,
        close=False,
        )

    return report(tags, 'blog_delete_preview', f)
Example #11
0
def login_verify():
    '''
    Verifies user login, provides session cookie if successful
    '''
    _forms = request.forms

    email = _forms.get('email')
    password = _forms.get('password')

    if login_verify_core(email, password) is True:

        if request.query.action:
            utils.safe_redirect(request.query.action)
        else:
            redirect(BASE_URL)

    else:
        tags = template_tags()

        tags.status = utils.Status(
            type='danger',
            no_sure=True,
            message="Email or password not found.")

        return template('ui/ui_login',
            **tags.__dict__)
Example #12
0
def main_ui():
    '''
    Top level UI
    This will eventually become a full-blown user dashboard.
    Right now it just returns a list of sites in the system.
    All users for the system can see this dashboard.
    '''
    user = auth.is_logged_in(request)

    # TODO: replace with actual user-centric setting
    try:
        from settings import MAX_RECENT_PAGES
    except ImportError:
        MAX_RECENT_PAGES = 10

    recent_pages = Page.select().where(
        Page.user == user).order_by(
        Page.modified_date.desc()).limit(MAX_RECENT_PAGES)

    your_blogs = user.blogs()

    tpl = template('ui/ui_dashboard',
        search_context=(search_contexts['sites'], None),
        menu=generate_menu('system_menu', None),
        recent_pages=recent_pages,
        your_blogs=your_blogs,
        **template_tags(user=user).__dict__
        )

    return tpl
Example #13
0
def blog_publish(blog_id):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)

    queue_length = Queue.job_counts(blog=blog)

    if queue_length > 0:
        start_message = template('queue/queue_run_include',
            queue=Queue.jobs(blog),
            percentage_complete=0,
            blog=blog,
            break_path='{}/blog/{}/publish/break'.format(BASE_URL, blog.id)
            )
        Queue.start(blog, queue_length)
    else:
        start_message = "Queue empty."

    tags = template_tags(blog_id=blog.id,
            user=user)

    #
    return template('queue/queue_run_ui',
        start=queue_length,
        start_message=start_message,
        action_url="../../blog/{}/publish/progress/{}".format(blog.id,
                                                              queue_length),
        title='Publishing queue progress',
        # search_context=(search_context['blog_queue'], blog),
        menu=generate_menu('blog_queue', blog),
        **tags.__dict__)
Example #14
0
def page_media_upload(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    overwrite = []

    for n in request.files:
        x = request.files.get(n)
        media_path = _join(page.blog.path, page.blog.media_path_generated)
        file_path = _join(media_path, x.filename)
        if _exists(file_path):
            from core.error import FileExistsError
            raise FileExistsError("File '{}' already exists on the server.".format(
                utils.html_escape(x.filename)))
        else:
            Media.register_media(x.filename, file_path, user, page=page)
            if not _exists(media_path):
                makedirs(media_path)
            x.save(file_path)

    tags = template_tags(page=page)

    return template('edit/page_media_list.tpl',
        **tags.__dict__)
Example #15
0
def build_pages_fileinfos(pages):
    '''
    Creates fileinfo entries for the template mappings associated with
    an iterable list of Page objects.
    '''

    n = 0
    for page in pages:
        n += 1
        template_mappings = page.template_mappings

        if template_mappings.count() == 0:
            raise TemplateMapping.DoesNotExist(
                'No template mappings found for this page.')

        tags = template_tags(page_id=page.id)

        for t in template_mappings:
            path_string = generate_date_mapping(page.publication_date.date(),
                                                tags, t.path_string)
            if path_string == '':
                continue
            master_path_string = path_string + "." + page.blog.base_extension
            add_page_fileinfo(page, t, master_path_string,
                              page.blog.url + "/" + master_path_string,
                              page.blog.path + '/' + master_path_string,
                              str(page.publication_date))

    return n
Example #16
0
def media_edit_save(blog_id, media_id):
    '''
    Save changes to a media entry.
    '''
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    is_member = auth.is_blog_member(user, blog)
    media = Media.load(media_id)
    permission = auth.is_media_owner(user, media)

    friendly_name = request.forms.getunicode('media_friendly_name')

    changes = False

    if friendly_name != media.friendly_name:
        changes = True
        media.friendly_name = friendly_name

    import datetime

    if changes is True:
        media.modified_date = datetime.datetime.utcnow()
        media.save()

        status = utils.Status(
            type='success',
            message='Changes to media <b>{}</b> saved successfully.'.format(
                media.for_display)
            )
    else:

        status = utils.Status(
            type='warning',
            no_sure=True,
            message='No discernible changes submitted for media <b>{}</b>.'.format(
                media.id, media.for_display)
            )

    logger.info("Media {} edited by user {}.".format(
        media.for_log,
        user.for_log))

    from core.ui import kv
    kv_ui_data = kv.ui(media.kv_list())

    tags = template_tags(blog=blog,
         media=media,
         status=status,
         user=user)

    tags.sidebar = sidebar.render_sidebar(
        panel_set='edit_media',
        status_badge=status_badge,
        kv_object='Media',
        kv_objectid=media.id,
        kv_ui=kv_ui_data)

    return media_edit_output(tags)
Example #17
0
def blog_new_page(blog_id):
    '''
    Displays UI for newly created (unsaved) page
    '''
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_member(user, blog)

    tags = template_tags(blog=blog, user=user)

    tags.page = Page()

    referer = request.headers.get('Referer')
    if referer is None:
        referer = BASE_URL + "/blog/" + str(blog.id)

    blog_new_page = tags.page

    for n in new_page_submission_fields:
        blog_new_page.__setattr__(n, "")
        if n in request.query:
            blog_new_page.__setattr__(n, request.query.getunicode(n))

    import datetime

    blog_new_page.blog = blog
    blog_new_page.user = user
    blog_new_page.publication_date = datetime.datetime.utcnow()
    blog_new_page.basename = ''

    from core.cms import save_action_list

    from core.ui import kv
    kv_ui_data = kv.ui(blog_new_page.kv_list())

    try:
        html_editor_settings = Template.get(
            Template.blog == blog, Template.title == 'HTML Editor Init',
            Template.template_type == template_type.system).body
    except Template.DoesNotExist:
        from core.static import html_editor_settings

    return template(
        'edit/page',
        menu=generate_menu('create_page', blog),
        parent_path=referer,
        # search_context=(search_context['blog'], blog),
        html_editor_settings=html_editor_settings,
        sidebar=sidebar.render_sidebar(panel_set='edit_page',
                                       status_badge=status_badge,
                                       save_action=save_action,
                                       save_action_list=save_action_list,
                                       kv_ui=kv_ui_data,
                                       kv_object='Page',
                                       kv_objectid=None,
                                       **tags.__dict__),
        **tags.__dict__)
Example #18
0
def build_archives_fileinfos(pages):
    '''
    Takes a page (maybe a collection of same) and produces fileinfos
    for the date-based archive entries for each
    '''

    counter = 0

    mapping_list = {}

    for page in pages:

        tags = template_tags(page_id=page.id)

        if page.archive_mappings.count() == 0:
            raise TemplateMapping.DoesNotExist(
                'No template mappings found for the archives for this page.')

        for m in page.archive_mappings:

            path_string = generate_date_mapping(page.publication_date, tags,
                                                m.path_string)
            if path_string == '':
                continue
            if path_string in mapping_list:
                continue

            # tag_context = generate_archive_context_from_page(m.archive_xref, page.blog, page)
            mapping_list[path_string] = ((
                None,
                m,
                path_string,
                page.blog.url + "/" + path_string,
                page.blog.path + '/' + path_string,
            ), (page))

    for n in mapping_list:
        counter += 1
        new_fileinfo = add_page_fileinfo(*mapping_list[n][0])
        archive_context = []
        m = mapping_list[n][0][1]

        for r in m.archive_xref:
            archive_context.append(archive_functions[r]["format"](
                archive_functions[r]["mapping"](mapping_list[n][1])))

        for t, r in zip(archive_context, m.archive_xref):
            new_fileinfo_context = FileInfoContext.get_or_create(
                fileinfo=new_fileinfo, object=r, ref=t)

        new_fileinfo.mapping_sort = archive_context
        new_fileinfo.save()

    # @return mapping_list
    return counter
Example #19
0
def new_category(blog_id):

    from core.models import db
    with db.atomic() as txn:

        user = auth.is_logged_in(request)
        blog = Blog.load(blog_id)
        permission = auth.is_blog_editor(user, blog)

        category_list = [n for n in blog.categories]

        category = Category(id=0,
            title='',
            blog=blog)

        top_level_category = Category(
            id=None,
            title='[Top-level category]',
            parent=None
            )

        category_list.insert(0, top_level_category)

        tags = template_tags(
            blog=blog,
            user=user)

    if request.method == "POST":
        with db.atomic() as txn:
            category_title = request.forms.getunicode('category_title')
            try:
                parent_category = int(request.forms.getunicode('category_parent'))
            except ValueError:
                parent_category = None

            with db.atomic() as txn:

                category = Category(blog=blog,
                    title=category_title,
                    parent_category=parent_category
                    )
                category.save()

        redirect('{}/blog/{}/category/{}'.format(
            BASE_URL, blog.id, category.id))

    tpl = template('edit/category',
        category=category,
        category_list=category_list,
        menu=generate_menu('blog_new_category', category),
        search_context=(search_contexts['sites'], None),
        **tags.__dict__)

    return tpl
Example #20
0
def login():
    '''
    User login interface
    '''
    tpl = template('ui/ui_login', **template_tags().__dict__)

    logger.info("Login page requested from IP {}.".format(request.remote_addr))

    response.delete_cookie("login", path="/")

    return tpl
Example #21
0
def generate_page_tags(f, blog):
    '''
    Returns the page text and the pathname for a file to generate.
    Used with build_file but can be used for other things as well.

    :param f:
        The fileinfo object to use.
    :param blog:
        The blog object to use as the context for the fileinfo.
    '''

    if f.page is None:

        if f.xref.template.template_type == template_type.index:
            tags = template_tags(blog=blog,
                template=f.xref.template,
                fileinfo=f)
        else:

            archive_pages = generate_archive_context_from_fileinfo(
                f.xref.archive_xref, blog.pages.published, f)

            # The context object we use

            tags = template_tags(blog=blog,
                template=f.xref.template,
                archive=archive_pages,
                archive_context=f,
                fileinfo=f)

    else:
        tags = template_tags(page=f.page,
            template=f.xref.template,
            fileinfo=f)

    if tags.archive is not None:
        if tags.archive.pages.count() == 0:
            raise NoArchiveForFileInfo('No archives for page {} using fileinfo {}'.format(
            f.page, f))

    return tags
Example #22
0
def page_edit(page_id):
    '''
    UI for editing a page in a blog
    '''
    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    status = None
    referer = request.headers.get('Referer')

    if (referer is None
        or page.modified_date is None
        or re.match(re.escape(BASE_URL + "/blog/" + str(page.blog.id)), referer) is None):

        referer = BASE_URL + "/blog/" + str(page.blog.id)

    if page.modified_date is None:
        status = utils.Status(
            type='info',
            message="Page <b>{}</b> created.".format(page.for_log))
        page.modified_date = datetime.datetime.utcnow()
        page.save(user)

    tags = template_tags(page=page,
        user=user,
        status=status)

    from core.ui import kv
    kv_ui_data = kv.ui(page.kv_list())

    tpl = template('edit/page',
        menu=generate_menu('edit_page', page),
        parent_path=referer,
        # search_context=(search_context['blog'], page.blog),
        html_editor_settings=html_editor_settings(page.blog),
        sidebar=sidebar.render_sidebar(
            panel_set='edit_page',
            status_badge=status_badge,
            save_action_list=save_action_list,
            save_action=save_action,
            kv_ui=kv_ui_data,
            kv_object='Page',
            kv_objectid=page.id,
            **tags.__dict__),
        msg_float=False,
        **tags.__dict__)

    logger.info("Page {} opened for editing by {}.".format(
        page.for_log,
        user.for_log))

    return tpl
Example #23
0
def login():
    '''
    User login interface
    '''
    tpl = template('ui/ui_login',
        **template_tags().__dict__)

    logger.info("Login page requested from IP {}.".format(request.remote_addr))

    response.delete_cookie("login", path="/")

    return tpl
Example #24
0
def blog_apply_theme(blog_id, theme_id):
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)
    reason = auth.check_template_lock(blog)

    theme = Theme.load(theme_id)

    tags = template_tags(blog=blog,
            user=user)

    from core.utils import Status

    if request.forms.getunicode('confirm') == user.logout_nonce:

        from core.models import db
        with db.atomic() as txn:
            blog.apply_theme(theme, user)

        status = Status(
            type='success',
            close=False,
            message='''
Theme <b>{}</b> was successfully applied to blog <b>{}</b>.</p>
It is recommended that you <a href="{}">republish this blog.</a>
'''.format(theme.for_display, blog.for_display, '{}/blog/{}/republish'.format(
                BASE_URL, blog.id))
            )

    else:

        status = Status(
            type='warning',
            close=False,
            message='''
You are about to apply theme <b>{}</b> to blog <b>{}</b>.</p>
<p>This will OVERWRITE AND REMOVE ALL EXISTING TEMPLATES on this blog!</p>
'''.format(theme.for_display, blog.for_display),
            url='{}/blog/{}/themes'.format(
                BASE_URL, blog.id),
            yes={'id':'delete',
                'name':'confirm',
                'label':'Yes, I want to apply this theme',
                'value':user.logout_nonce},
            no={'label':'No, don\'t apply this theme',
                'url':'{}/blog/{}/themes'.format(
                BASE_URL, blog.id)}
            )

    tags.status = status if reason is None else reason

    return report(tags, 'blog_apply_theme', [blog, theme])
Example #25
0
def blog_settings(blog_id, nav_setting):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_admin(user, blog)

    auth.check_settings_lock(blog)

    tags = template_tags(blog_id=blog.id, user=user)

    tags.nav_default = nav_setting

    return blog_settings_output(tags)
Example #26
0
def media_edit_save(blog_id, media_id):
    '''
    Save changes to a media entry.
    '''
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    is_member = auth.is_blog_member(user, blog)
    media = Media.load(media_id)
    permission = auth.is_media_owner(user, media)

    friendly_name = request.forms.getunicode('media_friendly_name')

    changes = False

    if friendly_name != media.friendly_name:
        changes = True
        media.friendly_name = friendly_name

    import datetime

    if changes is True:
        media.modified_date = datetime.datetime.utcnow()
        media.save()

        status = utils.Status(
            type='success',
            message='Changes to media <b>{}</b> saved successfully.'.format(
                media.for_display))
    else:

        status = utils.Status(
            type='warning',
            no_sure=True,
            message='No discernible changes submitted for media <b>{}</b>.'.
            format(media.id, media.for_display))

    logger.info("Media {} edited by user {}.".format(media.for_log,
                                                     user.for_log))

    from core.ui import kv
    kv_ui_data = kv.ui(media.kv_list())

    tags = template_tags(blog=blog, media=media, status=status, user=user)

    tags.sidebar = sidebar.render_sidebar(panel_set='edit_media',
                                          status_badge=status_badge,
                                          kv_object='Media',
                                          kv_objectid=media.id,
                                          kv_ui=kv_ui_data)

    return media_edit_output(tags)
Example #27
0
def plugin_settings(plugin_id, errormsg=None):
    user = auth.is_logged_in(request)
    permission = auth.is_sys_admin(user)
    plugin = Plugin.get(Plugin.id == plugin_id)

    tags = template_tags(user=user)

    tpl = template('system/plugin',
                   plugin_ui=plugin.ui(),
                   search_context=(search_contexts['sites'], None),
                   menu=generate_menu('system_plugin_data', plugin),
                   **tags.__dict__)

    return tpl
Example #28
0
def blog_settings(blog_id, nav_setting):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_admin(user, blog)

    auth.check_settings_lock(blog)

    tags = template_tags(blog_id=blog.id,
        user=user)

    tags.nav_default = nav_setting

    return blog_settings_output(tags)
Example #29
0
def blog_apply_theme(blog_id, theme_id):
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)
    reason = auth.check_template_lock(blog)

    theme = Theme.load(theme_id)

    tags = template_tags(blog=blog, user=user)

    from core.utils import Status

    if request.forms.getunicode('confirm') == user.logout_nonce:

        from core.models import db
        with db.atomic() as txn:
            blog.apply_theme(theme, user)

        status = Status(type='success',
                        close=False,
                        message='''
Theme <b>{}</b> was successfully applied to blog <b>{}</b>.</p>
It is recommended that you <a href="{}">republish this blog.</a>
'''.format(theme.for_display, blog.for_display,
           '{}/blog/{}/republish'.format(BASE_URL, blog.id)))

    else:

        status = Status(type='warning',
                        close=False,
                        message='''
You are about to apply theme <b>{}</b> to blog <b>{}</b>.</p>
<p>This will OVERWRITE AND REMOVE ALL EXISTING TEMPLATES on this blog!</p>
'''.format(theme.for_display, blog.for_display),
                        url='{}/blog/{}/themes'.format(BASE_URL, blog.id),
                        yes={
                            'id': 'delete',
                            'name': 'confirm',
                            'label': 'Yes, I want to apply this theme',
                            'value': user.logout_nonce
                        },
                        no={
                            'label': 'No, don\'t apply this theme',
                            'url':
                            '{}/blog/{}/themes'.format(BASE_URL, blog.id)
                        })

    tags.status = status if reason is None else reason

    return report(tags, 'blog_apply_theme', [blog, theme])
Example #30
0
def page_edit(page_id):
    '''
    UI for editing a page in a blog
    '''
    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    status = None
    referer = request.headers.get('Referer')

    if (referer is None or page.modified_date is None
            or re.match(re.escape(BASE_URL + "/blog/" + str(page.blog.id)),
                        referer) is None):

        referer = BASE_URL + "/blog/" + str(page.blog.id)

    if page.modified_date is None:
        status = utils.Status(type='info',
                              message="Page <b>{}</b> created.".format(
                                  page.for_log))
        page.modified_date = datetime.datetime.utcnow()
        page.save(user)

    tags = template_tags(page=page, user=user, status=status)

    from core.ui import kv
    kv_ui_data = kv.ui(page.kv_list())

    tpl = template(
        'edit/page',
        menu=generate_menu('edit_page', page),
        parent_path=referer,
        # search_context=(search_context['blog'], page.blog),
        html_editor_settings=html_editor_settings(page.blog),
        sidebar=sidebar.render_sidebar(panel_set='edit_page',
                                       status_badge=status_badge,
                                       save_action_list=save_action_list,
                                       save_action=save_action,
                                       kv_ui=kv_ui_data,
                                       kv_object='Page',
                                       kv_objectid=page.id,
                                       **tags.__dict__),
        msg_float=False,
        **tags.__dict__)

    logger.info("Page {} opened for editing by {}.".format(
        page.for_log, user.for_log))

    return tpl
Example #31
0
def page_media_delete(page_id, media_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    media = Media.load(media_id)
    media_reference = MediaAssociation.get(MediaAssociation.page == page,
                                           MediaAssociation.media == media)
    media_reference.delete_instance(recursive=True, delete_nullable=True)

    tags = template_tags(page=page)

    return template('edit/page_media_list.tpl', **tags.__dict__)
Example #32
0
def template_set_default(template_id):
    '''
    UI for setting a given template as the default for an archive type
    '''

    user = auth.is_logged_in(request)
    tpl = Template.load(template_id)
    blog = Blog.load(tpl.blog.id)
    permission = auth.is_blog_designer(user, blog)

    auth.check_template_lock(blog)

    tags = template_tags(template=tpl, user=user)

    from core.utils import Status
    import settings

    if request.forms.getunicode('confirm') == user.logout_nonce:

        # check for form submission
        # setting should be done by way of class object
        # theme? blog? template?
        # blog.set_default_archive_template(template,{archive_type.index...})

        status = Status(
            type='success',
            close=False,
            message=
            'Template <b>{}</b> was successfully refreshed from theme <b>{}</b>.'
            .format(tpl.for_display, tpl.theme.for_display),
            action='Return to template',
            url='{}/template/{}/edit'.format(settings.BASE_URL, tpl.id))
        tags.status = status
    else:
        pass

    from core.models import archive_defaults

    return template('edit/template-set-default',
                    icons=icons,
                    search_context=(search_contexts['blog'], tags.blog),
                    menu=generate_menu('blog_edit_template', tags.template),
                    sidebar=sidebar.render_sidebar(
                        panel_set='edit_template',
                        publishing_mode=publishing_mode,
                        types=template_type,
                        **tags.__dict__),
                    archive_defaults=archive_defaults,
                    **tags.__dict__)
Example #33
0
def template_delete(template_id):
    '''
    UI for deleting a template
    '''
    user = auth.is_logged_in(request)
    tpl = Template.load(template_id)
    blog = Blog.load(tpl.blog)
    permission = auth.is_blog_designer(user, blog)

    from core.utils import Status
    import settings

    tags = template_tags(template_id=tpl.id, user=user)

    if request.forms.getunicode('confirm') == user.logout_nonce:

        # _template.delete(tpl)
        tpl.delete_instance()

        status = Status(type='success',
                        close=False,
                        message='Template {} was successfully deleted.'.format(
                            tpl.for_log),
                        action='Return to template list',
                        url='{}/blog/{}/templates'.format(
                            settings.BASE_URL, blog.id))

    else:

        status = Status(
            type='warning',
            close=False,
            message=
            'You are attempting to delete template <b>{}</b> from blog <b>{}</b>.'
            .format(tpl.for_display, blog.for_display),
            no={
                'url': '{}/template/{}/edit'.format(settings.BASE_URL, tpl.id),
                'label': 'No, I don\'t want to delete this template'
            },
            yes={
                'id': 'delete',
                'name': 'confirm',
                'label': 'Yes, I want to delete this template',
                'value': user.logout_nonce
            })

    tags.status = status

    return report(tags, 'blog_delete_template', tpl)
Example #34
0
def plugin_settings(plugin_id, errormsg=None):
    user = auth.is_logged_in(request)
    permission = auth.is_sys_admin(user)
    plugin = Plugin.get(Plugin.id == plugin_id)

    tags = template_tags(
        user=user)

    tpl = template('system/plugin',
        plugin_ui=plugin.ui(),
        search_context=(search_contexts['sites'], None),
        menu=generate_menu('system_plugin_data', plugin),
        **tags.__dict__)

    return tpl
Example #35
0
def page_media_add(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    media_list = Media.select().where(Media.blog == page.blog)

    tags = template_tags(page=page, user=user)

    return template('modal/modal_images.tpl',
                    media_list=media_list,
                    title="Select media",
                    buttons='',
                    **tags.__dict__)
Example #36
0
def new_category(blog_id):

    from core.models import db
    with db.atomic() as txn:

        user = auth.is_logged_in(request)
        blog = Blog.load(blog_id)
        permission = auth.is_blog_editor(user, blog)

        category_list = [n for n in blog.categories]

        category = Category(id=0, title='', blog=blog)

        top_level_category = Category(id=None,
                                      title='[Top-level category]',
                                      parent=None)

        category_list.insert(0, top_level_category)

        tags = template_tags(blog=blog, user=user)

    if request.method == "POST":
        with db.atomic() as txn:
            category_title = request.forms.getunicode('category_title')
            try:
                parent_category = int(
                    request.forms.getunicode('category_parent'))
            except ValueError:
                parent_category = None

            with db.atomic() as txn:

                category = Category(blog=blog,
                                    title=category_title,
                                    parent_category=parent_category)
                category.save()

        redirect('{}/blog/{}/category/{}'.format(BASE_URL, blog.id,
                                                 category.id))

    tpl = template('edit/category',
                   category=category,
                   category_list=category_list,
                   menu=generate_menu('blog_new_category', category),
                   search_context=(search_contexts['sites'], None),
                   **tags.__dict__)

    return tpl
Example #37
0
def page_revisions(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)
    tags = template_tags(page=page)

    try:
        tpl = template('modal/modal_revisions',
                       title='Revisions for page #{}'.format(page.id),
                       buttons='',
                       **tags.__dict__)
    except:
        raise

    return tpl
Example #38
0
def system_theme_data(theme_id):
    user = auth.is_logged_in(request)
    permission = auth.is_sys_admin(user)
    from core.models import Theme
    theme = Theme.load(theme_id)

    tags = template_tags(user=user)

    tags.report = ['Theme title: {}'.format(theme.title),
        'Theme description: {}'.format(theme.description),
        'Theme directory: {}'.format(theme.json),
        '<hr>',
        '<a href="{}/download">Download an archive of this theme</a>'.format(theme.id)
        ]

    return report(tags, 'system_theme_data', theme)
Example #39
0
def blog_queue(blog_id, status=None):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)

    tags = template_tags(blog_id=blog.id, user=user, status=status)

    return listing(request,
                   blog,
                   tags.queue,
                   'queue',
                   'blog_menu',
                   user=user,
                   tags_data={'blog': blog},
                   errormsg=tags.status)
Example #40
0
def blog_queue(blog_id, status=None):

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)

    tags = template_tags(blog_id=blog.id,
            user=user,
            status=status)

    return listing(request, blog, tags.queue,
                   'queue', 'blog_menu',
                   user=user,
                   tags_data={'blog':blog},
                   errormsg=tags.status
                   )
Example #41
0
def page_revisions(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)
    tags = template_tags(page=page)

    try:
        tpl = template('modal/modal_revisions',
        title='Revisions for page #{}'.format(page.id),
        buttons='',
        **tags.__dict__)
    except:
        raise

    return tpl
Example #42
0
def page_revision_restore(page_id, revision_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)
    page_revision = PageRevision.select().where(PageRevision.id == revision_id).get()

    status = utils.Status(
        type='success',
        message='Page <b>{}</b> has been restored from backup dated {}.'.format(page.for_log,
            page_revision.modified_date)
        )

    tags = template_tags(page=page,
        user=user,
        status=status)

    page_revision.id = page.id
    tags.page = page_revision

    referer = BASE_URL + "/blog/" + str(page.blog.id)

    # from core.cms import save_action_list
    # from core.ui_kv import kv_ui
    from core.ui import kv
    kv_ui_data = kv.ui(page.kv_list())
    # TODO: save action from this doesn't trigger queue run

    tpl = template('edit/page',
        status_badge=status_badge,
        save_action=save_action,
        menu=generate_menu('edit_page', page),
        search_context=(search_contexts['blog'], page.blog),
        html_editor_settings=html_editor_settings(page.blog),
        sidebar=sidebar.render_sidebar(
            panel_set='edit_page',
            status_badge=status_badge,
            save_action=save_action,
            save_action_list=save_action_list,
            kv_ui=kv_ui_data,
            kv_object='Page',
            kv_objectid=page.id,
            **tags.__dict__
            ),
        **tags.__dict__)

    return tpl
Example #43
0
def build_pages_fileinfos(pages, template_mappings=None):
    '''
    Creates fileinfo entries for the template mappings associated with
    an iterable list of Page objects.
    :param pages:
        List of page objects to build fileinfos for.
    '''

    fileinfos = []

    for n, page in enumerate(pages):

        if template_mappings is None:
            mappings = page.template_mappings
        else:
            mappings = template_mappings

        if mappings.count() == 0:
            raise TemplateMapping.DoesNotExist('No template mappings found for this page.')

        tags = template_tags(page=page)

        for t in mappings:

            # path_string = replace_mapping_tags(t.path_string)
            path_string = generate_date_mapping(
                page.publication_date_tz.date(), tags,
                replace_mapping_tags(t.path_string))

            # for tag archives, we need to return a list from the date mapping
            # in the event that we have a tag present that's an iterable like the tag list
            # e.g., for /%t/%Y, for a given page that has five tags
            # we return five values, one for each tag, along with the year

            if path_string == '' or path_string is None:
                continue

            # master_path_string = path_string

            fileinfos.append(
                add_page_fileinfo(page, t, path_string,
                    page.blog.url + "/" + path_string,
                    page.blog.path + '/' + path_string,
                    str(page.publication_date_tz))
                )

    return fileinfos
Example #44
0
def blog_purge(blog_id):
    '''
    UI for purging/republishing an entire blog
    Eventually to be reworked
    '''

    user = auth.is_logged_in(request)

    blog = Blog.load(blog_id)

    permission = auth.is_blog_publisher(user, blog)

    tags = template_tags(blog=blog, user=user)

    tags.report = cms.purge_blog(blog)

    return report(tags, 'blog_purge', blog)
Example #45
0
def kv_response(object_name, object_type, object_identifier, object_id):
    # from core.ui_kv import kv_ui
    from core.ui import kv
    from core.models import template_tags

    tag_args = {}
    tag_args[object_identifier] = object_id
    tags = template_tags(**tag_args)
    kv_ui_data = kv.ui(tags.__dict__[object_type].kv_list())

    tpl = template('sidebar/kv',
        kv_ui=kv_ui_data,
        kv_object=object_name,
        kv_objectid=object_id,
        **tags.__dict__)

    return tpl
Example #46
0
def page_media_add(page_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    media_list = Media.select().where(
        Media.blog == page.blog)

    tags = template_tags(page=page,
        user=user)

    return template('modal/modal_images.tpl',
        media_list=media_list,
        title="Select media",
        buttons='',
        **tags.__dict__)
Example #47
0
def page_media_delete(page_id, media_id):

    user = auth.is_logged_in(request)
    page = Page.load(page_id)
    permission = auth.is_page_editor(user, page)

    media = Media.load(media_id)
    media_reference = MediaAssociation.get(
        MediaAssociation.page == page,
        MediaAssociation.media == media)
    media_reference.delete_instance(recursive=True,
        delete_nullable=True)

    tags = template_tags(page=page)

    return template('edit/page_media_list.tpl',
        **tags.__dict__)
Example #48
0
def system_theme_data(theme_id):
    user = auth.is_logged_in(request)
    permission = auth.is_sys_admin(user)
    from core.models import Theme
    theme = Theme.load(theme_id)

    tags = template_tags(user=user)

    tags.report = [
        'Theme title: {}'.format(theme.title),
        'Theme description: {}'.format(theme.description),
        'Theme directory: {}'.format(theme.json), '<hr>',
        '<a href="{}/download">Download an archive of this theme</a>'.format(
            theme.id)
    ]

    return report(tags, 'system_theme_data', theme)
Example #49
0
def blog_purge(blog_id):
    '''
    UI for purging/republishing an entire blog
    Eventually to be reworked
    '''

    user = auth.is_logged_in(request)

    blog = Blog.load(blog_id)

    permission = auth.is_blog_publisher(user, blog)

    tags = template_tags(blog=blog, user=user)

    tags.report = cms.purge_blog(blog)

    return report(tags, 'blog_purge', blog)
Example #50
0
File: kv.py Project: ra2003/mercury
def kv_response(object_name, object_type, object_identifier, object_id):
    # from core.ui_kv import kv_ui
    from core.ui import kv
    from core.models import template_tags

    tag_args = {}
    tag_args[object_identifier] = object_id
    tags = template_tags(**tag_args)
    kv_ui_data = kv.ui(tags.__dict__[object_type].kv_list())

    tpl = template('sidebar/kv',
                   kv_ui=kv_ui_data,
                   kv_object=object_name,
                   kv_objectid=object_id,
                   **tags.__dict__)

    return tpl
Example #51
0
def build_indexes_fileinfos(templates):

    '''
    Rebuilds a fileinfo entry for a given main index.

    This will need to be run every time we create a new index type,
    or change a mapping. (Most of these should have a 1:1 mapping)

    A control message should not be needed, since these are 1:1

    This will port the code currently found in build_blog_fileinfo, much as the above function did.

    :param templates:
        A list of templates, typically for main indexes, to rebuild fileinfo entries for.

    '''
    for n, template in enumerate(templates):

        index_mappings = TemplateMapping.select().where(
            TemplateMapping.template == template)

        blog = index_mappings[0].template.blog

        tags = template_tags(blog_id=blog.id)

        for i in index_mappings:
            path_string = replace_mapping_tags(i.path_string)
            path_string = eval(path_string, tags.__dict__)

            if path_string == '' or path_string is None:
                continue

            # why are we doing this twice?
            # path_string = replace_mapping_tags(path_string)

            # raise Exception(path_string)

            master_path_string = path_string
            add_page_fileinfo(None, i, master_path_string,
                 blog.url + "/" + master_path_string,
                 blog.path + '/' + master_path_string)

    try:
        return n + 1
    except Exception:
        return 0
Example #52
0
def queue_page_archive_actions(page):
    '''
    Pushes to the publishing queue all the page archives for a given page object.

    :param page:
        The page object whose archives will be pushed to the publishing queue.
    '''

    #===========================================================================
    # NOTE: I tried to speed this up by checking the list of fileinfos
    # related to mappings for the page (if any), and then pushing those
    # if they exist, but I haven't seen evidence it does anything tangible
    # for performance.
    # I need to double-check that old mappings are in fact invalidated
    # when they are changed.
    #===========================================================================

    archive_templates = page.blog.archive_templates
    tags = template_tags(page=page)

    for n in archive_templates:
        try:
            if n.publishing_mode != publishing_mode.do_not_publish:
                fileinfo_mappings = FileInfo.select().where(
                    FileInfo.page == page,
                    FileInfo.template_mapping << n.mappings)
                if fileinfo_mappings.count() == 0:
                    fileinfo_mappings = build_archives_fileinfos_by_mappings(
                        n, (page, ))
                if len(fileinfo_mappings) == 0:
                    logger.info(
                        'No archive fileinfos could be built for page {} with template {}'
                        .format(page.for_log, n.for_log))
                else:
                    for fileinfo_mapping in fileinfo_mappings:
                        Queue.push(job_type=job_type.archive,
                                   blog=page.blog,
                                   site=page.blog.site,
                                   priority=7,
                                   data_integer=fileinfo_mapping.id)
        except Exception as e:
            from core.error import QueueAddError
            raise QueueAddError(
                'Archive template {} for page {} could not be queued: '.format(
                    n, page.for_log, e))
Example #53
0
def queue_page_archive_actions(page):
    '''
    Pushes to the publishing queue all the page archives for a given page object.

    :param page:
        The page object whose archives will be pushed to the publishing queue.
    '''

    #===========================================================================
    # NOTE: I tried to speed this up by checking the list of fileinfos
    # related to mappings for the page (if any), and then pushing those
    # if they exist, but I haven't seen evidence it does anything tangible
    # for performance.
    # I need to double-check that old mappings are in fact invalidated
    # when they are changed.
    #===========================================================================

    archive_templates = page.blog.archive_templates
    tags = template_tags(page=page)

    for n in archive_templates:
        try:
            if n.publishing_mode != publishing_mode.do_not_publish:
                fileinfo_mappings = FileInfo.select().where(FileInfo.page == page,
                                                FileInfo.template_mapping << n.mappings)
                if fileinfo_mappings.count() == 0:
                    fileinfo_mappings=build_archives_fileinfos_by_mappings(n,(page,))
                if len(fileinfo_mappings)==0:
                    logger.info('No archive fileinfos could be built for page {} with template {}'.format(
                        page.for_log,
                        n.for_log))
                else:
                    for fileinfo_mapping in fileinfo_mappings:
                        Queue.push(job_type=job_type.archive,
                                blog=page.blog,
                                site=page.blog.site,
                                priority=7,
                                data_integer=fileinfo_mapping.id)
        except Exception as e:
            from core.error import QueueAddError
            raise QueueAddError('Archive template {} for page {} could not be queued: '.format(
                n,
                page.for_log,
                e))
Example #54
0
def generate_page_text(f, tags):
    '''
    Generates the text for a given page based on its fileinfo
    and a given tagset.

    :param f:
        The fileinfo object to use.
    :param tags:
        The tagset to use.
    '''

    tp = f.template_mapping.template

    try:
        tpx = Cache.template_cache[f.template_mapping.template.id]

    except KeyError:
        try:
            pre_tags = Cache.blog_tag_cache[tp.blog.id]
        except KeyError:
            pre_tags = template_tags(blog=tp.blog)

        Cache.blog_tag_cache[tp.blog.id] = pre_tags

        tpx = MetalTemplate(source=tp.body,
            tags=pre_tags.__dict__)
        Cache.template_cache[f.template_mapping.template.id] = tpx

    try:
        return tpx.render(**tags.__dict__)

    except Exception:
        import traceback, sys
        tb = sys.exc_info()[2]
        line_number = traceback.extract_tb(tb)[-1][1] - 1

        raise PageTemplateError("Error in template '{}': {} ({}) at line {}".format(
            tp.for_log,
            sys.exc_info()[0],
            sys.exc_info()[1],
            line_number
            ))
Example #55
0
def blog_create_user(blog_id):
    '''
    Creates a user and gives it certain permissions within the context of a given blog
    '''

    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_admin(user, blog)
    tags = template_tags(blog_id=blog.id,
        user=user)

    edit_user = Struct()
    edit_user.name = ""
    edit_user.email = ""

    return template('edit/user_settings',
        section_title="Create new blog user",
        # search_context=(search_context['sites'], None),
        edit_user=edit_user,
        **tags.__dict__
        )
Example #56
0
def blog_break_queue(blog_id):
    user = auth.is_logged_in(request)
    blog = Blog.load(blog_id)
    permission = auth.is_blog_publisher(user, blog)

    Queue.stop(blog)

    tags = template_tags(blog=blog,
            user=user)

    return template('queue/queue_run_ui',
        start=None,
        action_url='',
        start_message='''
<p>Queue publishing stopped. Note that queued items are still in the queue,
and may still be processed on the next queue run.</p>
<p><a href="{}/blog/{}/queue/clear"><button class="btn">Clear the queue</button></a> to remove them entirely.</p>
'''.format(BASE_URL, blog_id),
        title='Publishing queue progress',
        # search_context=(search_context['blog_queue'], blog),
        menu=generate_menu('blog_queue', blog),
        **tags.__dict__)
Example #57
0
def system_info():

    # alt. implementation, less boilerplate?
    # user, permission = auth.is_sys_admin()

    user = auth.is_logged_in(request)
    permission = auth.is_sys_admin(user)

    tags = template_tags(
        user=user)

    python_list = []
    environ_list = []
    settings_list = []

    # Generate interpreter info
    import os
    data = os.environ.__dict__['_data']
    for n in data:
        environ_list.append((n, data[n]))

    # List all settings variables
    import settings
    s_dict = settings.__dict__
    for n in s_dict:
        if n is not '__builtins__':
            settings_list.append((n, s_dict[n]))

    # List all plugins

    tpl = template('ui/ui_system_info',
        menu=generate_menu('system_info', None),
        search_context=(search_contexts['sites'], None),
        environ_list=sorted(environ_list),
        settings_list=sorted(settings_list),
        **tags.__dict__)

    return tpl