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 +%> +
${_("Nothing since your last visit.")}
+%endif +${plain_link(_("More"), thing.link)} +${thing.subscribed.render()} | +${thing.link.render()} | +
${thing.comment.render()} | +${thing.recentcomments.render()} | +
${thing.blogfeed.render()} |
+${thing.meetups.render()} |
+
${thing.wikiedits.render()} |
++ |
+ <%call expr="make_link('title', 'title')"> + ${thing.title} + %call> +
+ + % if thing.render_full: +