messages can now have toggleable formatting at the database level
parent
662f9c3b70
commit
fa9cc49337
|
@ -550,6 +550,21 @@ class BBJ(object):
|
||||||
return response["data"]
|
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):
|
def user_is_admin(self, user_name_or_id):
|
||||||
"""
|
"""
|
||||||
Return boolean True or False whether the given user identifier
|
Return boolean True or False whether the given user identifier
|
||||||
|
|
|
@ -500,6 +500,13 @@ class App(object):
|
||||||
width=30, height=6)
|
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):
|
def on_post(self, button, message):
|
||||||
quotes = self.get_quotes(message)
|
quotes = self.get_quotes(message)
|
||||||
author = self.usermap[message["author"]]
|
author = self.usermap[message["author"]]
|
||||||
|
@ -520,7 +527,11 @@ class App(object):
|
||||||
msg = "Thread"
|
msg = "Thread"
|
||||||
else: msg = "Post"
|
else: msg = "Post"
|
||||||
|
|
||||||
|
raw = message["send_raw"]
|
||||||
buttons.insert(0, urwid.Button("Delete %s" % msg, self.deletion_dialog, message))
|
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))
|
buttons.insert(0, urwid.Button("Edit Post", self.edit_post, message))
|
||||||
|
|
||||||
if not buttons:
|
if not buttons:
|
||||||
|
@ -550,6 +561,8 @@ class App(object):
|
||||||
but can be passed `str` for strings.
|
but can be passed `str` for strings.
|
||||||
"""
|
"""
|
||||||
quotes = []
|
quotes = []
|
||||||
|
if msg_object["send_raw"]:
|
||||||
|
return quotes
|
||||||
for paragraph in msg_object["body"]:
|
for paragraph in msg_object["body"]:
|
||||||
# yes python is lisp fuck you
|
# yes python is lisp fuck you
|
||||||
[quotes.append(cdr) for car, cdr in paragraph if car == "quote"]
|
[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.
|
An urwid.Text object that works with the BBJ formatting directives.
|
||||||
"""
|
"""
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
|
if message["send_raw"]:
|
||||||
|
return super(MessageBody, self).__init__(message["body"])
|
||||||
|
|
||||||
text_objects = message["body"]
|
text_objects = message["body"]
|
||||||
result = []
|
result = []
|
||||||
last_directive = None
|
last_directive = None
|
||||||
|
|
|
@ -32,5 +32,6 @@ create table messages (
|
||||||
author text, -- string (uuid1, user.user_id)
|
author text, -- string (uuid1, user.user_id)
|
||||||
created real, -- floating point unix timestamp (when reply was posted)
|
created real, -- floating point unix timestamp (when reply was posted)
|
||||||
edited int, -- bool
|
edited int, -- bool
|
||||||
body text -- string
|
body text, -- string
|
||||||
|
send_raw int -- bool (1/true == never apply formatting)
|
||||||
);
|
);
|
||||||
|
|
57
server.py
57
server.py
|
@ -214,11 +214,15 @@ class API(object):
|
||||||
def thread_create(self, args, database, user, **kwargs):
|
def thread_create(self, args, database, user, **kwargs):
|
||||||
"""
|
"""
|
||||||
Creates a new thread and returns it. Requires the non-empty
|
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"])
|
validate(args, ["body", "title"])
|
||||||
thread = db.thread_create(
|
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 = \
|
cherrypy.thread_data.usermap = \
|
||||||
create_usermap(database, thread["messages"])
|
create_usermap(database, thread["messages"])
|
||||||
return thread
|
return thread
|
||||||
|
@ -229,10 +233,14 @@ class API(object):
|
||||||
"""
|
"""
|
||||||
Creates a new reply for the given thread and returns it.
|
Creates a new reply for the given thread and returns it.
|
||||||
Requires the string arguments `thread_id` and `body`
|
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"])
|
validate(args, ["thread_id", "body"])
|
||||||
return db.thread_reply(
|
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
|
@api_method
|
||||||
|
@ -266,13 +274,19 @@ class API(object):
|
||||||
of a post without actually attempting to replace it, use
|
of a post without actually attempting to replace it, use
|
||||||
`edit_query` first.
|
`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.
|
Returns the new message object.
|
||||||
"""
|
"""
|
||||||
if user == db.anon:
|
if user == db.anon:
|
||||||
raise BBJUserError("Anons cannot edit messages.")
|
raise BBJUserError("Anons cannot edit messages.")
|
||||||
validate(args, ["body", "thread_id", "post_id"])
|
validate(args, ["body", "thread_id", "post_id"])
|
||||||
return db.message_edit_commit(
|
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
|
@api_method
|
||||||
|
@ -286,6 +300,8 @@ class API(object):
|
||||||
or have admin rights. The same error descriptions and code
|
or have admin rights. The same error descriptions and code
|
||||||
are returned on falilure. Boolean true is returned on
|
are returned on falilure. Boolean true is returned on
|
||||||
success.
|
success.
|
||||||
|
|
||||||
|
If the post_id is 0, the whole thread is deleted.
|
||||||
"""
|
"""
|
||||||
if user == db.anon:
|
if user == db.anon:
|
||||||
raise BBJUserError("Anons cannot delete messages.")
|
raise BBJUserError("Anons cannot delete messages.")
|
||||||
|
@ -294,6 +310,31 @@ class API(object):
|
||||||
database, user["user_id"], args["thread_id"], args["post_id"])
|
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
|
@api_method
|
||||||
def is_admin(self, args, database, user, **kwargs):
|
def is_admin(self, args, database, user, **kwargs):
|
||||||
|
@ -343,17 +384,17 @@ class API(object):
|
||||||
@api_method
|
@api_method
|
||||||
def set_thread_pin(self, args, database, user, **kwargs):
|
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.
|
must be a boolean of what the pinned status should be.
|
||||||
This method requires that the caller is logged in and
|
This method requires that the caller is logged in and
|
||||||
has admin status on their account.
|
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"]:
|
if not user["is_admin"]:
|
||||||
raise BBJUserError("Only admins can set thread pins")
|
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
|
@api_method
|
||||||
|
|
66
src/db.py
66
src/db.py
|
@ -93,7 +93,7 @@ def thread_set_pin(connection, thread_id, pin_bool):
|
||||||
return 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.
|
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
|
# the thread is initially commited with reply_count -1 so that i can
|
||||||
# just pass the message to the reply method, instead of duplicating
|
# just pass the message to the reply method, instead of duplicating
|
||||||
# its code here. It then increments to 0.
|
# 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
|
# 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
|
# objects, just to be 100% sure what is returned is what was committed
|
||||||
return thread_get(connection, thread_id)
|
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.
|
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.
|
This is to keep post_id 0 in exact parity with its parent thread.
|
||||||
"""
|
"""
|
||||||
validate([("body", body)])
|
validate([("body", body)])
|
||||||
|
|
||||||
now = time_override or time()
|
now = time_override or time()
|
||||||
thread = thread_get(connection, thread_id, messages=False)
|
thread = thread_get(connection, thread_id, messages=False)
|
||||||
count = thread["reply_count"] + 1
|
thread["reply_count"] += 1
|
||||||
|
count = thread["reply_count"]
|
||||||
scheme = schema.message(
|
scheme = schema.message(
|
||||||
thread_id, count, author_id,
|
thread_id, count, author_id,
|
||||||
now, False, body)
|
now, False, body, bool(send_raw))
|
||||||
|
|
||||||
connection.execute("""
|
connection.execute("""
|
||||||
INSERT INTO messages
|
INSERT INTO messages
|
||||||
VALUES (?,?,?,?,?,?)
|
VALUES (?,?,?,?,?,?,?)
|
||||||
""", schema_values("message", scheme))
|
""", schema_values("message", scheme))
|
||||||
|
|
||||||
connection.execute("""
|
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):
|
def message_delete(connection, author, thread_id, post_id):
|
||||||
"""
|
"""
|
||||||
'Delete' a message from a thread. If the message being
|
'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.
|
Requires an author id, the thread_id, and post_id.
|
||||||
The same rules for edits apply to deletions: the same
|
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
|
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
|
Attempt to commit new_body, and optionally send_raw (default doesnt modify),
|
||||||
message_edit_query first. Returns the newly updated message object.
|
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 = 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("""
|
connection.execute("""
|
||||||
UPDATE messages SET
|
UPDATE messages SET
|
||||||
body = ?,
|
body = ?,
|
||||||
|
send_raw = ?,
|
||||||
edited = ?
|
edited = ?
|
||||||
WHERE thread_id = ?
|
WHERE thread_id = ?
|
||||||
AND post_id = ?
|
AND post_id = ?
|
||||||
""", (new_body, True, thread_id, post_id))
|
""", (new_body, send_raw, display, thread_id, post_id))
|
||||||
|
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|
||||||
|
message["body"] = new_body
|
||||||
|
message["send_raw"] = send_raw
|
||||||
|
message["edited"] = display
|
||||||
|
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,8 @@ def apply_formatting(msg_obj, formatter):
|
||||||
documentation for each formatter.
|
documentation for each formatter.
|
||||||
"""
|
"""
|
||||||
for x, obj in enumerate(msg_obj):
|
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
|
return msg_obj
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,8 @@ def message(
|
||||||
author, # string (uuid1, user.user_id)
|
author, # string (uuid1, user.user_id)
|
||||||
created, # floating point unix timestamp (when reply was posted)
|
created, # floating point unix timestamp (when reply was posted)
|
||||||
edited, # bool
|
edited, # bool
|
||||||
body): # string
|
body, # string
|
||||||
|
send_raw): # bool
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"thread_id": thread_id,
|
"thread_id": thread_id,
|
||||||
|
@ -155,5 +156,6 @@ def message(
|
||||||
"author": author,
|
"author": author,
|
||||||
"created": created,
|
"created": created,
|
||||||
"edited": bool(edited),
|
"edited": bool(edited),
|
||||||
"body": body
|
"body": body,
|
||||||
|
"send_raw": bool(send_raw)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,4 @@ def schema_values(scheme, obj):
|
||||||
elif scheme == "message":
|
elif scheme == "message":
|
||||||
return ordered_keys(obj,
|
return ordered_keys(obj,
|
||||||
"thread_id", "post_id", "author",
|
"thread_id", "post_id", "author",
|
||||||
"created", "edited", "body")
|
"created", "edited", "body", "send_raw")
|
||||||
|
|
Loading…
Reference in New Issue