added user upload limits
authorRodney Ewing <ewing.rj@gmail.com>
Wed, 12 Jun 2013 19:02:11 +0000 (12:02 -0700)
committerRodney Ewing <ewing.rj@gmail.com>
Mon, 26 Aug 2013 13:33:28 +0000 (06:33 -0700)
mediagoblin/config_spec.ini
mediagoblin/db/migrations.py
mediagoblin/db/models.py
mediagoblin/storage/__init__.py
mediagoblin/storage/cloudfiles.py
mediagoblin/storage/filestorage.py
mediagoblin/submit/views.py
mediagoblin/user_pages/views.py

index 8f03509d0aed255ae901bd5bdb8ed636608e7e56..596e438892dc8518a98973252b2f125366a9d08b 100644 (file)
@@ -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
index 374ab4c8f6b6bbba969d2b8ccf399cdb62931207..b3dca72c3cc1539ca1969022763b8f553d63614a 100644 (file)
@@ -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()
index 9cb39ff4821c4a8e622034d3a4341d0c2febec28..697ee32624419f64e2437e7229772614fd6b9562 100644 (file)
@@ -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)
index bbe134a7bf02b39933462eb1921c7856c93fc555..51b46c07151f8bb65f9346cce2f92c47275d26f1 100644 (file)
@@ -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
index 250f06d4f554ecf85a11b5a38867b4221e7ab5dc..47c81ad6b42ff940aef51e25e29372787523c7c3 100644 (file)
@@ -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
index 3d6e0753e7798898e10c9cdf30fa314cc60c8176..29b8383b4deaea0aecac52e4b220346603921226 100644 (file)
@@ -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
index 6bb95ecb07875bc82cdaddc5675ff516440cadac..d5f05cb7fc1674a265d0e75eb82d868bca10f95a 100644 (file)
@@ -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()
 
index 49691a2990dee937dde385856400181cda7d56e3..3b411fe049714db45eac4006847bdabd79666220 100644 (file)
@@ -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(