diff --git a/r2/r2/config/routing.py b/r2/r2/config/routing.py index 1603d86e..ba5a534e 100644 --- a/r2/r2/config/routing.py +++ b/r2/r2/config/routing.py @@ -94,6 +94,7 @@ def make_map(global_conf={}, app_conf={}): mc('/dashboard/comments', action='listing', controller='interestingcomments') mc('/dashboard/subscribed', action='listing', controller='interestingsubscribed') mc('/dashboard/posts', action='listing', controller='interestingposts') + mc('/dashboard/', action='listing', controller='dashboard') # Can't use map.resource because the version of the Routing module we're # using doesn't support the controller_action kw arg diff --git a/r2/r2/controllers/__init__.py b/r2/r2/controllers/__init__.py index d00b3734..58a8dd47 100644 --- a/r2/r2/controllers/__init__.py +++ b/r2/r2/controllers/__init__.py @@ -40,9 +40,11 @@ from listingcontroller import EditsController from listingcontroller import MeetupslistingController from listingcontroller import KarmaawardController -from listingcontroller import InterestingcommentsController -from listingcontroller import InterestingsubscribedController -from listingcontroller import InterestingpostsController + +from dashboardcontroller import InterestingcommentsController +from dashboardcontroller import InterestingsubscribedController +from dashboardcontroller import InterestingpostsController +from dashboardcontroller import DashboardController from listingcontroller import MyredditsController diff --git a/r2/r2/controllers/dashboardcontroller.py b/r2/r2/controllers/dashboardcontroller.py new file mode 100644 index 00000000..f09c4483 --- /dev/null +++ b/r2/r2/controllers/dashboardcontroller.py @@ -0,0 +1,266 @@ +from r2.controllers.listingcontroller import CommentsController, MeetupslistingController +from r2.models import * +from validator import * +from r2.lib.pages import * +from r2.lib.menus import DashboardTimeMenu +from r2.lib.db import queries + +from pylons.i18n import _ + +from datetime import datetime + +def last_dashboard_visit(): + hc_key = "dashboard_visit-%s" % c.user.name + cache_visit = g.permacache.get(hc_key, None) + if cache_visit: + return cache_visit + else: + last_visit = c.user.dashboard_visit + g.permacache.set(hc_key, last_visit, time = int(g.dashboard_visits_period)) + c.user.dashboard_visit = datetime.now(g.tz) + c.user._commit() + return last_visit + +class InterestingcommentsController(CommentsController): + title_text = _('Leading Comments') + builder_cls = UnbannedCommentBuilder + + @property + def header_sub_nav(self): + return [NamedButton("dashboard", dest="dashboard"), + NamedButton("leadingsubscribed", dest="dashboard/subscribed"), + NamedButton("leadingposts", dest="dashboard/posts"), + NamedButton("leadingcomments", dest="dashboard/comments")] + + def query(self): + q = Comment._query(Comment.c._spam == (True,False), + sort = desc('_interestingness'), + eager_load = True, data = True) + if not c.user_is_admin: + q._filter(Comment.c._spam == False) + + if self.time == 'last': + q._filter(Thing.c._date >= last_dashboard_visit()) + elif self.time != 'all': + q._filter(queries.db_times[self.time]) + + return q + + @staticmethod + def staticquery(): + q = Comment._query(Comment.c._spam == (True,False), + sort = desc('_interestingness'), + eager_load = True, data = True) + if not c.user_is_admin: + q._filter(Comment.c._spam == False) + + q._filter(Thing.c._date >= last_dashboard_visit()) + + return q + + def builder(self): + b = self.builder_cls(self.query_obj, + num = self.num, + skip = self.skip, + after = self.after, + count = self.count, + reverse = self.reverse, + wrap = self.builder_wrapper, + sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) + return b + + @property + def top_filter(self): + return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') + + @validate(VUser(), + time = VMenu('where', DashboardTimeMenu)) + def GET_listing(self, time, **env): + self.time = time + return CommentsController.GET_listing(self, **env) + +class InterestingsubscribedController(CommentsController): + title_text = _('Leading Subscribed Comments') + builder_cls = UnbannedCommentBuilder + + @property + def header_sub_nav(self): + return [NamedButton("dashboard", dest="dashboard"), + NamedButton("leadingsubscribed", dest="dashboard/subscribed"), + NamedButton("leadingposts", dest="dashboard/posts"), + NamedButton("leadingcomments", dest="dashboard/comments")] + + def query(self): + q = SubscriptionStorage._query(SubscriptionStorage.c._thing1_id == c.user._id, + SubscriptionStorage.c._t2_deleted == False, + SubscriptionStorage.c._name == 'subscriptionstorage', + sort = desc('_t2_interestingness'), + eager_load = True, + thing_data = not g.use_query_cache + ) + if not c.user_is_admin: + q._filter(SubscriptionStorage.c._t2_spam == False) + + q.prewrap_fn = lambda x: x._thing2 + + if self.time == 'last': + q._filter(SubscriptionStorage.c._date >= last_dashboard_visit()) + elif self.time != 'all': + q._filter(SubscriptionStorage.c._date >= timeago(queries.relation_db_times[self.time])) + + return q + + @staticmethod + def staticquery(): + q = SubscriptionStorage._query(SubscriptionStorage.c._thing1_id == c.user._id, + SubscriptionStorage.c._t2_deleted == False, + SubscriptionStorage.c._name == 'subscriptionstorage', + sort = desc('_t2_interestingness'), + eager_load = True, + thing_data = not g.use_query_cache + ) + if not c.user_is_admin: + q._filter(SubscriptionStorage.c._t2_spam == False) + + q.prewrap_fn = lambda x: x._thing2 + + q._filter(SubscriptionStorage.c._date >= last_dashboard_visit()) + + return q + + def builder(self): + b = self.builder_cls(self.query_obj, + num = self.num, + skip = self.skip, + after = self.after, + count = self.count, + reverse = self.reverse, + wrap = self.builder_wrapper, + sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) + return b + + @property + def top_filter(self): + return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') + + @validate(VUser(), + time = VMenu('where', DashboardTimeMenu)) + def GET_listing(self, time, **env): + self.time = time + return CommentsController.GET_listing(self, **env) + +class InterestingpostsController(CommentsController): + title_text = _('Leading Posts') + builder_cls = QueryBuilder + + @property + def header_sub_nav(self): + return [NamedButton("dashboard", dest="dashboard"), + NamedButton("leadingsubscribed", dest="dashboard/subscribed"), + NamedButton("leadingposts", dest="dashboard/posts"), + NamedButton("leadingcomments", dest="dashboard/comments")] + + def query(self): + q = Link._query(Link.c._spam == (True,False), + sort = desc('_interestingness'), + eager_load = True, data = True) + if not c.user_is_admin: + q._filter(Link.c._spam == False) + + if self.time == 'last': + q._filter(Thing.c._date >= last_dashboard_visit()) + elif self.time != 'all': + q._filter(queries.db_times[self.time]) + + return q + + @staticmethod + def staticquery(): + q = Link._query(Link.c._spam == (True,False), + sort = desc('_interestingness'), + eager_load = True, data = True) + if not c.user_is_admin: + q._filter(Link.c._spam == False) + + q._filter(Thing.c._date >= last_dashboard_visit()) + + return q + + def builder(self): + b = self.builder_cls(self.query_obj, + num = self.num, + skip = self.skip, + after = self.after, + count = self.count, + reverse = self.reverse, + wrap = self.builder_wrapper, + sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) + return b + + @property + def top_filter(self): + return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') + + @validate(VUser(), + time = VMenu('where', DashboardTimeMenu)) + def GET_listing(self, time, **env): + self.time = time + return CommentsController.GET_listing(self, **env) + +class DashboardController(CommentsController): + @property + def header_sub_nav(self): + return [NamedButton("dashboard", dest="dashboard"), + NamedButton("leadingsubscribed", dest="dashboard/subscribed"), + NamedButton("leadingposts", dest="dashboard/posts"), + NamedButton("leadingcomments", dest="dashboard/comments")] + + render_cls = FormPage + builder_cls = UnbannedCommentBuilder + + def iterable_builder(self, query): + b = self.builder_cls(query, + num = 3, + wrap = self.builder_wrapper, + sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) + return b + + + def create_listing(self, controller, title, link): + return DashboardListing(self.iterable_builder(controller), title, link).listing() + + @staticmethod + def builder_wrapper(thing): + w = Wrapped(thing) + + if isinstance(thing, Link): + w.render_class = LinkCompressed + + return w + + def content(self): + controllers = (InterestingsubscribedController.staticquery(), + InterestingpostsController.staticquery(), + InterestingcommentsController.staticquery(), + CommentsController.staticquery()) + titles = ('Subscribed Comments', 'Leading Posts', + 'Leading Comments', 'Recent Comments') + + links = ('/dashboard/subscribed', '/dashboard/posts', + '/dashboard/comments', '/comments') + + builders = [self.create_listing(*controller) for controller in zip(controllers, titles, links)] + + self.builder_cls = IDBuilder + + builders.append(self.create_listing(MeetupslistingController.staticquery(), 'Upcoming Meetups', '/meetups')) + + return Dashtable(*builders) + + @validate(VUser(), + time = VMenu('where', DashboardTimeMenu)) + def GET_listing(self, time, **env): + self.time = time + content = self.content() + res = FormPage("Dashboard", content = content, header_sub_nav = self.header_sub_nav).render() + return res diff --git a/r2/r2/controllers/listingcontroller.py b/r2/r2/controllers/listingcontroller.py index 26a4d3a7..6a84c623 100644 --- a/r2/r2/controllers/listingcontroller.py +++ b/r2/r2/controllers/listingcontroller.py @@ -460,6 +460,10 @@ def header_sub_nav(self): def query(self): return Meetup.upcoming_meetups_by_timestamp() + @staticmethod + def staticquery(): + return Meetup.upcoming_meetups_by_timestamp() + class ByIDController(ListingController): title_text = _('API') @@ -844,6 +848,15 @@ def query(self): return q + @staticmethod + def staticquery(): + q = Comment._query(Comment.c._spam == (True,False), + sort = desc('_date'), data = True) + if not c.user_is_admin: + q._filter(Comment.c._spam == False) + + return q + def builder(self): if c.user.pref_show_parent_comments: builder_cls = ContextualCommentBuilder @@ -905,156 +918,3 @@ def top_filter(self): def GET_listing(self, time, **env): self.time = time return CommentsController.GET_listing(self, **env) - -def last_dashboard_visit(): - hc_key = "dashboard_visit-%s" % c.user.name - cache_visit = g.permacache.get(hc_key, None) - if cache_visit: - return cache_visit - else: - last_visit = c.user.dashboard_visit - g.permacache.set(hc_key, last_visit, time = int(g.dashboard_visits_period)) - c.user.dashboard_visit = datetime.now(g.tz) - c.user._commit() - return last_visit - -class InterestingcommentsController(CommentsController): - title_text = _('Leading Comments') - builder_cls = UnbannedCommentBuilder - - @property - def header_sub_nav(self): - return [NamedButton("leadingsubscribed", dest="dashboard/subscribed"), - NamedButton("leadingposts", dest="dashboard/posts"), - NamedButton("leadingcomments", dest="dashboard/comments")] - - def query(self): - q = Comment._query(Comment.c._spam == (True,False), - sort = desc('_interestingness'), - eager_load = True, data = True) - if not c.user_is_admin: - q._filter(Comment.c._spam == False) - - if self.time == 'last': - q._filter(Thing.c._date >= last_dashboard_visit()) - elif self.time != 'all': - q._filter(queries.db_times[self.time]) - - return q - - def builder(self): - b = self.builder_cls(self.query_obj, - num = self.num, - skip = self.skip, - after = self.after, - count = self.count, - reverse = self.reverse, - wrap = self.builder_wrapper, - sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) - return b - - @property - def top_filter(self): - return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') - - @validate(VUser(), - time = VMenu('where', DashboardTimeMenu)) - def GET_listing(self, time, **env): - self.time = time - return CommentsController.GET_listing(self, **env) - -class InterestingsubscribedController(CommentsController): - title_text = _('Leading Subscribed Comments') - builder_cls = UnbannedCommentBuilder - - @property - def header_sub_nav(self): - return [NamedButton("leadingsubscribed", dest="dashboard/subscribed"), - NamedButton("leadingposts", dest="dashboard/posts"), - NamedButton("leadingcomments", dest="dashboard/comments")] - - def query(self): - q = SubscriptionStorage._query(SubscriptionStorage.c._thing1_id == c.user._id, - SubscriptionStorage.c._t2_deleted == False, - SubscriptionStorage.c._name == 'subscriptionstorage', - sort = desc('_t2_interestingness'), - eager_load = True, - thing_data = not g.use_query_cache - ) - #if not c.user_is_admin: - # q._filter(Comment.c._spam == False) - - q.prewrap_fn = lambda x: x._thing2 - - if self.time == 'last': - q._filter(SubscriptionStorage.c._date >= last_dashboard_visit()) - elif self.time != 'all': - q._filter(SubscriptionStorage.c._date >= timeago(queries.relation_db_times[self.time])) - - return q - - def builder(self): - b = self.builder_cls(self.query_obj, - num = self.num, - skip = self.skip, - after = self.after, - count = self.count, - reverse = self.reverse, - wrap = self.builder_wrapper, - sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) - return b - - @property - def top_filter(self): - return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') - - @validate(VUser(), - time = VMenu('where', DashboardTimeMenu)) - def GET_listing(self, time, **env): - self.time = time - return CommentsController.GET_listing(self, **env) - -class InterestingpostsController(CommentsController): - title_text = _('Leading Posts') - builder_cls = QueryBuilder - - @property - def header_sub_nav(self): - return [NamedButton("leadingsubscribed", dest="dashboard/subscribed"), - NamedButton("leadingposts", dest="dashboard/posts"), - NamedButton("leadingcomments", dest="dashboard/comments")] - - def query(self): - q = Link._query(Link.c._spam == (True,False), - sort = desc('_interestingness'), - eager_load = True, data = True) - if not c.user_is_admin: - q._filter(Link.c._spam == False) - - if self.time == 'last': - q._filter(Thing.c._date >= last_dashboard_visit()) - elif self.time != 'all': - q._filter(queries.db_times[self.time]) - - return q - - def builder(self): - b = self.builder_cls(self.query_obj, - num = self.num, - skip = self.skip, - after = self.after, - count = self.count, - reverse = self.reverse, - wrap = self.builder_wrapper, - sr_ids = [c.current_or_default_sr._id, Subreddit._by_name('discussion')._id]) - return b - - @property - def top_filter(self): - return DashboardTimeMenu(default = self.time, title = _('Filter'), type='dropdown2') - - @validate(VUser(), - time = VMenu('where', DashboardTimeMenu)) - def GET_listing(self, time, **env): - self.time = time - return CommentsController.GET_listing(self, **env) diff --git a/r2/r2/lib/menus.py b/r2/r2/lib/menus.py index 5daa9198..90f859d3 100644 --- a/r2/r2/lib/menus.py +++ b/r2/r2/lib/menus.py @@ -73,6 +73,7 @@ def __getattr__(self, attr): posts = _('Posts'), topcomments = _('Top Comments'), newcomments = _('New Comments'), + dashboard = _('Dashboard'), leadingsubscribed = _('Leading Subscribed'), leadingcomments = _('Leading Comments'), leadingposts = _('Leading Posts'), diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index 64a68168..9a4edaf3 100644 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -194,6 +194,7 @@ def corner_buttons(self): """set up for buttons in upper right corner of main page.""" buttons = [] if c.user_is_loggedin: + buttons += [NamedButton("dashboard", False)] if c.user.name in g.admins: if c.user_is_admin: buttons += [NamedButton("adminoff", False, @@ -330,6 +331,18 @@ def wrap_thing(thing): return w +def last_dashboard_visit(): + hc_key = "dashboard_visit-%s" % c.user.name + cache_visit = g.permacache.get(hc_key, None) + if cache_visit: + return cache_visit + else: + last_visit = c.user.dashboard_visit + g.permacache.set(hc_key, last_visit, time = int(g.dashboard_visits_period)) + c.user.dashboard_visit = datetime.now(g.tz) + c.user._commit() + return last_visit + class RecentComments(RecentItems): def query(self): return c.current_or_default_sr.get_comments('new', 'all') @@ -678,6 +691,20 @@ def __init__(self, a = None): Wrapped.__init__(self, a = a, datefmt = datefmt) +class Dashboard(Reddit): + """Container for the dashboard main page""" + def rightbox(self): + """Contents of the right box when rendering""" + pass + +class Dashtable(Wrapped): + def __init__(self, subscribed, link, comment, recentcomments, meetups): + wikiedits = DashboardWikiEditsListing(g.recent_edits_feed) + + blogfeed = DashboardBlogListing(g.feedbox_urls) + Wrapped.__init__(self, subscribed = subscribed, link = link, comment = comment, + recentcomments = recentcomments, meetups = meetups, + wikiedits = wikiedits, blogfeed = blogfeed) class EditReddit(Reddit): """Container for the about page for a reddit""" @@ -1485,11 +1512,15 @@ def __init__(self, feed_urls, *a, **kw): self.feed_urls = feed_urls Wrapped.__init__(self, *a, **kw) +class DashboardBlogListing(FeedBox): pass + class RecentWikiEditsBox(Wrapped): def __init__(self, feed_url, *a, **kw): self.feed_url = feed_url Wrapped.__init__(self, *a, **kw) +class DashboardWikiEditsListing(RecentWikiEditsBox): pass + class SiteMeter(Wrapped): def __init__(self, codename, *a, **kw): self.codename = codename diff --git a/r2/r2/models/link.py b/r2/r2/models/link.py index 680ef6c8..b52fb645 100644 --- a/r2/r2/models/link.py +++ b/r2/r2/models/link.py @@ -46,6 +46,7 @@ import re import random import urllib +import lxml from datetime import datetime class LinkExists(Exception): pass @@ -120,6 +121,13 @@ def can_submit(self, user): else: return False + def dashboard_snippet(self): + content = lxml.html.document_fromstring(self.article).text_content() + if len(content) < 300: + return self.article + else: + return content[:300]+content[300:].partition(' ')[0]+'...' + def is_blessed(self): return self.blessed diff --git a/r2/r2/models/listing.py b/r2/r2/models/listing.py index e80d57d8..77f7cbc6 100644 --- a/r2/r2/models/listing.py +++ b/r2/r2/models/listing.py @@ -130,6 +130,12 @@ def listing(self): #make into a tree thing return Wrapped(self) +class DashboardListing(Listing): + def __init__(self, builder, title, link): + self.title = title + self.link = link + Listing.__init__(self, builder) + class OrganicListing(Listing): # class used in Javascript to manage these objects _js_cls = "OrganicListing" diff --git a/r2/r2/public/static/main.css b/r2/r2/public/static/main.css index 12f2c919..297fbddf 100644 --- a/r2/r2/public/static/main.css +++ b/r2/r2/public/static/main.css @@ -631,6 +631,16 @@ div.comment, div.message { margin: 0 0 12px; padding: 5px; } +div.dashlisting { + padding: 0 10px 0; + margin: 0 5px 5px; +} +div.dashtable { + margin: 10px 0 0; +} +div.dashtable table tr td div.simpledash div.dashlisting { + padding: 0 15px 0; +} div.comment { margin: 10px 0px 0px 0px; padding: 2px 0 2px 15px; diff --git a/r2/r2/templates/dashboardbloglisting.html b/r2/r2/templates/dashboardbloglisting.html new file mode 100644 index 00000000..6dd2bdc8 --- /dev/null +++ b/r2/r2/templates/dashboardbloglisting.html @@ -0,0 +1,47 @@ +<% + import simplejson + from r2.lib.filters import unsafe + container_id = thing.feed_urls[0] + title_id = "%s_title" % container_id +%> +
+

+ + +
diff --git a/r2/r2/templates/dashboardlisting.html b/r2/r2/templates/dashboardlisting.html new file mode 100644 index 00000000..ab5d620c --- /dev/null +++ b/r2/r2/templates/dashboardlisting.html @@ -0,0 +1,45 @@ +## The contents of this file are subject to the Common Public Attribution +## License Version 1.0. (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License at +## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public +## License Version 1.1, but Sections 14 and 15 have been added to cover use of +## software over a computer network and provide for limited attribution for the +## Original Developer. In addition, Exhibit A has been modified to be consistent +## with Exhibit B. +## +## Software distributed under the License is distributed on an "AS IS" basis, +## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +## the specific language governing rights and limitations under the License. +## +## The Original Code is Reddit. +## +## The Original Developer is the Initial Developer. The Initial Developer of +## the Original Code is CondeNet, Inc. +## +## All portions of the code written by CondeNet are Copyright (c) 2006-2008 +## CondeNet, Inc. All Rights Reserved. +################################################################################ + +<% from r2.lib.template_helpers import replace_render, add_sr %> +<%namespace file="utils.html" import="plain_link" /> + +<% + _id = ("_%s" % thing.parent_name) if hasattr(thing, 'parent_name') else '' + %> +

${thing.title}

+
+
+
+
+ + %for a in thing.things: + ${unsafe(replace_render(thing, a))} + %endfor +
+ +%if not thing.things: +

${_("Nothing since your last visit.")}

+%endif +${plain_link(_("More"), thing.link)} +
+ diff --git a/r2/r2/templates/dashboardwikieditslisting.html b/r2/r2/templates/dashboardwikieditslisting.html new file mode 100644 index 00000000..5e041077 --- /dev/null +++ b/r2/r2/templates/dashboardwikieditslisting.html @@ -0,0 +1,46 @@ +<% + from r2.lib.template_helpers import static, add_sr, join_urls + container_id = thing.feed_url +## title_id = "%s_title" % container_id +%> +
+

Recent Wiki Edits

+ + +
diff --git a/r2/r2/templates/dashtable.html b/r2/r2/templates/dashtable.html new file mode 100644 index 00000000..497a6a3e --- /dev/null +++ b/r2/r2/templates/dashtable.html @@ -0,0 +1,24 @@ +
+ + + + + + + + + + + + + + + + + + + + + +
${thing.subscribed.render()}${thing.link.render()}
${thing.comment.render()}${thing.recentcomments.render()}
${thing.blogfeed.render()}
${thing.meetups.render()}
${thing.wikiedits.render()}
+
diff --git a/r2/r2/templates/linkcompressed.html b/r2/r2/templates/linkcompressed.html new file mode 100644 index 00000000..bde59503 --- /dev/null +++ b/r2/r2/templates/linkcompressed.html @@ -0,0 +1,378 @@ +## The contents of this file are subject to the Common Public Attribution +## License Version 1.0. (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License at +## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public +## License Version 1.1, but Sections 14 and 15 have been added to cover use of +## software over a computer network and provide for limited attribution for the +## Original Developer. In addition, Exhibit A has been modified to be consistent +## with Exhibit B. +## +## Software distributed under the License is distributed on an "AS IS" basis, +## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +## the specific language governing rights and limitations under the License. +## +## The Original Code is Reddit. +## +## The Original Developer is the Initial Developer. The Initial Developer of +## the Original Code is CondeNet, Inc. +## +## All portions of the code written by CondeNet are Copyright (c) 2006-2008 +## CondeNet, Inc. All Rights Reserved. +################################################################################ + +<%! + from r2.models.subreddit import Default + from r2.lib.template_helpers import get_domain, static + from r2.lib.filters import safemarkdown, cleanhtml + from r2.lib.utils import prettytime, epochtime + from r2.lib.strings import strings + from r2.models.poll import renderpolls + %> + +<%namespace file="utils.html" import="separator,plain_link"/> +<%inherit file="printable.html"/> + +<%def name="RenderPrintable()"> +<% + css_class = "post" + if thing.blessed: + css_class += ' editors-pick' + + if thing.render_full: + full_article = True + heading_size = 1 + else: + full_article = False + heading_size = 2 + css_class += ' list' + + fullname = thing._fullname +%> + +
+ ${self.title()}${unsafe(self.draft())} +
+ + %if thing.hide_score: + + %else: + ${self.score(thing, thing.likes, label = False)} + %endif + ${unsafe(self.author(friend = thing.friend, + label = unsafe('Post author:') if full_article else ''))} + ${prettytime(thing._date)} +
+##{_RL + +##}_RL +
+## The div below is a hack to get the space compressor to leave whitespace in articles alone +
+
+ ${self.summary()} +
+
+
+ %if full_article: + + %endif +
+ ${parent.midcol(thing.votable)} + %if c.user_is_loggedin and (not c.profilepage): + <% clicked = thing.clicked() if thing.clicked else None %> + + %endif + %if thing.comments_enabled: + <% + num_comments = 0 if not thing.num_comments else thing.num_comments + %> + ${_('Comments (%d)') % (num_comments)} + %endif +
+
    + ${self.buttons(comments = False, report=False)} +
+ %if full_article: + <% tags = thing.get_tags() %> + %if len(tags): +
+ Tags: + %for tag in thing.get_tags(): + ${tag.name}${separator(" ")} + %endfor +
+ %endif + %endif +
+ ## Each link has a hidden status span that is used to indicate voting errors. + +
+
+ + + +<%def name="title()"> + ${thing.title} + + +<%def name="draft()"> + %if thing.draft: + Draft + %endif + + +<%def name="article()"> + ${unsafe(renderpolls(cleanhtml(thing.article), thing))} + + +<%def name="summary()"> + ${unsafe(cleanhtml(renderpolls(thing.dashboard_snippet(), thing)))} + <%call expr="make_link('read_more', 'more', 'more')"> + continue reading » + + + +<%def name="numcol()"> + <% num = thing.num %> + + %if thing.top_link: + + %endif + ##placeholder for the number + $num + %if thing.top_link: + + %endif + + + +<%def name="make_link(name, css_class, anchor=None)"> + + ${caller.body()} + + + +<%def name="entry()"> +

+ <%call expr="make_link('title', 'title')"> + ${thing.title} + +

+ + % if thing.render_full: +
+ ${unsafe(cleanhtml(renderpolls(thing.article, thing)))} +
+ <% tags = thing.get_tags() %> + %if len(tags): +
+ Tags: +
+ %endif + %else: + % if hasattr(thing, '_summary'): +
+ ${unsafe(cleanhtml(renderpolls(thing._summary(), thing)))} +
+ % if thing._has_more(): + <%call expr="make_link('read_more', 'md', 'more')"> + Continue reading “${thing.title}”... + + % endif + %endif + %endif + ${self.mediadiv()} + + +<%def name="subreddit()" buffered="True"> + <% + if c.cname: + path = "http://" + get_domain(cname = (c.site == thing.subreddit), + subreddit = (c.site != thing.subreddit)) + else: + path = thing.subreddit.path + endif + %> + + ${thing.subreddit.name} + + + + +<%def name="domain()"> + + (${thing.domain}) + + + +<%def name="tagline()"> + <% + from r2.lib.utils import timeago + from r2.models import FakeSubreddit + + if isinstance(c.site, FakeSubreddit) or c.site != thing.subreddit: + taglinetext = _("Submitted %(when)s ago by %(author)s to %(reddit)s") + else: + taglinetext = _("Submitted %(when)s ago by %(author)s") + taglinetext = taglinetext.replace(" ", " ") + %> + ${unsafe(taglinetext % dict(reddit = self.subreddit(), + when = thing.timesince, + author= self.author(friend = thing.friend)))} + + +<%def name="child()"> + + +<%def name="buttons(comments=True,delete=True,report=True,ban=True,additional='')"> + <% fullname = thing._fullname %> + %if comments: + <% + if not thing.num_comments: + # generates "comment" the imperative verb + com_label = _("Comment") + else: + # generates "XX comments" as a noun + com_label = ungettext("comment", "comments", thing.num_comments) + %> +
  • + ${parent.comment_button("comment", fullname, com_label, + thing.num_comments, thing.permalink, + newwindow = c.user.pref_newwindow)} +
  • + %endif +##
  • +## ${plain_link("Feed", "%s/.rss" % thing.url)} +##
  • + %if c.user_is_loggedin: + %if thing.can_submit(c.user): +
  • + <% kw = {"class": "edit", "title": "Edit"} %> + ${plain_link("Edit", "/edit/%s" % thing._id36, **kw)} +
  • + %endif + %if not thing.author == c.user: +
  • + ${parent.state_button("linksubscribe", fullname, _("Subscribe"), \ + "return change_state_by_class(this, '%s', 'mod')"%("linkunsubscribe" if thing.link_subscribed(c.user, thing) else "linksubscribe"), \ + _("Subscribed"), a_class="subscription"+(" mod" if thing.link_subscribed(c.user, thing) else ""), \ + tool_tip = "No longer receive notifications for this post" if thing.link_subscribed(c.user, thing) else "Get notifications of top level comments on this post")} +
  • + %endif + %if c.user_is_admin: +
  • + %if thing.is_blessed(): + ${parent.state_button("unbless", fullname, _("Demote"), \ + "return change_state(this, 'unbless');", _("Demoted"))} + %else: + ${parent.state_button("bless", fullname, _("Promote"), \ + "return change_state(this, 'bless');", _("Promoted"))} + %endif +
  • + %endif +
  • + ${parent.state_button("save", fullname, _("Save"), \ + "return change_state_by_class(this, '%s', 'mod')"%("unsave" if thing.saved else "save"), \ + _("Saved"), a_class="save"+(" mod" if thing.saved else ""), \ + tool_tip = "Unsave" if thing.saved else "Save")} +
  • + %if not thing.render_full: +
  • + %if thing.hidden: + ${parent.state_button("unhide", fullname, _("Unhide"), \ + "return change_w_callback(this, $ListClass.unhide);", _("Unhidden"), a_class="hide mod", tool_tip="Unhide")} + %else: + ${parent.state_button("hide", fullname, _("Hide"), \ + "return change_w_callback(this, $ListClass.hide);", _("Hidden"), a_class="hide", tool_tip="Hide")} + %endif +
  • + %endif + %endif + ${parent.delete_or_report_buttons(delete=delete,report=report)} + ${parent.buttons(ban=ban)} + %if thing.cc_licensed: +
  • + CC Licenced +
  • + %endif + ${additional} + ${self.media_embed()} + + + +<%def name="media_embed()"> + %if thing.media_object: +
  • + \ + + ${_("Watch")} + + + +
  • + %endif + + +<%def name="thumbnail()"> + %if thing.thumbnail: + <%call expr="make_link('thumbnail', 'thumbnail')"> + + + %endif + + +<%def name="mediadiv()"> + %if thing.media_object: + + %endif +