Added message privacy, currently 4 levels

This commit is contained in:
Yusur 2019-10-10 21:14:58 +02:00
parent 09a8575068
commit 7700d4fa04
6 changed files with 651 additions and 9 deletions

94
app.py
View file

@ -11,6 +11,8 @@ __version__ = '0.4-dev'
app = Flask(__name__)
app.config.from_pyfile('config.py')
### DATABASE ###
database = SqliteDatabase(app.config['DATABASE'])
class BaseModel(Model):
@ -73,9 +75,51 @@ class Message(BaseModel):
# The text of the message.
text = TextField()
# Additional info (in JSON format)
# TODO: remove because it's dumb.
info = TextField(default='{}')
# The posted date.
pub_date = DateTimeField()
# Info about privacy of the message.
@property
def privacy(self):
try:
return MessagePrivacy.get(MessagePrivacy.message == self).value
except MessagePrivacy.DoesNotExist:
# default to public
return MSGPRV_PUBLIC
def is_visible(self, is_public_timeline=False):
user = self.user
cur_user = get_current_user()
privacy = self.privacy
if user == cur_user:
# short path
return True
elif privacy == MSGPRV_PUBLIC:
return True
elif privacy == MSGPRV_UNLISTED:
# TODO user's posts may appear the same in public timeline,
# even if unlisted
return not is_public_timeline
elif privacy == MSGPRV_FRIENDS:
if cur_user is None:
return False
return user.is_following(cur_user) and cur_user.is_following(user)
else:
return False
# The message privacy values.
MSGPRV_PUBLIC = 0 # everyone
MSGPRV_UNLISTED = 1 # everyone, doesn't show up in public timeline
MSGPRV_FRIENDS = 2 # only accounts which follow each other
MSGPRV_ONLYME = 3 # only the poster
# Doing it into a separate table to don't worry about schema change.
# Added in v0.4.
class MessagePrivacy(BaseModel):
# The message.
message = ForeignKeyField(Message, primary_key=True)
# The privacy value. Needs to be one of these above.
value = IntegerField()
# this model contains two foreign keys to user -- it essentially allows us to
# model a "many-to-many" relationship between users. by querying and joining
@ -112,10 +156,13 @@ class Notification(BaseModel):
def create_tables():
with database:
database.create_tables([User, Message, Relationship, Upload, Notification])
database.create_tables([
User, Message, Relationship, Upload, Notification, MessagePrivacy])
if not os.path.isdir(UPLOAD_DIRECTORY):
os.makedirs(UPLOAD_DIRECTORY)
### UTILS ###
_forbidden_extensions = 'com net org txt'.split()
_username_characters = frozenset(string.ascii_letters + string.digits + '_')
@ -180,6 +227,35 @@ def get_object_or_404(model, *expressions):
except model.DoesNotExist:
abort(404)
class Visibility(object):
'''
Workaround for the visibility problem for posts.
Cannot be directly resolved with filter().
TODO find a better solution, this seems to be too slow.
'''
def __init__(self, query, is_public_timeline=False):
self.query = query
self.is_public_timeline = is_public_timeline
def __iter__(self):
for i in self.query:
if i.is_visible(self.is_public_timeline):
yield i
def count(self):
counter = 0
for i in self.query:
if i.is_visible(self.is_public_timeline):
counter += 1
return counter
def paginate(self, page):
counter = 0
pages_no = range((page - 1) * 20, page * 20)
for i in self.query:
if i.is_visible(self.is_public_timeline):
if counter in pages_no:
yield i
counter += 1
# flask provides a "session" object, which allows us to store information across
# requests (stored by default in a secure cookie). this function allows us to
# mark a user as being logged-in by setting some values in the session data:
@ -267,22 +343,23 @@ def homepage():
return render_template('homepage.html')
def private_timeline():
# the private timeline exemplifies the use of a subquery -- we are asking for
# the private timeline (aka feed) exemplifies the use of a subquery -- we are asking for
# messages where the person who created the message is someone the current
# user is following. these messages are then ordered newest-first.
user = get_current_user()
messages = (Message
messages = Visibility(Message
.select()
.where((Message.user << user.following())
| (Message.user == user))
.order_by(Message.pub_date.desc()))
# TODO change to "feed.html"
return object_list('private_messages.html', messages, 'message_list')
@app.route('/explore/')
def public_timeline():
messages = (Message
messages = Visibility(Message
.select()
.order_by(Message.pub_date.desc()))
.order_by(Message.pub_date.desc()), True)
return object_list('explore.html', messages, 'message_list')
@app.route('/signup/', methods=['GET', 'POST'])
@ -345,7 +422,7 @@ def user_detail(username):
# get all the users messages ordered newest-first -- note how we're accessing
# the messages -- user.message_set. could also have written it as:
# Message.select().where(Message.user == user)
messages = user.messages.order_by(Message.pub_date.desc())
messages = Visibility(user.messages.order_by(Message.pub_date.desc()), True)
return object_list('user_detail.html', messages, 'message_list', user=user)
@app.route('/+<username>/follow/', methods=['POST'])
@ -364,6 +441,7 @@ def user_follow(username):
flash('You are following %s' % user.username)
push_notification('follow', user, user=cur_user.id)
# TODO change to "profile.html"
return redirect(url_for('user_detail', username=user.username))
@app.route('/+<username>/unfollow/', methods=['POST'])
@ -392,6 +470,10 @@ def create():
user=user,
text=request.form['text'],
pub_date=datetime.datetime.now())
MessagePrivacy.create(
message=message,
value=request.form.get('privacy', '0')
)
file = request.files.get('file')
if file:
print('Uploading', file.filename)