Exemple #1
0
def comic_admin_folders_do(request):
    folder = session.query(ComicChapter).get(request.POST['folder_id'])
    action = request.POST['action']
    # Prevent accidents
    return HTTPSeeOther(
        location=request.route_url('comic.admin') + '#manage-folders')

    # TODO this should verify, somehow, that there's actually something to the left or right to move to
    # TODO ha ha this doesn't work if the chosen folder has children, dummy!
    if direction == "left":
        # Move the folder leftwards, so that its new "right" is one less than its current "left"
        diff = (folder.left - 1) - folder.right
    elif direction == "right":
        # Move the folder rightwards, so that its new "left" is one more than its current "right"
        diff = (folder.right + 1) - folder.left
    else:
        raise HTTPBadRequest

    # TODO this assumes one direction only...  or does it?
    (
        session.query(ComicChapter)
        .filter(ComicChapter.left.between(
            *sorted((folder.left, folder.left + diff))))
        .update({
            ComicChapter.left: ComicChapter.left - diff,
            ComicChapter.right: ComicChapter.right - diff,
        }, synchronize_session=False)
    )

    folder.left += diff
    folder.right += diff

    return HTTPSeeOther(
        location=request.route_url('comic.admin') + '#manage-folders')
Exemple #2
0
def comic_admin_folders_do(request):
    for folder_id, direction in request.POST.items():
        folder = session.query(ComicChapter).get(folder_id)

        # TODO this should verify, somehow, that there's actually something to the left or right to move to
        if direction == "left":
            # Move the folder leftwards, so that its new "right" is one less than its current "left"
            diff = (folder.left - 1) - folder.right
        elif direction == "right":
            # Move the folder rightwards, so that its new "left" is one more than its current "right"
            diff = (folder.right + 1) - folder.left
        else:
            continue

        # TODO this assumes one direction only...  or does it?
        (
            session.query(ComicChapter)
            .filter(ComicChapter.left.between(*sorted((folder.left, folder.left + diff))))
            .update(
                {ComicChapter.left: ComicChapter.left - diff, ComicChapter.right: ComicChapter.right - diff},
                synchronize_session=False,
            )
        )

        folder.left += diff
        folder.right += diff
        session.flush()

    return HTTPSeeOther(location=request.route_url("comic.admin") + "#manage-folders")
Exemple #3
0
def comic_admin_folders_do(request):
    folder = session.query(ComicChapter).get(request.POST['folder_id'])
    action = request.POST['action']
    # Prevent accidents
    return HTTPSeeOther(location=request.route_url('comic.admin') +
                        '#manage-folders')

    # TODO this should verify, somehow, that there's actually something to the left or right to move to
    # TODO ha ha this doesn't work if the chosen folder has children, dummy!
    if direction == "left":
        # Move the folder leftwards, so that its new "right" is one less than its current "left"
        diff = (folder.left - 1) - folder.right
    elif direction == "right":
        # Move the folder rightwards, so that its new "left" is one more than its current "right"
        diff = (folder.right + 1) - folder.left
    else:
        raise HTTPBadRequest

    # TODO this assumes one direction only...  or does it?
    (session.query(ComicChapter).filter(
        ComicChapter.left.between(
            *sorted((folder.left, folder.left + diff)))).update(
                {
                    ComicChapter.left: ComicChapter.left - diff,
                    ComicChapter.right: ComicChapter.right - diff,
                },
                synchronize_session=False))

    folder.left += diff
    folder.right += diff

    return HTTPSeeOther(location=request.route_url('comic.admin') +
                        '#manage-folders')
Exemple #4
0
def permissions_revoke(request):
    # TODO error checking, eh.  is there even a flash thing yet, haha
    data = dict(
        group_id=request.POST['group'],
        scope=request.POST['scope'],
        permission=request.POST['priv'],
    )

    session.query(GroupPermission).filter_by(**data).delete()

    return HTTPSeeOther(location=request.route_url('__core__.admin.permissions'))
Exemple #5
0
def permissions_revoke(request):
    # TODO error checking, eh.  is there even a flash thing yet, haha
    data = dict(
        group_id=request.POST['group'],
        scope=request.POST['scope'],
        permission=request.POST['priv'],
    )

    session.query(GroupPermission).filter_by(**data).delete()

    return HTTPSeeOther(
        location=request.route_url('__core__.admin.permissions'))
Exemple #6
0
def comic_admin(request):
    # Figure out the starting date for the calendar.  We want to show the
    # previous four weeks, and start at the beginning of the week.
    today = get_current_publication_date(XXX_HARDCODED_TIMEZONE)
    weekday_offset = (today.weekday() - 6) % -7
    start = today + timedelta(days=weekday_offset - 7 * 4)

    # Grab "recent" pages -- any posted in the past two weeks OR in the future.
    recent_pages = (
        session.query(ComicPage)
        .join(ComicPage.chapter)
        .filter(ComicPage.date_published >= start.astimezone(pytz.utc))
        .order_by(ComicPage.date_published.desc())
        .all()
    )

    last_queued, queue_next_date = _get_last_queued_date()
    num_queued = sum(1 for page in recent_pages if page.is_queued)

    day_to_page = {page.date_published.date(): page for page in recent_pages}

    chapters = (
        session.query(ComicChapter)
        .order_by(ComicChapter.order.asc())
        .options(
            joinedload('children')
        )
        .all()
    )

    # Express calendar in dates.  Go at least four weeks into the future, OR
    # one week beyond the last queued comic (for some padding).
    calendar_start = start.date()
    calendar_start -= timedelta(days=calendar_start.isoweekday() % 7)
    calendar_end = today.date() + timedelta(days=7 * 4)
    if day_to_page:
        calendar_end = max(calendar_end, max(day_to_page) + timedelta(days=7))

    # TODO really really really need to move configuration out of comics.  this
    # was clever but not clever enough.
    comic = session.query(Comic).order_by(Comic.id.asc()).first()

    return dict(
        comic=comic,
        chapters=chapters,
        num_queued=num_queued,
        last_queued=last_queued,
        queue_next_date=queue_next_date,

        day_to_page=day_to_page,
        calendar_start=calendar_start,
        calendar_end=calendar_end,
    )
Exemple #7
0
def comic_admin_folders_new_do(request):
    # TODO error checking...  might be no POSTs, for example
    folder = session.query(GalleryFolder).get(request.POST['relativeto'])
    if not folder:
        # TODO
        raise HTTPBadRequest

    where = request.POST['where']
    if where == 'before':
        # "Before" really means taking its place, so the target folder and
        # every subsequent folder should scoot forwards two places
        left = folder.left
    elif where == 'after':
        # New folder's left should immediately follow the target folder's right
        left = folder.right + 1
    elif where == 'child':
        # New folder should go where its parents' current right is -- in case
        # there are already children, this puts the new folder last
        left = folder.right
    else:
        # TODO
        raise HTTPBadRequest

    # Shift any endpoints after the new left ahead by 2.  This will cover both
    # ancestors and unrelated folders that are further along
    (
        session.query(GalleryFolder)
        .filter(GalleryFolder.left >= left)
        .update({
            GalleryFolder.left: GalleryFolder.left + 2,
        }, synchronize_session=False)
    )
    (
        session.query(GalleryFolder)
        .filter(GalleryFolder.right >= left)
        .update({
            GalleryFolder.right: GalleryFolder.right + 2,
        }, synchronize_session=False)
    )

    # Create the new folder
    # TODO title has to be unique, non-blank, or somethin
    session.add(GalleryFolder(
        title=request.POST['title'],
        left=left,
        right=left + 1,
        # TODO temporary hack until i get rid of comics entirely
        comic_id=folder.comic_id,
    ))

    return HTTPSeeOther(
        location=request.route_url('comic.admin') + '#manage-folders')
Exemple #8
0
def permissions(request):
    perms = session.query(GroupPermission).all()
    groups = session.query(Group).all()

    all_privileges = []
    for scope, priv_names in sorted(ALL_KNOWN_PERMISSIONS_XXX.items()):
        for priv_name in priv_names:
            all_privileges.append((scope, priv_name))

    return dict(
        permissions=perms,
        groups=groups,
        all_privileges=all_privileges,
    )
Exemple #9
0
def permissions(request):
    perms = session.query(GroupPermission).all()
    groups = session.query(Group).all()

    all_privileges = []
    for scope, priv_names in sorted(ALL_KNOWN_PERMISSIONS_XXX.items()):
        for priv_name in priv_names:
            all_privileges.append((scope, priv_name))

    return dict(
        permissions=perms,
        groups=groups,
        all_privileges=all_privileges,
    )
Exemple #10
0
def comic_admin_folders_new_do(request):
    # TODO error checking...  might be no POSTs, for example
    folder = session.query(GalleryFolder).get(request.POST['relativeto'])
    if not folder:
        # TODO
        raise HTTPBadRequest

    where = request.POST['where']
    if where == 'before':
        # "Before" really means taking its place, so the target folder and
        # every subsequent folder should scoot forwards two places
        left = folder.left
    elif where == 'after':
        # New folder's left should immediately follow the target folder's right
        left = folder.right + 1
    elif where == 'child':
        # New folder should go where its parents' current right is -- in case
        # there are already children, this puts the new folder last
        left = folder.right
    else:
        # TODO
        raise HTTPBadRequest

    # Shift any endpoints after the new left ahead by 2.  This will cover both
    # ancestors and unrelated folders that are further along
    (session.query(GalleryFolder).filter(GalleryFolder.left >= left).update(
        {
            GalleryFolder.left: GalleryFolder.left + 2,
        },
        synchronize_session=False))
    (session.query(GalleryFolder).filter(GalleryFolder.right >= left).update(
        {
            GalleryFolder.right: GalleryFolder.right + 2,
        },
        synchronize_session=False))

    # Create the new folder
    # TODO title has to be unique, non-blank, or somethin
    session.add(
        GalleryFolder(
            title=request.POST['title'],
            left=left,
            right=left + 1,
            # TODO temporary hack until i get rid of comics entirely
            comic_id=folder.comic_id,
        ))

    return HTTPSeeOther(location=request.route_url('comic.admin') +
                        '#manage-folders')
Exemple #11
0
def comic_admin_folders_new_do(request):
    # TODO error checking...  might be no POSTs, for example
    folder = session.query(GalleryFolder).get(request.POST['relativeto'])
    if not folder:
        # TODO
        raise HTTPBadRequest

    where = request.POST['where']
    if where == 'before':
        # "Before" really means taking its place, so the target folder and
        # every subsequent folder should scoot forwards two places
        left = folder.left
    elif where == 'after':
        # New folder's left should immediately follow the target folder's right
        left = folder.right + 1
    elif where == 'child':
        # Target folder needs to widen by 2, then the new folder should go just
        # before its right -- in case there are already children, this puts the
        # new folder last
        folder.right += 2
        session.flush()
        left = folder.right - 2
    else:
        # TODO
        raise HTTPBadRequest

    # Push everyone else forwards by 2
    (
        session.query(GalleryFolder)
        .filter(GalleryFolder.left >= left)
        .update({
            GalleryFolder.left: GalleryFolder.left + 2,
            GalleryFolder.right: GalleryFolder.right + 2,
        }, synchronize_session=False)
    )

    # Create the new folder
    # TODO title has to be unique, non-blank, or somethin
    session.add(GalleryFolder(
        title=request.POST['title'],
        left=left,
        right=left + 1,
        # TODO temporary hack until i get rid of comics entirely
        comic_id=folder.comic_id,
    ))

    return HTTPSeeOther(
        location=request.route_url('comic.admin') + '#manage-folders')
Exemple #12
0
def comic_archive_by_date(request):
    # XXX remove this; currently used by _base.mako
    comic = session.query(Comic).order_by(Comic.id.asc()).first()

    if request.has_permission('queue', comic):
        queued_clause = True
    else:
        queued_clause = ~GalleryItem.is_queued

    items = (session.query(GalleryItem).order_by(
        GalleryItem.date_published).filter(queued_clause).all())

    return dict(
        comic=comic,
        items=items,
    )
Exemple #13
0
def comic_queue_do(request):
    # TODO this would be easier with a real validator.
    weekdays = []
    for wd in request.POST.getall('weekday'):
        if wd in '0123456':
            weekdays.append(int(wd))

    queued = (
        session.query(ComicPage)
        .join(ComicPage.chapter)
        .filter(ComicPage.is_queued)
        .order_by(ComicPage.order.asc())
        .all()
    )

    new_dates = _generate_queue_dates(weekdays, start=get_current_publication_date(XXX_HARDCODED_TIMEZONE))
    for page, new_date in zip(queued, new_dates):
        page.date_published = datetime.combine(
            new_date, time()).replace(tzinfo=pytz.utc)
        page.timezone = XXX_HARDCODED_TIMEZONE.zone

    comic.config_queue = ''.join(str(wd) for wd in sorted(weekdays))

    # TODO flash message?
    return HTTPSeeOther(location=request.route_url('comic.admin', comic))
Exemple #14
0
def comic_most_recent(request):
    page = (
        session.query(ComicPage)
        .join(ComicPage.chapter)
        # "Most recent" never includes the queue
        .filter(~ ComicPage.is_queued)
        .order_by(ComicPage.order.desc())
        .first()
    )

    comic = page.chapter.comic
    include_queued = request.has_permission('queue', comic)
    adjacent_pages = get_adjacent_pages(page, include_queued)

    # TODO this is duplicated below lol
    from spline_wiki.models import Wiki
    wiki = Wiki(request.registry.settings['spline.wiki.root'])
    transcript = wiki['!comic-pages'][str(page.id)]['en']

    ns = dict(
        comic=comic,
        page=page,
        transcript=transcript,
        adjacent_pages=adjacent_pages,
    )

    # TODO sometime maybe the landing page will be a little more interesting
    # and this can go away
    if page:
        renderer = 'spline_comic:templates/page.mako'
    else:
        renderer = 'spline_comic:templates/comic-landing.mako'

    return render_to_response(renderer, ns, request=request)
Exemple #15
0
def login__do(request):
    from pyramid.httpexceptions import HTTPForbidden, HTTPSeeOther

    # TODO don't allow re-login (replay attack where someone hits back+f5,
    # solved by switching to a new session on login/logout and invalidating the
    # old one)
    # TODO haha this still doesn't actually ask for a password.

    # TODO key errors...
    username = request.POST['username']

    try:
        user = session.query(User).filter_by(name=username).one()
    except NoResultFound:
        raise HTTPForbidden(detail="you don't have an account chief")

    given_pw = request.POST['password'].encode('utf8')
    actual_pw = user.password.encode('ascii')
    if actual_pw == bcrypt.hashpw(given_pw, actual_pw):
        headers = remember(request, user)
        # TODO return to same url?
        return HTTPSeeOther(request.route_url('__core__.home'),
                            headers=headers)
    else:
        raise HTTPForbidden
Exemple #16
0
def comic_most_recent(request):
    page = (
        session.query(ComicPage).join(ComicPage.chapter)
        # "Most recent" never includes the queue
        .filter(~ComicPage.is_queued).order_by(ComicPage.order.desc()).first())

    comic = page.chapter.comic
    include_queued = request.has_permission('queue', comic)
    adjacent_pages = get_adjacent_pages(page, include_queued)

    # TODO this is duplicated below lol
    from spline_wiki.models import Wiki
    wiki = Wiki(request.registry.settings['spline.wiki.root'])
    transcript = wiki['!comic-pages'][str(page.id)]['en']

    ns = dict(
        comic=comic,
        page=page,
        transcript=transcript,
        adjacent_pages=adjacent_pages,
    )

    # TODO sometime maybe the landing page will be a little more interesting
    # and this can go away
    if page:
        renderer = 'spline_comic:templates/page.mako'
    else:
        renderer = 'spline_comic:templates/comic-landing.mako'

    return render_to_response(renderer, ns, request=request)
Exemple #17
0
def offer_blocks(event):
    last_post = session.query(BlogPost).order_by(BlogPost.timestamp.desc()).first()

    block = FrontPageBlock(
        renderer='spline_blog:templates/_lib#front_page_block.mako',
        last_post=last_post,
    )
    event.blocks.append(block)
Exemple #18
0
def view(request):
    try:
        post = session.query(BlogPost).filter_by(
            id=request.matchdict['post_id']).one()
    except NoResultFound:
        return HTTPNotFound

    return dict(post=post, )
Exemple #19
0
def view(request):
    try:
        post = session.query(BlogPost).filter_by(id=request.matchdict['post_id']).one()
    except NoResultFound:
        return HTTPNotFound

    return dict(
        post=post,
    )
Exemple #20
0
def quote_view(request):
    quote = session.query(Quote).get(request.matchdict['id'])

    if not quote:
        return HTTPNotFound()

    return dict(
        quote=quote,
    )
Exemple #21
0
def page_route_factory(request):
    page_id = int(request.matchdict['page_id'])
    try:
        page = (session.query(ComicPage).filter_by(id=page_id).options(
            joinedload('folder').joinedload('ancestors')).one())
    except NoResultFound:
        raise HTTPNotFound
    else:
        canonicalize_resource_url(request, page)
        return page
Exemple #22
0
def get_recent_pages():
    # TODO needs date filter for features
    return (
        session.query(ComicPage)
        .filter(~ ComicPage.is_queued)
        .order_by(ComicPage.order.desc())
        .options(
            eagerload_all(ComicPage.chapter, ComicChapter.comic)
        )
    )
Exemple #23
0
def get_recent_pages():
    # TODO needs date filter for features
    return (
        session.query(ComicPage)
        .filter(~ ComicPage.is_queued)
        .order_by(ComicPage.order.desc())
        .options(
            eagerload_all(ComicPage.chapter, ComicChapter.comic)
        )
    )
Exemple #24
0
 def activity_from_database(self, table, render_function,
         timestamp_accessor=operator.attrgetter('timestamp')):
     """Fetch rows from the database and wrap them in `ActivitySource`s."""
     # TODO optimize the heck outta this like spline.frontpage
     q = (
         session.query(table)
         .order_by(timestamp_accessor(table).desc())
         .limit(self.max_count)
     )
     self.add_activity(q, render_function, timestamp_accessor)
Exemple #25
0
 def activity_from_database(
     self,
     table,
     render_function,
     timestamp_accessor=operator.attrgetter('timestamp')):
     """Fetch rows from the database and wrap them in `ActivitySource`s."""
     # TODO optimize the heck outta this like spline.frontpage
     q = (session.query(table).order_by(
         timestamp_accessor(table).desc()).limit(self.max_count))
     self.add_activity(q, render_function, timestamp_accessor)
Exemple #26
0
def folder_route_factory(request):
    path_parts = request.matchdict['folder_path'].split('/')
    try:
        folder = (session.query(ComicChapter).filter_by(
            title_slug=path_parts[-1]).options(joinedload('ancestors')).one())
    except NoResultFound:
        raise HTTPNotFound
    else:
        canonicalize_resource_url(request, folder)
        return folder
Exemple #27
0
def comic_admin(request):
    # Figure out the starting date for the calendar.  We want to show the
    # previous four weeks, and start at the beginning of the week.
    today = get_current_publication_date(XXX_HARDCODED_TIMEZONE)
    weekday_offset = (today.weekday() - 6) % -7
    start = today + timedelta(days=weekday_offset - 7 * 4)

    # Grab "recent" pages -- any posted in the past two weeks OR in the future.
    recent_pages = (session.query(ComicPage).join(ComicPage.chapter).filter(
        ComicPage.date_published >= start.astimezone(pytz.utc)).order_by(
            ComicPage.date_published.desc()).all())

    last_queued, queue_next_date = _get_last_queued_date()
    num_queued = sum(1 for page in recent_pages if page.is_queued)

    day_to_page = {page.date_published.date(): page for page in recent_pages}

    chapters = (session.query(ComicChapter).order_by(
        ComicChapter.order.asc()).options(joinedload('children')).all())

    # Express calendar in dates.  Go at least four weeks into the future, OR
    # one week beyond the last queued comic (for some padding).
    calendar_start = start.date()
    calendar_start -= timedelta(days=calendar_start.isoweekday() % 7)
    calendar_end = today.date() + timedelta(days=7 * 4)
    if day_to_page:
        calendar_end = max(calendar_end, max(day_to_page) + timedelta(days=7))

    # TODO really really really need to move configuration out of comics.  this
    # was clever but not clever enough.
    comic = session.query(Comic).order_by(Comic.id.asc()).first()

    return dict(
        comic=comic,
        chapters=chapters,
        num_queued=num_queued,
        last_queued=last_queued,
        queue_next_date=queue_next_date,
        day_to_page=day_to_page,
        calendar_start=calendar_start,
        calendar_end=calendar_end,
    )
Exemple #28
0
def build_menu(event):
    # TODO can these be...  cached?  but then how would it be busted.
    # TODO order?
    # TODO this is pretty piss-poor now that "comic" has been kind of
    # overloaded to mean not really that
    for comic in session.query(Comic):
        event.add_item(
            "{} comic".format(comic.title),
            'comic.most-recent',
            comic,
        )
Exemple #29
0
def find_activity(event):
    # TODO proooobably need some kinda helpful shared  date and count limit here
    # TODO start porting to logic?
    pastes = session.query(Paste).order_by(Paste.timestamp.desc()).limit(8).all()

    # TODO is this a good idea
    if pastes:
        event.blocks.append(FrontPageBlock(
            renderer='spline_pastebin:templates/_lib#front_page_block.mako',
            pastes=pastes,
        ))
Exemple #30
0
def comic_archive_by_date(request):
    # XXX remove this; currently used by _base.mako
    comic = session.query(Comic).order_by(Comic.id.asc()).first()

    if request.has_permission('queue', comic):
        queued_clause = True
    else:
        queued_clause = ~GalleryItem.is_queued

    items = (
        session.query(GalleryItem)
        .order_by(GalleryItem.date_published)
        .filter(queued_clause)
        .all()
    )

    return dict(
        comic=comic,
        items=items,
    )
Exemple #31
0
def build_menu(event):
    # TODO can these be...  cached?  but then how would it be busted.
    # TODO order?
    # TODO this is pretty piss-poor now that "comic" has been kind of
    # overloaded to mean not really that
    for comic in session.query(Comic):
        event.add_item(
            "{} comic".format(comic.title),
            'comic.most-recent',
            comic,
        )
Exemple #32
0
def folder_route_factory(request):
    path_parts = request.matchdict['folder_path'].split('/')
    try:
        folder = (
            session.query(ComicChapter)
            .filter_by(title_slug=path_parts[-1])
            .options(joinedload('ancestors'))
            .one()
        )
    except NoResultFound:
        raise HTTPNotFound
    else:
        canonicalize_resource_url(request, folder)
        return folder
Exemple #33
0
def express_love__do(request):
    # TODO real form handling thx

    source = request.user
    # TODO error handling lol
    target = session.query(User).filter_by(name=request.POST['target']).one()

    session.add(Love(
        source=source,
        target=target,
        comment=request.POST['comment'],
    ))

    return HTTPSeeOther(request.route_url('love.list'))
Exemple #34
0
def get_adjacent_pages(page, include_queued):
    if not page:
        return None, None

    q = session.query(ComicPage).join(ComicPage.chapter)
    if not include_queued:
        q = q.filter(~ ComicPage.is_queued)

    prev_by_date = (
        q
        .filter(ComicPage.date_published < page.date_published)
        .order_by(ComicPage.date_published.desc())
        .first()
    )
    next_by_date = (
        q
        .filter(ComicPage.date_published > page.date_published)
        .order_by(ComicPage.date_published.asc())
        .first()
    )

    # So, "by story" is a little more complicated.  What it really means is:
    # 1. If there's a prev/next page in this folder, use that.
    # 2. Otherwise, if there's a prev/next folder, use the
    # last/first page of it.
    # Folders are sorted by their /right/ edge so that a page in a folder comes
    # after every page in any subfolders, just like the archive view shows.
    prev_by_story = (
        q
        .filter(ComicChapter.comic_id == page.chapter.comic_id)
        .filter(or_(
            ComicChapter.right < page.chapter.right,
            and_(ComicChapter.id == page.chapter.id, ComicPage.order < page.order),
        ))
        .order_by(ComicChapter.right.desc(), ComicPage.order.desc())
        .first()
    )
    next_by_story = (
        q
        .filter(ComicChapter.comic_id == page.chapter.comic_id)
        .filter(or_(
            ComicChapter.right > page.chapter.right,
            and_(ComicChapter.id == page.chapter.id, ComicPage.order > page.order),
        ))
        .order_by(ComicChapter.right.asc(), ComicPage.order.asc())
        .first()
    )

    return PreviousNextPager(
        prev_by_date, next_by_date, prev_by_story, next_by_story)
Exemple #35
0
def page_route_factory(request):
    page_id = int(request.matchdict['page_id'])
    try:
        page = (
            session.query(ComicPage)
            .filter_by(id=page_id)
            .options(joinedload('folder').joinedload('ancestors'))
            .one()
        )
    except NoResultFound:
        raise HTTPNotFound
    else:
        canonicalize_resource_url(request, page)
        return page
Exemple #36
0
def permissions_grant(request):
    # TODO error checking, eh.  is there even a flash thing yet, haha
    data = dict(
        group_id=request.POST['group'],
        scope=request.POST['scope'],
        permission=request.POST['priv'],
    )

    existing = session.query(GroupPermission).filter_by(**data).all()

    if not existing:
        session.add(GroupPermission(**data))

    return HTTPSeeOther(location=request.route_url('__core__.admin.permissions'))
Exemple #37
0
def get_adjacent_pages(page, include_queued):
    if not page:
        return None, None

    q = session.query(ComicPage).join(ComicPage.chapter)
    if not include_queued:
        q = q.filter(~ ComicPage.is_queued)

    # TODO humble suggestion: perhaps ordering by date should order by, you know...  date?
    prev_by_date = (
        q
        .filter(ComicPage.order < page.order)
        .order_by(ComicPage.order.desc())
        .first()
    )
    next_by_date = (
        q
        .filter(ComicPage.order > page.order)
        .order_by(ComicPage.order.asc())
        .first()
    )

    # So, "by story" is a little more complicated.  What it really means is:
    # 1. If there's a prev/next page in this folder, use that.
    # 2. Otherwise, if there's a prev/next folder, use the
    # last/first page of it.
    prev_by_story = (
        q
        .filter(ComicChapter.comic_id == page.chapter.comic_id)
        .filter(or_(
            ComicChapter.order < page.chapter.order,
            and_(ComicChapter.id == page.chapter.id, ComicPage.order < page.order),
        ))
        .order_by(ComicChapter.order.desc(), ComicPage.order.desc())
        .first()
    )
    next_by_story = (
        q
        .filter(ComicChapter.comic_id == page.chapter.comic_id)
        .filter(or_(
            ComicChapter.right > page.chapter.right,
            and_(ComicChapter.id == page.chapter.id, ComicPage.order > page.order),
        ))
        .order_by(ComicChapter.order.asc(), ComicPage.order.asc())
        .first()
    )

    return PreviousNextPager(
        prev_by_date, next_by_date, prev_by_story, next_by_story)
Exemple #38
0
def express_love__do(request):
    # TODO real form handling thx

    source = request.user
    # TODO error handling lol
    target = session.query(User).filter_by(name=request.POST['target']).one()

    session.add(
        Love(
            source=source,
            target=target,
            comment=request.POST['comment'],
        ))

    return HTTPSeeOther(request.route_url('love.list'))
Exemple #39
0
def permissions_grant(request):
    # TODO error checking, eh.  is there even a flash thing yet, haha
    data = dict(
        group_id=request.POST['group'],
        scope=request.POST['scope'],
        permission=request.POST['priv'],
    )

    existing = session.query(GroupPermission).filter_by(**data).all()

    if not existing:
        session.add(GroupPermission(**data))

    return HTTPSeeOther(
        location=request.route_url('__core__.admin.permissions'))
Exemple #40
0
def view(request):
    paste = session.query(Paste) \
        .filter(Paste.id == request.matchdict['id']) \
        .one()

    if paste.syntax != '':
        lexer = pygments.lexers.get_lexer_by_name(paste.syntax)
    else:
        lexer = pygments.lexers.TextLexer()
    pretty_content = pygments.highlight(paste.content, lexer, pygments.formatters.HtmlFormatter())

    return dict(
        paste=paste,
        pretty_content=pretty_content,
    )
Exemple #41
0
def wiki_history(page, request):
    if not page.exists:
        # TODO not sure what should be done here really
        raise HTTPNotFound

    history = page.get_history()

    if history.all_emails:
        q = (session.query(User.email,
                           User).filter(User.email.in_(history.all_emails)))
        history.native_email_map = dict(q)

    return dict(
        page=page,
        history=history,
    )
Exemple #42
0
def wiki_history(page, request):
    if not page.exists:
        # TODO not sure what should be done here really
        raise HTTPNotFound

    history = page.get_history()

    if history.all_emails:
        q = (session.query(User.email, User)
            .filter(User.email.in_(history.all_emails))
        )
        history.native_email_map = dict(q)

    return dict(
        page=page,
        history=history,
    )
Exemple #43
0
def _get_last_queued_date():
    queued_q = session.query(ComicPage).join(ComicPage.chapter).filter(ComicPage.is_queued)

    last_queued = queued_q.order_by(ComicPage.date_published.desc()).first()

    if last_queued:
        queue_end_date = last_queued.date_published
    else:
        queue_end_date = datetime.combine(
            get_current_publication_date(XXX_HARDCODED_TIMEZONE).astimezone(pytz.utc), time()
        )
    if queue_end_date == END_OF_TIME:
        queue_next_date = None
    else:
        weekdays = [int(wd) for wd in XXX_HARDCODED_QUEUE]
        queue_next_date = next(_generate_queue_dates(weekdays, start=queue_end_date))

    return last_queued, queue_next_date
Exemple #44
0
def create_user(parser, args):
    engine = engine_from_config(vars(args), 'sqlalchemy.')
    session.configure(bind=engine)

    with transaction.manager:
        all_groups = {
            group.name: group
            for group in session.query(Group)
        }

    username = input('username: '******'email: ').strip()
    password = getpass.getpass('password: '******'confirm password: '******'t match!")
        sys.exit(1)

    group_names = []
    if all_groups:
        print()
        print("available groups: {}".format(', '.join(all_groups)))
        group_names = input('comma-separated list of groups to add to: ').strip().split(',')

    with transaction.manager:
        pwhash = bcrypt.hashpw(
            password.encode('utf8'),
            bcrypt.gensalt(14),
        ).decode('ascii')

        # TODO would be neat to have a password field that hashes on assignment
        # and does the right thing with equality check
        user = User(name=username, email=email, password=pwhash, groups=[])
        for group_name in group_names:
            user.groups.append(all_groups[group_name])
        session.add(user)
        session.flush()
        userid = user.id

    print()
    print("created user {} with id {}".format(username, userid))
Exemple #45
0
def comic_queue_do(request):
    # TODO this would be easier with a real validator.
    weekdays = []
    for wd in request.POST.getall('weekday'):
        if wd in '0123456':
            weekdays.append(int(wd))

    queued = (session.query(ComicPage).join(ComicPage.chapter).filter(
        ComicPage.is_queued).order_by(ComicPage.order.asc()).all())

    new_dates = _generate_queue_dates(
        weekdays, start=get_current_publication_date(XXX_HARDCODED_TIMEZONE))
    for page, new_date in zip(queued, new_dates):
        page.date_published = datetime.combine(new_date,
                                               time()).replace(tzinfo=pytz.utc)
        page.timezone = XXX_HARDCODED_TIMEZONE.zone

    comic.config_queue = ''.join(str(wd) for wd in sorted(weekdays))

    # TODO flash message?
    return HTTPSeeOther(location=request.route_url('comic.admin', comic))
Exemple #46
0
    def root_factory(self, request):
        """Given a request with a matched URL, produce the object referred to
        by that URL.

        Raise a 404 if no such object exists.
        """
        table = self.column.class_
        query = session.query(table)

        relchain = ()
        current = self
        while current:
            for rel in relchain:
                query = query.join(rel)

            if current.marker in request.matchdict:
                ident = request.matchdict[current.marker]
                if current.slug_column:
                    # Split the "real" identifier from the slug
                    ident, _, _ = ident.partition(u'-')
                    # TODO redirect if any slug doesn't match the db
                    # TODO come to think of it...  contains_eager?

                query = query.filter(current.column == ident)
            elif current is self:
                # Missing the identifier for the most specific model, which...
                # don't make sense
                raise HTTPNotFound()

            relchain = current.relchain
            current = current.parent

        try:
            row = query.one()
        except NoResultFound:
            # Note that MultipleResultsFound isn't caught, because that should
            # not be *possible* and indicates a programming error
            raise HTTPNotFound()

        return row
Exemple #47
0
    def root_factory(self, request):
        """Given a request with a matched URL, produce the object referred to
        by that URL.

        Raise a 404 if no such object exists.
        """
        table = self.column.class_
        query = session.query(table)

        relchain = ()
        current = self
        while current:
            for rel in relchain:
                query = query.join(rel)

            if current.marker in request.matchdict:
                ident = request.matchdict[current.marker]
                if current.slug_column:
                    # Split the "real" identifier from the slug
                    ident, _, _ = ident.partition(u'-')
                    # TODO redirect if any slug doesn't match the db
                    # TODO come to think of it...  contains_eager?

                query = query.filter(current.column == ident)
            elif current is self:
                # Missing the identifier for the most specific model, which...
                # don't make sense
                raise HTTPNotFound()

            relchain = current.relchain
            current = current.parent

        try:
            row = query.one()
        except NoResultFound:
            # Note that MultipleResultsFound isn't caught, because that should
            # not be *possible* and indicates a programming error
            raise HTTPNotFound()

        return row
Exemple #48
0
def _get_last_queued_date():
    queued_q = (session.query(ComicPage).join(ComicPage.chapter).filter(
        ComicPage.is_queued))

    last_queued = queued_q.order_by(ComicPage.date_published.desc()).first()

    if last_queued:
        queue_end_date = last_queued.date_published
    else:
        queue_end_date = datetime.combine(
            get_current_publication_date(XXX_HARDCODED_TIMEZONE).astimezone(
                pytz.utc),
            time(),
        )
    if queue_end_date == END_OF_TIME:
        queue_next_date = None
    else:
        weekdays = [int(wd) for wd in XXX_HARDCODED_QUEUE]
        queue_next_date = next(
            _generate_queue_dates(weekdays, start=queue_end_date))

    return last_queued, queue_next_date
Exemple #49
0
def login__do(request):
    from pyramid.httpexceptions import HTTPForbidden, HTTPSeeOther

    # TODO don't allow re-login (replay attack where someone hits back+f5,
    # solved by switching to a new session on login/logout and invalidating the
    # old one)
    # TODO haha this still doesn't actually ask for a password.

    # TODO key errors...
    username = request.POST['username']

    try:
        user = session.query(User).filter_by(name=username).one()
    except NoResultFound:
        raise HTTPForbidden(detail="you don't have an account chief")

    given_pw = request.POST['password'].encode('utf8')
    actual_pw = user.password.encode('ascii')
    if actual_pw == bcrypt.hashpw(given_pw, actual_pw):
        headers = remember(request, user)
        # TODO return to same url?
        return HTTPSeeOther(request.route_url('__core__.home'), headers=headers)
    else:
        raise HTTPForbidden
Exemple #50
0
def list_love(request):
    loves = session.query(Love).order_by(Love.timestamp.desc())
    return dict(
        loves=loves,
    )
Exemple #51
0
def comic_upload_do(request):
    # TODO validation and all that boring stuff
    file_upload = request.POST['file']
    fh = file_upload.file

    from spline.feature.filestore import IStorage
    storage = request.registry.queryUtility(IStorage)

    _, ext = os.path.splitext(file_upload.filename)
    filename = storage.store(fh, ext)
    # TODO ha ha this is stupid
    # TODO very skinny images shouldn't be blindly made 200x200
    fh.seek(0)
    thumb = subprocess.check_output(
        ['convert', '-', '-resize', '200x200', '-'], stdin=fh)
    # TODO this interface is bad also
    thumbname = storage.store(BytesIO(thumb), ext)

    # TODO wire into transaction so the file gets deleted on rollback

    last_chapter = (session.query(ComicChapter).filter(
        ComicChapter.id == int(request.POST['chapter'])).one())

    when = request.POST['when']
    if when == 'now':
        date_published = datetime.now(pytz.utc)
    elif when == 'queue':
        last_queued, queue_next_date = _get_last_queued_date()
        date_published = datetime.combine(
            queue_next_date,
            time(tzinfo=XXX_HARDCODED_TIMEZONE),
        )
        date_published = date_published.astimezone(pytz.utc)

    # Fetch next page number and ordering.  Also need to shift everyone else
    # forwards by one if this is bumping the queue.  Blurgh.
    max_order, = (session.query(func.max(ComicPage.order)).filter(
        ComicPage.date_published <= date_published).first())
    if max_order is None:
        max_order = 0
    next_order = max_order + 1

    (session.query(ComicPage).filter(ComicPage.order >= next_order).update(
        {ComicPage.order: ComicPage.order + 1}))

    max_page_number, = (session.query(func.max(
        ComicPage.page_number)).with_parent(last_chapter).filter(
            ComicPage.date_published <= date_published).first())
    if max_page_number is None:
        max_page_number = 0
    next_page_number = max_page_number + 1

    (session.query(ComicPage).with_parent(last_chapter).filter(
        ComicPage.page_number >= next_page_number).update(
            {ComicPage.page_number: ComicPage.page_number + 1}))

    page = ComicPage(
        chapter=last_chapter,
        author=request.user,
        date_published=date_published,
        timezone=XXX_HARDCODED_TIMEZONE.zone,
        order=next_order,
        page_number=next_page_number,

        # TODO more validation here too
        title=request.POST['title'],
        comment=request.POST['comment'],
        media=[
            GalleryMedia_Image(
                image_file=os.path.basename(filename),
                thumbnail_file=os.path.basename(thumbname),
            )
        ],
    )

    if request.POST.get('iframe_url'):
        url = request.POST['iframe_url']
        # If it's a YouTube URL, convert to the embed URL automatically
        # TODO this seems like a neat thing to do for many other services and
        # make a tiny library out of, if it's not done already?
        # TODO why doesn't this use urlparse???
        m = re.match(
            '^(?:https?://)?(?:www[.])?youtube[.]com/watch[?]v=([-_0-9a-zA-Z]+)(?:&|$)',
            url)
        if m:
            url = "https://www.youtube.com/embed/{}?rel=0".format(m.group(1))

        m = re.match('^(?:https?://)?youtu[.]be/([-_0-9a-zA-Z]+)(?:[?]|$)',
                     url)
        if m:
            url = "https://www.youtube.com/embed/{}?rel=0".format(m.group(1))

        try:
            width = int(request.POST['iframe_width'])
        except (KeyError, ValueError):
            width = 800
        try:
            height = int(request.POST['iframe_height'])
        except (KeyError, ValueError):
            height = 600

        page.media.append(
            GalleryMedia_IFrame(url=url, width=width, height=height))

    session.add(page)
    session.flush()

    return HTTPSeeOther(location=request.resource_url(page))
Exemple #52
0
def authenticate_userid(userid, request):
    if userid is None:
        return None

    # TODO should this forget who you are if you don't exist?
    return session.query(User).get(userid)
Exemple #53
0
def quote_list(request):
    quotes = session.query(Quote).order_by(Quote.timestamp.desc())
    return dict(
        quotes=quotes,
    )
Exemple #54
0
 def authenticated_userid(self, request):
     userid = self.unauthenticated_userid(request)
     if userid:
         return session.query(User).get(userid)
     else:
         return None
Exemple #55
0
def init_db(parser, args):
    settings = vars(args)

    # Logging
    # TODO this should probably go in the main entry point
    coloredlogs.install(level=logging.INFO)
    # TODO what is this for, it was debug-only, is there any reason we wouldn't want it
    #logging.basicConfig()
    #logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)

    # Plugin loading
    plugin_list = import_plugins(settings.get('spline.plugins', ()))

    engine = engine_from_config(settings, 'sqlalchemy.')
    session.configure(bind=engine)

    Base.metadata.create_all(engine, checkfirst=True)

    comic_title = settings.get('spline.comic_name')
    chapter_title = settings.get('spline.chapter_name')

    with transaction.manager:
        try:
            g = session.query(Group).filter_by(id=1).one()
        except:
            g = Group(id=1, name='admin')
            session.add(g)
        try:
            gp0 = session.query(GroupPermission).filter_by(id=1).one()
        except:
            gp0 = GroupPermission(id=1, scope='core', permission='admin')
            gp0.group = g
            session.add(gp0)
        # Only needed if the comic plugin is loaded
        if 'spline_comic' in plugin_list:
            from spline_comic.models import Comic, ComicChapter
            try:
                gp1 = session.query(GroupPermission).filter_by(id=1).one()
            except:
                gp1 = GroupPermission(id=1, scope='comic', permission='admin')
                gp1.group = g
                session.add(gp1)
            try:
                comic = session.query(Comic).filter_by(id=1).one()
            except:
                comic = Comic(id=1, title=comic_title, config_timezone='GMT')
                session.add(comic)
            try:
                chap = session.query(ComicChapter).filter_by(id=1).one()
            except:
                chap = ComicChapter(id=1, title=chapter_title, left=0, right=0)
                chap.comic = comic
                session.add(chap)
        # Only needed if the wiki is loaded
        if 'spline_wiki' in plugin_list:
            try:
                gp2 = session.query(GroupPermission).filter_by(id=2).one()
            except:
                gp2 = GroupPermission(id=2, scope='wiki', permission='edit')
                gp2.group = g
                session.add(gp2)
Exemple #56
0
def list_love(request):
    loves = session.query(Love).order_by(Love.timestamp.desc())
    return dict(loves=loves, )