From bdd2242155d3192615740661ce52f6fb960d1a05 Mon Sep 17 00:00:00 2001 From: Rodney Ewing Date: Wed, 12 Jun 2013 12:02:11 -0700 Subject: [PATCH] added user upload limits --- mediagoblin/config_spec.ini | 3 +++ mediagoblin/db/migrations.py | 24 +++++++++++++++++++-- mediagoblin/db/models.py | 3 +++ mediagoblin/storage/__init__.py | 7 ++++++ mediagoblin/storage/cloudfiles.py | 6 ++++++ mediagoblin/storage/filestorage.py | 3 +++ mediagoblin/submit/views.py | 34 ++++++++++++++++++++++++++++++ mediagoblin/user_pages/views.py | 4 ++++ 8 files changed, 82 insertions(+), 2 deletions(-) diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini index 8f03509d..596e4388 100644 --- a/mediagoblin/config_spec.ini +++ b/mediagoblin/config_spec.ini @@ -75,6 +75,9 @@ theme = string() plugin_web_path = string(default="/plugin_static/") plugin_linked_assets_dir = string(default="%(here)s/user_dev/plugin_static/") +# User upload limit (in Mbs) +upload_limit = integer(default=500) + [jinja2] # Jinja2 supports more directives than the minimum required by mediagoblin. # This setting allows users creating custom templates to specify a list of diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index 374ab4c8..b3dca72c 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -425,7 +425,7 @@ class RequestToken_v0(declarative_base()): callback = Column(Unicode, nullable=False, default=u"oob") created = Column(DateTime, nullable=False, default=datetime.datetime.now) updated = Column(DateTime, nullable=False, default=datetime.datetime.now) - + class AccessToken_v0(declarative_base()): """ Model for representing the access tokens @@ -438,7 +438,7 @@ class AccessToken_v0(declarative_base()): request_token = Column(Unicode, ForeignKey(RequestToken_v0.token)) created = Column(DateTime, nullable=False, default=datetime.datetime.now) updated = Column(DateTime, nullable=False, default=datetime.datetime.now) - + class NonceTimestamp_v0(declarative_base()): """ @@ -460,3 +460,23 @@ def create_oauth1_tables(db): NonceTimestamp_v0.__table__.create(db.bind) db.commit() + + +@RegisterMigration(15, MIGRATIONS) +def upload_limits(db): + """Add user upload limit columns""" + metadata = MetaData(bind=db.bind) + + user_table = inspect_table(metadata, 'core__users') + media_entry_table = inspect_table(metadata, 'core__media_entries') + + col = Column('uploaded', Integer, default=0) + col.create(user_table) + + col = Column('upload_limit', Integer) + col.create(user_table) + + col = Column('file_size', Integer, default=0) + col.create(media_entry_table) + + db.commit() diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 9cb39ff4..697ee326 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -73,6 +73,8 @@ class User(Base, UserMixin): is_admin = Column(Boolean, default=False, nullable=False) url = Column(Unicode) bio = Column(UnicodeText) # ?? + uploaded = Column(Integer, default=0) + upload_limit = Column(Integer) ## TODO # plugin data would be in a separate model @@ -189,6 +191,7 @@ class MediaEntry(Base, MediaEntryMixin): # or use sqlalchemy.types.Enum? license = Column(Unicode) collected = Column(Integer, default=0) + file_size = Column(Integer, default=0) fail_error = Column(Unicode) fail_metadata = Column(JSONEncoded) diff --git a/mediagoblin/storage/__init__.py b/mediagoblin/storage/__init__.py index bbe134a7..51b46c07 100644 --- a/mediagoblin/storage/__init__.py +++ b/mediagoblin/storage/__init__.py @@ -191,6 +191,13 @@ class StorageInterface(object): # Copy to storage system in 4M chunks shutil.copyfileobj(source_file, dest_file, length=4*1048576) + def get_file_size(self, filepath): + """ + Return the size of the file in bytes. + """ + # Subclasses should override this method. + self.__raise_not_implemented() + ########### # Utilities diff --git a/mediagoblin/storage/cloudfiles.py b/mediagoblin/storage/cloudfiles.py index 250f06d4..47c81ad6 100644 --- a/mediagoblin/storage/cloudfiles.py +++ b/mediagoblin/storage/cloudfiles.py @@ -168,6 +168,12 @@ class CloudFilesStorage(StorageInterface): # Copy to storage system in 4096 byte chunks dest_file.send(source_file) + def get_file_size(self, filepath): + """Returns the file size in bytes""" + obj = self.container.get_object( + self._resolve_filepath(filepath)) + return obj.total_bytes + class CloudFilesStorageObjectWrapper(): """ Wrapper for python-cloudfiles's cloudfiles.storage_object.Object diff --git a/mediagoblin/storage/filestorage.py b/mediagoblin/storage/filestorage.py index 3d6e0753..29b8383b 100644 --- a/mediagoblin/storage/filestorage.py +++ b/mediagoblin/storage/filestorage.py @@ -111,3 +111,6 @@ class BasicFileStorage(StorageInterface): os.makedirs(directory) # This uses chunked copying of 16kb buffers (Py2.7): shutil.copy(filename, self.get_local_path(filepath)) + + def get_file_size(self, filepath): + return os.stat(self._resolve_filepath(filepath)).st_size diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 6bb95ecb..d5f05cb7 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -43,6 +43,20 @@ def submit_start(request): """ First view for submitting a file. """ + user = request.user + if user.upload_limit: + upload_limit = user.upload_limit + else: + upload_limit = mg_globals.app_config['upload_limit'] + + if user.uploaded >= upload_limit: + messages.add_message( + request, + messages.WARNING, + _('Sorry, you have reached your upload limit.')) + return redirect( + request, '/u/{0}'.format(user.username)) + submit_form = submit_forms.SubmitStartForm(request.form, license=request.user.license_preference) @@ -86,6 +100,26 @@ def submit_start(request): with queue_file: queue_file.write(request.files['file'].stream.read()) + # Get file size an round to 2 decimal places + file_size = request.app.queue_store.get_file_size( + entry.queued_media_file) / (1024.0 * 1024) + file_size = float('{0:.2f}'.format(file_size)) + + # Check if over upload limit + if (user.uploaded + file_size) >= upload_limit: + messages.add_message( + request, + messages.WARNING, + _('Sorry, uploading this file will put you over your' + ' upload limit.')) + return redirect( + request, '/u/{0}'.format(user.username)) + + user.uploaded = user.uploaded + file_size + user.save() + + entry.file_size = file_size + # Save now so we have this data before kicking off processing entry.save() diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 49691a29..3b411fe0 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -296,6 +296,10 @@ def media_confirm_delete(request, media): if request.method == 'POST' and form.validate(): if form.confirm.data is True: username = media.get_uploader.username + + request.user.uploaded = request.user.uploaded - media.file_size + request.user.save() + # Delete MediaEntry and all related files, comments etc. media.delete() messages.add_message( -- 2.25.1