messages can now have toggleable formatting at the database level

pull/4/head
Blake DeMarcy 2017-04-18 12:17:03 -05:00
parent 662f9c3b70
commit fa9cc49337
8 changed files with 139 additions and 29 deletions

View File

@ -550,6 +550,21 @@ class BBJ(object):
return response["data"]
def set_post_raw(self, thread_id, post_id, value):
"""
This is a subset of `edit_message` that retains the old
body and just sets its `send_raw` to your supplied `value`.
The `edited` parameter of the message on the server is not
modified.
"""
response = self(
"set_post_raw",
thread_id=thread_id,
post_id=post_id,
value=bool(value))
return response["data"]
def user_is_admin(self, user_name_or_id):
"""
Return boolean True or False whether the given user identifier

View File

@ -500,6 +500,13 @@ class App(object):
width=30, height=6)
def toggle_formatting(self, button, message):
self.remove_overlays()
raw = not message["send_raw"]
network.set_post_raw(message["thread_id"], message["post_id"], raw)
return self.refresh()
def on_post(self, button, message):
quotes = self.get_quotes(message)
author = self.usermap[message["author"]]
@ -520,7 +527,11 @@ class App(object):
msg = "Thread"
else: msg = "Post"
raw = message["send_raw"]
buttons.insert(0, urwid.Button("Delete %s" % msg, self.deletion_dialog, message))
buttons.insert(0, urwid.Button(
"Enable Formatting" if raw else "Disable Formatting",
self.toggle_formatting, message))
buttons.insert(0, urwid.Button("Edit Post", self.edit_post, message))
if not buttons:
@ -550,6 +561,8 @@ class App(object):
but can be passed `str` for strings.
"""
quotes = []
if msg_object["send_raw"]:
return quotes
for paragraph in msg_object["body"]:
# yes python is lisp fuck you
[quotes.append(cdr) for car, cdr in paragraph if car == "quote"]
@ -1297,6 +1310,9 @@ class MessageBody(urwid.Text):
An urwid.Text object that works with the BBJ formatting directives.
"""
def __init__(self, message):
if message["send_raw"]:
return super(MessageBody, self).__init__(message["body"])
text_objects = message["body"]
result = []
last_directive = None

View File

@ -32,5 +32,6 @@ create table messages (
author text, -- string (uuid1, user.user_id)
created real, -- floating point unix timestamp (when reply was posted)
edited int, -- bool
body text -- string
body text, -- string
send_raw int -- bool (1/true == never apply formatting)
);

View File

@ -214,11 +214,15 @@ class API(object):
def thread_create(self, args, database, user, **kwargs):
"""
Creates a new thread and returns it. Requires the non-empty
string arguments `body` and `title`
string arguments `body` and `title`.
If the argument `send_raw` is specified and has a non-nil
value, the OP message will never recieve special formatting.
"""
validate(args, ["body", "title"])
thread = db.thread_create(
database, user["user_id"], args["body"], args["title"])
database, user["user_id"], args["body"],
args["title"], args.get("send_raw"))
cherrypy.thread_data.usermap = \
create_usermap(database, thread["messages"])
return thread
@ -229,10 +233,14 @@ class API(object):
"""
Creates a new reply for the given thread and returns it.
Requires the string arguments `thread_id` and `body`
If the argument `send_raw` is specified and has a non-nil
value, the message will never recieve special formatting.
"""
validate(args, ["thread_id", "body"])
return db.thread_reply(
database, user["user_id"], args["thread_id"], args["body"])
database, user["user_id"], args["thread_id"],
args["body"], args.get("send_raw"))
@api_method
@ -266,13 +274,19 @@ class API(object):
of a post without actually attempting to replace it, use
`edit_query` first.
Optionally you may also include the argument `send_raw` to
set the message's formatting flag. However, if this is the
only change you would like to make, you should use the
endpoint `set_post_raw` instead.
Returns the new message object.
"""
if user == db.anon:
raise BBJUserError("Anons cannot edit messages.")
validate(args, ["body", "thread_id", "post_id"])
return db.message_edit_commit(
database, user["user_id"], args["thread_id"], args["post_id"], args["body"])
database, user["user_id"], args["thread_id"],
args["post_id"], args["body"], args.get("send_raw"))
@api_method
@ -286,6 +300,8 @@ class API(object):
or have admin rights. The same error descriptions and code
are returned on falilure. Boolean true is returned on
success.
If the post_id is 0, the whole thread is deleted.
"""
if user == db.anon:
raise BBJUserError("Anons cannot delete messages.")
@ -294,6 +310,31 @@ class API(object):
database, user["user_id"], args["thread_id"], args["post_id"])
@api_method
def set_post_raw(self, args, database, user, **kwargs):
"""
Requires the boolean argument of `value`, string argument
`thread_id`, and integer argument `post_id`. `value`, when false,
means that the message will be passed through message formatters
before being sent to clients. When `value` is true, this means
it will never go through formatters, all of its whitespace is
sent to clients verbatim and expressions are not processed.
The same rules for editing messages (see `edit_query`) apply here
and the same error objects are returned for violations.
You may optionally set this value as well when using `edit_post`,
but if this is the only change you want to make to the message,
using this endpoint instead is preferable.
"""
if user == db.anon:
raise BBJUserError("Anons cannot edit messages.")
validate(args, ["value", "thread_id", "post_id"])
return db.message_edit_commit(
database, user["user_id"],
args["thread_id"], args["post_id"],
None, args["value"], None)
@api_method
def is_admin(self, args, database, user, **kwargs):
@ -343,17 +384,17 @@ class API(object):
@api_method
def set_thread_pin(self, args, database, user, **kwargs):
"""
Requires the arguments `thread_id` and `pinned`. Pinned
Requires the arguments `thread_id` and `value`. `value`
must be a boolean of what the pinned status should be.
This method requires that the caller is logged in and
has admin status on their account.
Returns the same boolean you supply as `pinned`
Returns the same boolean you supply as `value`
"""
validate(args, ["thread_id", "pinned"])
validate(args, ["thread_id", "value"])
if not user["is_admin"]:
raise BBJUserError("Only admins can set thread pins")
return db.set_thread_pin(database, args["thread_id"], args["pinned"])
return db.set_thread_pin(database, args["thread_id"], args["value"])
@api_method

View File

@ -93,7 +93,7 @@ def thread_set_pin(connection, thread_id, pin_bool):
return pin_bool
def thread_create(connection, author_id, body, title):
def thread_create(connection, author_id, body, title, send_raw=False):
"""
Create a new thread and return it.
"""
@ -116,31 +116,32 @@ def thread_create(connection, author_id, body, title):
# the thread is initially commited with reply_count -1 so that i can
# just pass the message to the reply method, instead of duplicating
# its code here. It then increments to 0.
thread_reply(connection, author_id, thread_id, body, time_override=now)
thread_reply(connection, author_id, thread_id, body, send_raw, time_override=now)
# fetch the new thread out of the database instead of reusing the returned
# objects, just to be 100% sure what is returned is what was committed
return thread_get(connection, thread_id)
def thread_reply(connection, author_id, thread_id, body, time_override=None):
def thread_reply(connection, author_id, thread_id, body, send_raw=False, time_override=None):
"""
Submit a new reply for thread_id. Return the new reply object.
time_overide can be time() value to set as the new message time.
time_overide can be a time() value to set as the new message time.
This is to keep post_id 0 in exact parity with its parent thread.
"""
validate([("body", body)])
now = time_override or time()
thread = thread_get(connection, thread_id, messages=False)
count = thread["reply_count"] + 1
thread["reply_count"] += 1
count = thread["reply_count"]
scheme = schema.message(
thread_id, count, author_id,
now, False, body)
now, False, body, bool(send_raw))
connection.execute("""
INSERT INTO messages
VALUES (?,?,?,?,?,?)
VALUES (?,?,?,?,?,?,?)
""", schema_values("message", scheme))
connection.execute("""
@ -157,7 +158,7 @@ def thread_reply(connection, author_id, thread_id, body, time_override=None):
def message_delete(connection, author, thread_id, post_id):
"""
'Delete' a message from a thread. If the message being
deleted is an OP [pid 0], delete the whole thread.
deleted is an OP [post_id == 0], delete the whole thread.
Requires an author id, the thread_id, and post_id.
The same rules for edits apply to deletions: the same
@ -215,25 +216,58 @@ def message_edit_query(connection, author, thread_id, post_id):
return message
def message_edit_commit(connection, author_id, thread_id, post_id, new_body):
def message_edit_commit(
connection,
author_id,
thread_id,
post_id,
new_body,
send_raw=None,
set_display=True):
"""
Attempt to commit new_body to the existing message. Touches base with
message_edit_query first. Returns the newly updated message object.
Attempt to commit new_body, and optionally send_raw (default doesnt modify),
to the existing message.
The send_raw and set_display paramter may be specified as the NoneType
to leave its old value intact. Otherwise its given value is coerced to
a boolean and is set on the message. send_raw when not explicitly specified
will keep its old value, while an unspecified set_display will set it to True.
new_body may also be a NoneType to retain its old value.
Touches base with message_edit_query first. Returns
the newly updated message object.
"""
validate([("body", new_body)])
message = message_edit_query(connection, author_id, thread_id, post_id)
message["body"] = new_body
message["edited"] = True
if new_body == None:
new_body = message["body"]
validate([("body", new_body)])
if send_raw == None:
send_raw = message["send_raw"]
else:
send_raw = bool(send_raw)
if set_display == None:
display = message["edited"]
else:
display = bool(set_display)
connection.execute("""
UPDATE messages SET
body = ?,
send_raw = ?,
edited = ?
WHERE thread_id = ?
AND post_id = ?
""", (new_body, True, thread_id, post_id))
""", (new_body, send_raw, display, thread_id, post_id))
connection.commit()
message["body"] = new_body
message["send_raw"] = send_raw
message["edited"] = display
return message

View File

@ -180,7 +180,8 @@ def apply_formatting(msg_obj, formatter):
documentation for each formatter.
"""
for x, obj in enumerate(msg_obj):
msg_obj[x]["body"] = formatter(obj["body"])
if not msg_obj[x]["send_raw"]:
msg_obj[x]["body"] = formatter(obj["body"])
return msg_obj

View File

@ -147,7 +147,8 @@ def message(
author, # string (uuid1, user.user_id)
created, # floating point unix timestamp (when reply was posted)
edited, # bool
body): # string
body, # string
send_raw): # bool
return {
"thread_id": thread_id,
@ -155,5 +156,6 @@ def message(
"author": author,
"created": created,
"edited": bool(edited),
"body": body
"body": body,
"send_raw": bool(send_raw)
}

View File

@ -27,4 +27,4 @@ def schema_values(scheme, obj):
elif scheme == "message":
return ordered_keys(obj,
"thread_id", "post_id", "author",
"created", "edited", "body")
"created", "edited", "body", "send_raw")