diff --git a/r2/example.ini b/r2/example.ini index c7cbdf9c..67cae30b 100644 --- a/r2/example.ini +++ b/r2/example.ini @@ -124,6 +124,7 @@ HOT_PAGE_AGE = 1 # media_period = 10 minutes rising_period = 12 hours +comment_visits_period = 600 # time of ratelimit purgatory (min) RATELIMIT = 1 diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py index d1a4adec..ba80015d 100644 --- a/r2/r2/controllers/front.py +++ b/r2/r2/controllers/front.py @@ -42,6 +42,7 @@ import re import time as time_module from urllib import quote_plus +from datetime import datetime class FrontController(RedditController): @@ -117,6 +118,42 @@ def GET_details(self, article): rightbox, so it is only useful for Admin-only wizardry.""" return DetailsPage(link = article).render() + def _comment_visits(self, article, user, new_visit=None): + + hc_key = "comment_visits-%s-%s" % (user.name, article._id36) + old_visits = g.permacache.get(hc_key, []) + + append = False + + if new_visit is None: + pass + elif len(old_visits) == 0: + append = True + else: + last_visit = max(old_visits) + time_since_last = new_visit - last_visit + if (time_since_last.days > 0 or time_since_last.seconds > int(g.comment_visits_period)): + append = True + else: + # They were just here a few seconds ago; consider that + # the same "visit" as right now + old_visits.pop() + + if append: + copy = list(old_visits) # make a copy + copy.append(new_visit) + if len(copy) > 3: + copy.pop(0) + g.permacache.set(hc_key, copy, time = 86400 * 2) + + if len(old_visits) > 0: + time_since_pub = new_visit - article._date + ten_percent = new_visit - (time_since_pub // 10) + old_visit = min(old_visits) + if ten_percent < old_visit: + old_visits.insert(0, ten_percent) + + return old_visits @validate(article = VLink('article'), comment = VCommentID('comment'), @@ -142,10 +179,14 @@ def GET_comments(self, article, comment, context, sort, num_comments): #check for 304 self.check_modified(article, 'comments') - # if there is a focal comment, communicate down to comment_skeleton.html who - # that will be + # If there is a focal comment, communicate down to + # comment_skeleton.html who that will be. Also, skip + # comment_visits check + previous_visits = None if comment: c.focal_comment = comment._id36 + elif (c.user_is_loggedin): + previous_visits = self._comment_visits(article, c.user, datetime.now(g.tz)) # check if we just came from the submit page infotext = None @@ -180,6 +221,9 @@ def GET_comments(self, article, comment, context, sort, num_comments): ) displayPane.append(permamessage) + if previous_visits: + displayPane.append(CommentVisitsBox(previous_visits)) + # insert reply box only for logged in user if c.user_is_loggedin and article.subreddit_slow.can_comment(c.user): displayPane.append(CommentReplyBox()) diff --git a/r2/r2/controllers/meetupscontroller.py b/r2/r2/controllers/meetupscontroller.py index 53c7004a..612d09c9 100644 --- a/r2/r2/controllers/meetupscontroller.py +++ b/r2/r2/controllers/meetupscontroller.py @@ -10,7 +10,7 @@ from r2.lib.filters import python_websafe from r2.lib.jsonresponse import Json from r2.lib.menus import CommentSortMenu,NumCommentsMenu -from r2.lib.pages import BoringPage, ShowMeetup, NewMeetup, EditMeetup, PaneStack, CommentListing, LinkInfoPage, CommentReplyBox, NotEnoughKarmaToPost +from r2.lib.pages import BoringPage, ShowMeetup, NewMeetup, EditMeetup, PaneStack, CommentListing, LinkInfoPage, CommentReplyBox, NotEnoughKarmaToPost, CommentVisitsBox from r2.models import Meetup,Link,Subreddit,CommentBuilder,PendingJob from r2.models.listing import NestedListing from validator import validate, VUser, VModhash, VRequired, VMeetup, VEditMeetup, VFloat, ValueOrBlank, ValidIP, VMenu, VCreateMeetup, VTimestamp @@ -168,6 +168,43 @@ def GET_edit(self, meetup): timestamp=int(meetup.timestamp * 1000), tzoffset=meetup.tzoffset)).render() + def _comment_visits(self, article, user, new_visit=None): + + hc_key = "comment_visits-%s-%s" % (user.name, article._id36) + old_visits = g.permacache.get(hc_key, []) + + append = False + + if new_visit is None: + pass + elif len(old_visits) == 0: + append = True + else: + last_visit = max(old_visits) + time_since_last = new_visit - last_visit + if (time_since_last.days > 0 or time_since_last.seconds > int(g.comment_visits_period)): + append = True + else: + # They were just here a few seconds ago; consider that + # the same "visit" as right now + old_visits.pop() + + if append: + copy = list(old_visits) # make a copy + copy.append(new_visit) + if len(copy) > 3: + copy.pop(0) + g.permacache.set(hc_key, copy, time = 86400 * 2) + + if len(old_visits) > 0: + time_since_pub = new_visit - article._date + ten_percent = new_visit - (time_since_pub // 10) + old_visit = min(old_visits) + if ten_percent < old_visit: + old_visits.insert(0, ten_percent) + + return old_visits + # Show a meetup. Most of this code was coped from GET_comments in front.py @validate(meetup = VMeetup('id'), sort = VMenu('controller', CommentSortMenu), @@ -179,6 +216,10 @@ def GET_show(self, meetup, sort, num_comments): user_num = c.user.pref_num_comments or g.num_comments num = g.max_comments if num_comments == 'true' else user_num + previous_visits = None + if (c.user_is_loggedin): + previous_visits = self._comment_visits(article, c.user, datetime.now(g.tz)) + builder = CommentBuilder(article, CommentSortMenu.operator(sort), None, None) listing = NestedListing(builder, num=num, parent_name = article._fullname) displayPane = PaneStack() @@ -189,6 +230,9 @@ def GET_show(self, meetup, sort, num_comments): displayPane.append(CommentReplyBox(link_name = article._fullname)) + if previous_visits: + displayPane.append(CommentVisitsBox(previous_visits)) + # finally add the comment listing displayPane.append(listing.listing()) diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index 64a68168..500c95cf 100644 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -35,7 +35,7 @@ from r2.lib.menus import NavButton, NamedButton, NavMenu, JsButton, ExpandableButton, AbsButton from r2.lib.menus import SubredditButton, SubredditMenu, menu from r2.lib.strings import plurals, rand_strings, strings -from r2.lib.utils import title_to_url, query_string, UrlParser +from r2.lib.utils import title_to_url, query_string, UrlParser, timesince from r2.lib.template_helpers import add_sr, get_domain from r2.lib.promote import promote_builder_wrapper from r2.lib.wikipagecached import WikiPageCached @@ -1102,6 +1102,14 @@ class Frame(Wrapped): def __init__(self, url='', title='', fullname=''): Wrapped.__init__(self, url = url, title = title, fullname = fullname) +class CommentVisitsBox(Wrapped): + def __init__(self, visits, *a, **kw): + self.visits = [] + for visit in reversed(visits): + pretty = timesince(visit, resultion=1) + self.visits.append([pretty, visit]) + Wrapped.__init__(self, *a, **kw) + class FrameToolbar(Wrapped): """The reddit voting toolbar used together with Frame.""" extension_handling = False diff --git a/r2/r2/lib/template_helpers.py b/r2/r2/lib/template_helpers.py index 48dfd9e8..b6ed7645 100644 --- a/r2/r2/lib/template_helpers.py +++ b/r2/r2/lib/template_helpers.py @@ -66,7 +66,7 @@ def path_info(): params = dict(request.get)) return unsafe(simplejson.dumps(loc)) - + def replace_render(listing, item, style = None, display = True): style = style or c.render_style or 'html' diff --git a/r2/r2/public/static/comments.js b/r2/r2/public/static/comments.js index 1299fd8f..693efb52 100644 --- a/r2/r2/public/static/comments.js +++ b/r2/r2/public/static/comments.js @@ -241,19 +241,31 @@ function morechildren(form, link_id, children, depth) { function getAttrTime(e) { return parseInt(e.readAttribute('time')); } -function highlightNewComments() { - var lastViewed = $('lastViewed') - if (!lastViewed) - return; - - var last = getAttrTime(lastViewed); - if (last<=0) - return; +function highlightNewComments(last) { + if (!last) { + var lastViewed = $("comment-visits"); + if (!lastViewed) { + lastViewed = $('lastViewed'); + if (!lastViewed) + return; + last = getAttrTime(lastViewed); + } + else { + last = lastViewed.options[lastViewed.selectedIndex].value; + } + } + $$('div.comment').each(function(div, i) { var t = getAttrTime(div.select('.comment-date')[0]); - if (last + +
+
+ ${_("Highlight comments posted since:")} + +
+
diff --git a/r2/r2/templates/commentvisitsbox.xml b/r2/r2/templates/commentvisitsbox.xml new file mode 100644 index 00000000..d30a2984 --- /dev/null +++ b/r2/r2/templates/commentvisitsbox.xml @@ -0,0 +1,22 @@ +## 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. +################################################################################ +