Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Domain Notes: Keep and display notes for individual domains
Browse files Browse the repository at this point in the history
  • Loading branch information
Roger Ostrander committed Apr 1, 2014
1 parent debf587 commit d942c80
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 0 deletions.
18 changes: 18 additions & 0 deletions r2/r2/controllers/api.py
Expand Up @@ -3906,3 +3906,21 @@ def POST_store_visits(self, links):
return

LinkVisitsByAccount._visit(c.user, links)

@validatedForm(
VAdmin(),
VModhash(),
system=VLength('system', 1024),
subject=VLength('subject', 1024),
note=VLength('note', 10000),
author=VLength('author', 1024),
)
def POST_add_admin_note(self, form, jquery, system, subject, note, author):
if form.has_errors(('system', 'subject', 'note', 'author'),
errors.TOO_LONG):
return

if note:
from r2.models.admin_notes import AdminNotesBySystem
AdminNotesBySystem.add(system, subject, note, author)
form.refresh()
19 changes: 19 additions & 0 deletions r2/r2/lib/pages/admin_pages.py
Expand Up @@ -21,6 +21,7 @@
###############################################################################

from pylons import c, g
from pylons.i18n import N_
from r2.lib.wrapped import Templated
from pages import Reddit
from r2.lib.menus import (
Expand All @@ -30,6 +31,7 @@
NavMenu,
OffsiteButton,
)
from r2.lib.utils import timesince

def admin_menu(**kwargs):
buttons = [
Expand Down Expand Up @@ -80,6 +82,23 @@ def __init__(self, link):
NavMenu.__init__(self, [], title='admin', type="tabdrop")


class AdminNotesSidebar(Templated):
EMPTY_MESSAGE = {
"domain": N_("No notes for this domain"),
}

def __init__(self, system, subject):
from r2.models.admin_notes import AdminNotesBySystem

self.system = system
self.subject = subject
self.author = c.user.name
self.notes = AdminNotesBySystem.in_display_order(system, subject)
# Convert timestamps for easier reading/translation
for note in self.notes:
note["timesince"] = timesince(note["when"])
Templated.__init__(self)

try:
from r2admin.lib.pages import *
except ImportError:
Expand Down
5 changes: 5 additions & 0 deletions r2/r2/lib/pages/pages.py
Expand Up @@ -410,6 +410,11 @@ def rightbox(self):
if not c.user_is_loggedin and self.loginbox and not g.read_only_mode:
ps.append(LoginFormWide())

if isinstance(c.site, DomainSR) and c.user_is_admin:
from r2.lib.pages.admin_pages import AdminNotesSidebar
notebar = AdminNotesSidebar('domain', c.site.domain)
ps.append(notebar)

if c.user.pref_show_sponsorships or not c.user.gold:
ps.append(SponsorshipBox())

Expand Down
69 changes: 69 additions & 0 deletions r2/r2/models/admin_notes.py
@@ -0,0 +1,69 @@
# 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 reddit Inc.
#
# All portions of the code written by reddit are Copyright (c) 2006-2013 reddit
# Inc. All Rights Reserved.
###############################################################################

import json
from datetime import datetime

from pycassa.system_manager import UTF8_TYPE, TIME_UUID_TYPE
from pycassa.util import convert_uuid_to_time
from pylons import g

from r2.lib.db import tdb_cassandra


class AdminNotesBySystem(tdb_cassandra.View):
_use_db = True
_read_consistency_level = tdb_cassandra.CL.QUORUM
_write_consistency_level = tdb_cassandra.CL.ONE
_compare_with = TIME_UUID_TYPE
_extra_schema_creation_args = {
"key_validation_class": UTF8_TYPE,
"default_validation_class": UTF8_TYPE,
}

@classmethod
def add(cls, system_name, subject, note, author, when=None):
if not when:
when = datetime.now(g.tz)
jsonpacked = json.dumps({"note": note, "author": author})
updatedict = {when: jsonpacked}
key = cls._rowkey(system_name, subject)
cls._set_values(key, updatedict)

@classmethod
def in_display_order(cls, system_name, subject):
key = cls._rowkey(system_name, subject)
try:
query = cls._cf.get(key, column_reversed=True)
except tdb_cassandra.NotFoundException:
return []
result = []
for uuid, json_blob in query.iteritems():
when = datetime.fromtimestamp(convert_uuid_to_time(uuid), tz=g.tz)
payload = json.loads(json_blob)
payload['when'] = when
result.append(payload)
return result

@classmethod
def _rowkey(cls, system_name, subject):
return "%s:%s" % (system_name, subject)
16 changes: 16 additions & 0 deletions r2/r2/public/static/css/reddit.less
Expand Up @@ -8307,3 +8307,19 @@ body.with-listing-chooser {
border-radius: 5px;
-moz-border-radius: 5px;
}

#past-notes {
overflow-y: auto;
max-height: 150px;

li.adminnote {
border-top: 1px solid black;
overflow-x: auto;
}

.adminnote-info {
text-align: right;
font-size: small;
font-style: italic;
}
}
56 changes: 56 additions & 0 deletions r2/r2/templates/adminnotessidebar.html
@@ -0,0 +1,56 @@
## 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 reddit Inc.
##
## All portions of the code written by reddit are Copyright (c) 2006-2013
## reddit Inc. All Rights Reserved.
###############################################################################

<%namespace file="utils.html" import="error_field"/>
<%namespace file="utils.html" import="md" />

<div class="raisedbox spacer">
<form id="adminnotes-form" method="post" action="/api/add_admin_note"
onsubmit="return post_form(this, 'add_admin_note')">
<input type="hidden" name="system" value="${thing.system}">
<input type="hidden" name="subject" value="${thing.subject}">
<input type="hidden" name="author" value="${thing.author}">
<textarea name="note" rows=4 cols=20></textarea>
${error_field("TOO_LONG", "notes", "span")}
<input type="submit" class="notes=button" value="Add a new Note">
</form>
%if thing.notes:
${_("Past notes")}:
<ul id="past-notes">
%for note in thing.notes:
<li class="adminnote">
<div class="adminnote-text">
${md(note["note"])}
</div>
<div class="adminnote-info tagline">
${_("by %(author)s, %(when)s ago") % dict(
author=note["author"],
when=note["timesince"],
)}
</div>
</li>
%endfor
</ul>
%else:
${_(thing.EMPTY_MESSAGE[thing.system])}
%endif
</div>

0 comments on commit d942c80

Please sign in to comment.