Skip to content
This repository has been archived by the owner on Apr 17, 2018. It is now read-only.

Commit

Permalink
Add the ability to submit links. Fixes #548
Browse files Browse the repository at this point in the history
  • Loading branch information
jglamine committed Aug 8, 2016
1 parent c71f349 commit 5dc2650
Show file tree
Hide file tree
Showing 15 changed files with 456 additions and 171 deletions.
82 changes: 58 additions & 24 deletions r2/r2/controllers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ def link_listing_by_url(url, count = None):
listing = LinkListing(builder).listing()
return listing

def strip_link_prefix(title):
"""Remove the prefix '[Link]' from the begining of a title.
LessWrong adds the prefix when rendering links, so there's no need to put
it in the title.
"""
if title is None:
return

title = title.strip()
if title.startswith('['):
for prefix in ['[Link]', '[ Link ]', '[link]', '[ link ]']:
if title.startswith(prefix):
return title[len(prefix):].strip()
return title


class ApiController(RedditController):
def response_func(self, **kw):
Expand Down Expand Up @@ -289,6 +305,8 @@ def POST_unbless(self, res, link):
VCaptcha(),
VRatelimit(rate_user = True, rate_ip = True, prefix='rate_submit_'),
VModhash(),
ValidDomain('url'),
url = VUrl(['url']),
ip = ValidIP(),
sr = VSubmitSR('sr'),
title = VTitle('title'),
Expand All @@ -298,30 +316,41 @@ def POST_unbless(self, res, link):
continue_editing = VBoolean('keep_editing'),
notify_on_comment = VBoolean('notify_on_comment'),
cc_licensed = VBoolean('cc_licensed'),
tags = VTags('tags'))
def POST_submit(self, res, l, new_content, title, save, continue_editing, sr, ip, tags, notify_on_comment, cc_licensed):
tags = VTags('tags'),
kind = VOneOf('kind', ['link', 'self']))
def POST_submit(self, res, l, new_content, title, url, save,
continue_editing, sr, ip, tags, notify_on_comment,
cc_licensed, kind):
res._update('status', innerHTML = '')

edit_mode = not not l

if res._chk_error(errors.SUBREDDIT_FORBIDDEN):
# although new posts to main are disabled, editing of previous posts is permitted
if sr == Subreddit._by_name(g.default_sr) and l.can_submit(c.user):
c.errors.remove(errors.SUBREDDIT_FORBIDDEN)
else:
sr = None

should_ratelimit = sr.should_ratelimit(c.user, 'link') if sr else True

#remove the ratelimit error if the user's karma is high
# remove the ratelimit error if the user's karma is high
should_ratelimit = sr.should_ratelimit(c.user, 'link') if sr else True
if not should_ratelimit:
c.errors.remove(errors.RATELIMIT)

#ratelimiter
if res._chk_error(errors.RATELIMIT):
pass
# check for title, otherwise look it up and return it
elif res._chk_error(errors.NO_TITLE):
# clear out this error
res._chk_error(errors.TITLE_TOO_LONG)
else:
res._chk_error(errors.RATELIMIT)

if kind == 'link':
if not edit_mode:
# validate url on link submissions
if res._chk_errors((errors.NO_URL, errors.BAD_URL)):
res._focus('url')
elif res._chk_error(errors.ALREADY_SUB):
link = url[0]
res._redirect(link.already_submitted_link)
# TODO: Validate article content, enforce max-length.

# check for title
if res._chk_error(errors.NO_TITLE):
res._chk_error(errors.TITLE_TOO_LONG) # clear out this error
res._focus('title')
elif res._chk_error(errors.TITLE_TOO_LONG):
res._focus('title')
Expand All @@ -330,8 +359,9 @@ def POST_submit(self, res, l, new_content, title, save, continue_editing, sr, ip
elif res._chk_error(errors.SUBREDDIT_FORBIDDEN):
pass


if res.error or not title: return
post_title = strip_link_prefix(request.post.title)
if res.error or not title or not post_title:
return

# check whether this is spam:
spam = (c.user._spam or
Expand All @@ -342,17 +372,19 @@ def POST_submit(self, res, l, new_content, title, save, continue_editing, sr, ip
new_content = ''

# well, nothing left to do but submit it
# TODO: include article body in arguments to Link model
# print "\n".join(request.post.va)
if not l:
l = Link._submit(request.post.title, new_content, c.user, sr, ip, tags, spam,
notify_on_comment=notify_on_comment, cc_licensed=cc_licensed)
if not edit_mode:
l = Link._submit(post_title, new_content,
url if kind == 'link' else 'self',
c.user, sr, ip, tags, spam,
notify_on_comment=notify_on_comment,
cc_licensed=cc_licensed
)
if save == 'on':
r = l._save(c.user)
if g.write_query_queue:
queries.new_savehide(r)

#set the ratelimiter
# set the ratelimiter
if should_ratelimit:
VRatelimit.ratelimit(rate_user=True, rate_ip = True, prefix='rate_submit_')

Expand All @@ -364,8 +396,9 @@ def POST_submit(self, res, l, new_content, title, save, continue_editing, sr, ip
if c.user._id != l.author_id:
edit = Edit._new(l,c.user,new_content)
old_url = l.url
l.title = request.post.title
l.set_article(new_content)
l.title = post_title
if kind == 'self':
l.set_article(new_content)
l.notify_on_comment = notify_on_comment
l.cc_licensed = cc_licensed
l.change_subreddit(sr._id)
Expand Down Expand Up @@ -1832,6 +1865,7 @@ def POST_edit_promo(self, res, ip,

res._redirect('/promote/edit_promo/%s' % to36(l._id))
else:
# TODO: This appears to be dead code. Investigate whether it can be removed
l = Link._submit(title, url, c.user, sr, ip, False)

if expire == 'expirein' and timelimitlength and timelimittype:
Expand Down
12 changes: 11 additions & 1 deletion r2/r2/controllers/front.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,11 +544,16 @@ def GET_submit(self, can_submit, url, title, tags):
except NotFound:
sr = None

tab = 'article'
if 'link' in request.get:
tab = 'link'

return FormPage(_("Submit Article"),
content=NewLink(title=title or '',
subreddits = srs,
tags=tags,
sr_id = sr._id if sr else None,
tab=tab,
captcha=captcha)).render()

@validate(VUser(),
Expand All @@ -568,7 +573,12 @@ def GET_editarticle(self, article):
captcha = Captcha(tabular=False) if c.user.needs_captcha() else None

return FormPage(_("Edit article"),
content=EditLink(article, subreddits=subreddits, tags=article.tag_names(), captcha=captcha)).render()
content=EditLink(article,
subreddits=subreddits,
tags=article.tag_names(),
captcha=captcha
)
).render()

def _render_opt_in_out(self, msg_hash, leave):
"""Generates the form for an optin/optout page"""
Expand Down
9 changes: 5 additions & 4 deletions r2/r2/lib/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ def __getattr__(self, attr):
prefs = _("Preferences"),
stats = _("Stats"),
submit = _("Create new article"),
submitlink = _("Submit a new link"),
meetupsnew = _("Add new meetup"),
help = _("Help"),
blog = _("Blog"),
Expand Down Expand Up @@ -263,12 +264,13 @@ class NavButton(Styled):
passed to a NavMenu instance upon its construction."""
def __init__(self, title, dest, sr_path = True,
nocname=False, opt = '', aliases = [],
target = "", style = "plain", **kw):
target = "", style = "plain", dest_params = {}, **kw):

# keep original dest to check against c.location when rendering
self.aliases = set(a.rstrip('/') for a in aliases)
self.aliases.add(dest.rstrip('/'))
self.dest = dest
self.dest_params = dest_params

Styled.__init__(self, style = style, sr_path = sr_path,
nocname = nocname, target = target,
Expand All @@ -289,6 +291,7 @@ def build(self, base_path = ''):
else:
p = {}
base_path = ("%s/%s/" % (base_path, self.dest)).replace('//', '/')
p.update(self.dest_params)

self.bare_path = _force_unicode(base_path.replace('//', '/')).lower()
self.bare_path = self.bare_path.rstrip('/')
Expand Down Expand Up @@ -362,7 +365,7 @@ def selected_title(self):
return NavButton.selected_title(self)

class ExpandableButton(NamedButton):
def __init__(self, name, sr_path = True, nocname=False, dest = None,
def __init__(self, name, sr_path = True, nocname=False, dest = None,
sub_reddit = "/", sub_menus=[], **kw):
self.sub = sub_menus
self.sub_reddit = sub_reddit
Expand Down Expand Up @@ -613,5 +616,3 @@ class AdminTimeMenu(TimeMenu):
get_param = 't'
default = 'day'
options = ('hour', 'day', 'week')


65 changes: 62 additions & 3 deletions r2/r2/lib/pages/pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,10 @@ def corner_buttons(self):

buttons += [NamedButton('submit', sr_path = not c.default_sr,
nocname=not c.authorized_cname)]
buttons += [NamedButton('submitlink', dest = 'submit',
dest_params = { 'link': True },
sr_path = not c.default_sr,
nocname=not c.authorized_cname)]
if c.user.safe_karma >= g.discussion_karma_to_post:
buttons += [NamedButton('meetups/new', False,
nocname=not c.authorized_cname)]
Expand Down Expand Up @@ -1163,18 +1167,37 @@ def __init__(self, link = None, **kw):
Wrapped.__init__(self, link = link, *kw)



class NewLink(Wrapped):
"""Render the link submission form"""
def __init__(self, captcha = None, article = '', title= '', subreddits = (), tags = (), sr_id = None):
def __init__(self, captcha = None,
article = '',
title= '',
subreddits = (),
tags = (),
sr_id = None,
tab = 'article'):
self.tabs = Tabs()
self.tabs.add_tab('Text Article', 'article-field-pane', tab == 'article')
self.tabs.add_tab('Link', 'link-field-pane', tab == 'link')
self.editing = False

Wrapped.__init__(self, captcha = captcha, article = article,
title = title, subreddits = subreddits, tags = tags,
sr_id = sr_id, notify_on_comment = True,
cc_licensed = True)

class EditLink(Wrapped):
"""Render the edit link form"""
pass
def __init__(self, article, subreddits, tags, captcha):
self.editing = True
self.tabs = Tabs()
if article.is_self:
self.tabs.add_tab('Text Article', 'article-field-pane')
else:
self.tabs.add_tab('Link', 'link-field-pane')

Wrapped.__init__(self, article, captcha = captcha,
subreddits = subreddits, tags = tags)

class ShareLink(Wrapped):
def __init__(self, link_name = "", emails = None):
Expand Down Expand Up @@ -1659,3 +1682,39 @@ def __init__(self, name, page, skiplayout, **context):
title = self.pagename,
space_compress=False,
**context)

class TabModel(object):
def __init__(self, title, pane_id, is_selected):
self.title = title
self.pane_id = pane_id
self.is_selected = is_selected

class Tabs(Wrapped):
"""Renders a list of tabs which can be clicked on.
"""
def __init__(self):
self.tabs = []

def add_tab(self, title, pane_id, is_selected = False):
# The first tab is always selected
if len(self.tabs) == 0:
is_selected = True
# Ensure at most one tab is selected
if is_selected:
for tab in self.tabs:
tab.is_selected = False

self.tabs.append(TabModel(title, pane_id, is_selected))

def _get_tab(self, pane_id):
for tab in self.tabs:
if tab.pane_id == pane_id:
return tab

def has_tab(self, pane_id):
tab = self._get_tab(pane_id)
return tab is not None

def is_selected(self, pane_id):
tab = self._get_tab(pane_id)
return tab is not None and tab.is_selected
Loading

0 comments on commit 5dc2650

Please sign in to comment.