You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2019/07/18 18:56:14 UTC

[airavata-django-portal] branch master updated (16926e2 -> 89794ac)

This is an automated email from the ASF dual-hosted git repository.

machristie pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git.


    from 16926e2  AIRAVATA-3068 Don't show owner as a member that can be added to group
     new b1c9740  Show new user message when created in last week and no access to run any apps
     new d66982b  Show as disabled applications that can't be executed
     new 4eabaf9  Applications sub heading on dashboard
     new af63d17  AIRAVATA-3144 Max file upload limit
     new 89794ac  Remove unused import

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 django_airavata/apps/api/serializers.py            |   4 +
 .../api/static/django_airavata_api/js/index.js     |   4 +-
 .../django_airavata_api/js/models/Settings.js      |   9 ++
 .../django_airavata_api/js/service_config.js       |  19 +++-
 django_airavata/apps/api/urls.py                   |   3 +-
 django_airavata/apps/api/views.py                  |  22 +++-
 .../experiment/input-editors/FileInputEditor.vue   |  78 +++++++++++--
 .../js/containers/DashboardContainer.vue           | 121 ++++++++++++++++-----
 .../js/containers/UserStorageContainer.vue         |  77 ++++++++++---
 django_airavata/settings.py                        |   5 +
 .../common/js/components/ApplicationCard.vue       |  13 ++-
 django_airavata/uploadhandler.py                   |  22 ++++
 12 files changed, 309 insertions(+), 68 deletions(-)
 create mode 100644 django_airavata/apps/api/static/django_airavata_api/js/models/Settings.js
 create mode 100644 django_airavata/uploadhandler.py


[airavata-django-portal] 01/05: Show new user message when created in last week and no access to run any apps

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit b1c97409054fd59c68a951e6426b9ff035a23f48
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Wed Jul 17 16:06:58 2019 -0400

    Show new user message when created in last week and no access to run any apps
---
 .../api/static/django_airavata_api/js/index.js     |   2 +-
 .../django_airavata_api/js/service_config.js       |   2 +-
 django_airavata/apps/api/views.py                  |   9 +-
 .../js/containers/DashboardContainer.vue           | 103 ++++++++++++++-------
 4 files changed, 81 insertions(+), 35 deletions(-)

diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 1576fe3..3f019eb 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -135,7 +135,7 @@ const services = {
   UnverifiedEmailUserProfileService: ServiceFactory.service(
     "UnverifiedEmailUsers"
   ),
-  UserProfileService,
+  UserProfileService: ServiceFactory.service("UserProfiles"),
   UserStoragePathService: ServiceFactory.service("UserStoragePaths"),
   WorkspacePreferencesService: ServiceFactory.service("WorkspacePreferences")
 };
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index 94e1c9f..1855a75 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -329,7 +329,7 @@ export default {
   },
   UserProfiles: {
     url: "/api/user-profiles",
-    viewSet: ["list"],
+    viewSet: ["list", "retrieve"],
     modelClass: UserProfile
   },
   UserStoragePaths: {
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 90c0247..1ae5f87 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -906,7 +906,9 @@ def delete_file(request):
         raise Http404(str(e)) from e
 
 
-class UserProfileViewSet(mixins.ListModelMixin, GenericAPIBackedViewSet):
+class UserProfileViewSet(mixins.RetrieveModelMixin,
+                         mixins.ListModelMixin,
+                         GenericAPIBackedViewSet):
     serializer_class = serializers.UserProfileSerializer
 
     def get_list(self):
@@ -914,6 +916,11 @@ class UserProfileViewSet(mixins.ListModelMixin, GenericAPIBackedViewSet):
         return user_profile_client.getAllUserProfilesInGateway(
             self.authz_token, self.gateway_id, 0, -1)
 
+    def get_instance(self, lookup_value):
+        user_profile_client = self.request.profile_service['user_profile']
+        return user_profile_client.getUserProfileById(
+            self.authz_token, self.request.user.username, self.gateway_id)
+
 
 class GroupResourceProfileViewSet(APIBackedViewSet):
     serializer_class = serializers.GroupResourceProfileSerializer
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
index 5347df4..b3ec934 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
@@ -1,43 +1,82 @@
 <template>
-    <div>
-        <div class="row">
-            <div class="col">
-                <h1 class="h4 mb-4">Dashboard</h1>
-            </div>
-        </div>
-        <div class="row">
-            <application-card v-for="item in applicationModules" v-bind:appModule="item"
-                v-bind:key="item.appModuleId" @app-selected="handleAppSelected">
-            </application-card>
-        </div>
-
+  <div>
+    <div class="row">
+      <div class="col">
+        <h1 class="h4 mb-4">Dashboard</h1>
+      </div>
+    </div>
+    <div
+      class="row"
+      v-if="showNewUserMessage"
+    >
+      <div class="col">
+        <b-alert
+          variant="info"
+          show
+        >Welcome {{ userProfile.firstName }} {{ userProfile.lastName }}!
+          You currently don't have access to run any applications but the
+          administrator of this gateway has been notified and will be in
+          contact to grant you the appropriate privileges.</b-alert>
+      </div>
     </div>
+    <div class="row">
+      <application-card
+        v-for="item in applicationModules"
+        v-bind:appModule="item"
+        v-bind:key="item.appModuleId"
+        @app-selected="handleAppSelected"
+      >
+      </application-card>
+    </div>
+
+  </div>
 </template>
 
 <script>
-
-import { services } from 'django-airavata-api'
-import { components as comps } from 'django-airavata-common-ui'
+import { services, session } from "django-airavata-api";
+import { components as comps } from "django-airavata-common-ui";
 import urls from "../utils/urls";
 
 export default {
-    name: 'dashboard-container',
-    data () {
-        return {
-            applicationModules: null,
-        }
-    },
-    components: {
-        'application-card': comps.ApplicationCard,
-    },
-    methods: {
-        handleAppSelected: function(appModule) {
-            urls.navigateToCreateExperiment(appModule);
-        },
+  name: "dashboard-container",
+  data() {
+    return {
+      applicationModules: null,
+      userProfile: null
+    };
+  },
+  components: {
+    "application-card": comps.ApplicationCard
+  },
+  methods: {
+    handleAppSelected: function(appModule) {
+      urls.navigateToCreateExperiment(appModule);
+    }
+  },
+  computed: {
+    isNewUser() {
+      return (
+        this.userProfile &&
+        Date.now() - this.userProfile.creationTime.getTime() <
+          7 * 24 * 60 * 60 * 1000
+      );
     },
-    beforeMount: function () {
-        services.ApplicationModuleService.list()
-            .then(result => this.applicationModules = result);
+    showNewUserMessage() {
+      return (
+        this.isNewUser &&
+        this.userProfile &&
+        this.applicationModules &&
+        this.applicationModules.length === 0
+      );
     }
-}
+  },
+  beforeMount: function() {
+    services.ApplicationModuleService.list().then(
+      result => (this.applicationModules = result)
+    );
+    services.UserProfileService.retrieve({
+      lookup: session.Session.username
+    }).then(userProfile => (this.userProfile = userProfile));
+  }
+};
 </script>


[airavata-django-portal] 02/05: Show as disabled applications that can't be executed

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit d66982b38441d51ffa9738ab57047d53f88383bf
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Wed Jul 17 16:33:52 2019 -0400

    Show as disabled applications that can't be executed
---
 .../js/containers/DashboardContainer.vue           | 37 +++++++++++++++++-----
 .../common/js/components/ApplicationCard.vue       | 13 +++++---
 2 files changed, 38 insertions(+), 12 deletions(-)

diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
index b3ec934..cdbc8af 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
@@ -21,13 +21,15 @@
     </div>
     <div class="row">
       <application-card
-        v-for="item in applicationModules"
-        v-bind:appModule="item"
-        v-bind:key="item.appModuleId"
+        v-for="item in allApplicationData"
+        v-bind:appModule="item.appModule"
+        v-bind:key="item.appModule.appModuleId"
         @app-selected="handleAppSelected"
+        :disabled="item.disabled"
       >
       </application-card>
     </div>
+    </div>
 
   </div>
 </template>
@@ -41,8 +43,9 @@ export default {
   name: "dashboard-container",
   data() {
     return {
-      applicationModules: null,
-      userProfile: null
+      accessibleAppModules: null,
+      userProfile: null,
+      allApplicationModules: null
     };
   },
   components: {
@@ -65,18 +68,36 @@ export default {
       return (
         this.isNewUser &&
         this.userProfile &&
-        this.applicationModules &&
-        this.applicationModules.length === 0
+        this.accessibleAppModules.length === 0
       );
+    },
+    accessibleModuleIds() {
+      return this.accessibleAppModules
+        ? this.accessibleAppModules.map(a => a.appModuleId)
+        : [];
+    },
+    allApplicationData() {
+      return this.allApplicationModules
+        ? this.allApplicationModules.map(app => {
+            return {
+              appModule: app,
+              disabled: this.accessibleModuleIds.indexOf(app.appModuleId) < 0
+            };
+          })
+        : [];
     }
   },
   beforeMount: function() {
     services.ApplicationModuleService.list().then(
-      result => (this.applicationModules = result)
+      result => (this.accessibleAppModules = result)
     );
     services.UserProfileService.retrieve({
       lookup: session.Session.username
     }).then(userProfile => (this.userProfile = userProfile));
+    // Load all application, including ones that aren't accessible by this user
+    services.ApplicationModuleService.listAll().then(
+      result => (this.allApplicationModules = result)
+    );
   }
 };
 </script>
diff --git a/django_airavata/static/common/js/components/ApplicationCard.vue b/django_airavata/static/common/js/components/ApplicationCard.vue
index 531b931..5e2e4d8 100644
--- a/django_airavata/static/common/js/components/ApplicationCard.vue
+++ b/django_airavata/static/common/js/components/ApplicationCard.vue
@@ -1,14 +1,14 @@
 <template>
     <div class="col-md-6 col-xl-4">
-        <div class="card application-card">
-            <a href="#" class="card-link text-dark" @click.prevent="handleAppClick">
+        <div class="card application-card" :class="cardClasses">
+            <b-link :disabled="disabled" class="card-link text-dark" @click.prevent="handleAppClick">
                 <div class="card-body"  >
                     <h2 class="card-title h5">{{appModule.appModuleName}}</h2>
                     <span class="badge badge-primary mr-1" v-for="tag in appModule.tags" :key="tag">{{tag}}</span>
                     <span class="badge badge-primary mr-1" v-if="appModule.appModuleVersion" >{{appModule.appModuleVersion}}</span>
                     <p class="card-text card-text--small mt-3 text-secondary">{{appModule.appModuleDescription}}</p>
                 </div>
-            </a>
+            </b-link>
         </div>
     </div>
 </template>
@@ -16,7 +16,7 @@
 <script>
 export default {
     name: 'application-card',
-    props: ['appModule'],
+    props: ['appModule', 'disabled'],
     data:function () {
         return {
         };
@@ -25,6 +25,11 @@ export default {
         handleAppClick: function() {
             this.$emit('app-selected', this.appModule);
         }
+    },
+    computed: {
+      cardClasses() {
+        return this.disabled ? ['is-disabled'] : [];
+      },
     }
 }
 </script>


[airavata-django-portal] 03/05: Applications sub heading on dashboard

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 4eabaf99b75074f004dd32fcdc3a8154c151a4d8
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Wed Jul 17 16:38:47 2019 -0400

    Applications sub heading on dashboard
---
 .../django_airavata_workspace/js/containers/DashboardContainer.vue       | 1 +
 1 file changed, 1 insertion(+)

diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
index cdbc8af..b833d23 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/DashboardContainer.vue
@@ -3,6 +3,7 @@
     <div class="row">
       <div class="col">
         <h1 class="h4 mb-4">Dashboard</h1>
+        <h2 class="h6 mb-2 text-uppercase text-muted">Applications</h2>
       </div>
     </div>
     <div


[airavata-django-portal] 05/05: Remove unused import

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 89794acad01170ce2539aa01b57ee1e3339680c3
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Jul 18 14:53:21 2019 -0400

    Remove unused import
---
 django_airavata/apps/api/static/django_airavata_api/js/index.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index d4cc2c4..2cc852a 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -37,7 +37,6 @@ import SummaryType from "./models/SummaryType";
 import UserPermission from "./models/UserPermission";
 
 
-import UserProfileService from "./services/UserProfileService";
 import CloudJobSubmissionService from "./services/CloudJobSubmissionService";
 import GlobusJobSubmissionService from "./services/GlobusJobSubmissionService";
 import LocaJobSubmissionService from "./services/LocaJobSubmissionService";


[airavata-django-portal] 04/05: AIRAVATA-3144 Max file upload limit

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit af63d170dda382e014e6f9b446185789c2464ca9
Author: Marcus Christie <ma...@apache.org>
AuthorDate: Thu Jul 18 14:53:09 2019 -0400

    AIRAVATA-3144 Max file upload limit
---
 django_airavata/apps/api/serializers.py            |  4 ++
 .../api/static/django_airavata_api/js/index.js     |  1 +
 .../django_airavata_api/js/models/Settings.js      |  9 +++
 .../django_airavata_api/js/service_config.js       | 17 ++++-
 django_airavata/apps/api/urls.py                   |  3 +-
 django_airavata/apps/api/views.py                  | 13 ++++
 .../experiment/input-editors/FileInputEditor.vue   | 78 +++++++++++++++++++---
 .../js/containers/UserStorageContainer.vue         | 77 ++++++++++++++++-----
 django_airavata/settings.py                        |  5 ++
 django_airavata/uploadhandler.py                   | 22 ++++++
 10 files changed, 199 insertions(+), 30 deletions(-)

diff --git a/django_airavata/apps/api/serializers.py b/django_airavata/apps/api/serializers.py
index f7143b7..e9e4ade 100644
--- a/django_airavata/apps/api/serializers.py
+++ b/django_airavata/apps/api/serializers.py
@@ -928,3 +928,7 @@ class LogRecordSerializer(serializers.Serializer):
     message = serializers.CharField()
     details = StoredJSONField()
     stacktrace = serializers.ListField(child=serializers.CharField())
+
+
+class SettingsSerializer(serializers.Serializer):
+    fileUploadMaxFileSize = serializers.IntegerField()
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/index.js b/django_airavata/apps/api/static/django_airavata_api/js/index.js
index 3f019eb..d4cc2c4 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/index.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/index.js
@@ -126,6 +126,7 @@ const services = {
   ProjectService: ServiceFactory.service("Projects"),
   SCPDataMovementService,
   ServiceFactory,
+  SettingsService: ServiceFactory.service("Settings"),
   SharedEntityService: ServiceFactory.service("SharedEntities"),
   SshJobSubmissionService,
   StoragePreferenceService: ServiceFactory.service("StoragePreferences"),
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/models/Settings.js b/django_airavata/apps/api/static/django_airavata_api/js/models/Settings.js
new file mode 100644
index 0000000..1a5c3f3
--- /dev/null
+++ b/django_airavata/apps/api/static/django_airavata_api/js/models/Settings.js
@@ -0,0 +1,9 @@
+import BaseModel from "./BaseModel";
+
+const FIELDS = ["fileUploadMaxFileSize"];
+
+export default class Settings extends BaseModel {
+  constructor(data = {}) {
+    super(FIELDS, data);
+  }
+}
diff --git a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
index 1855a75..82261a6 100644
--- a/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
+++ b/django_airavata/apps/api/static/django_airavata_api/js/service_config.js
@@ -15,8 +15,10 @@ import Group from "./models/Group";
 import GroupResourceProfile from "./models/GroupResourceProfile";
 import IAMUserProfile from "./models/IAMUserProfile";
 import LogRecord from "./models/LogRecord";
+import Notification from "./models/Notification";
 import Parser from "./models/Parser";
 import Project from "./models/Project";
+import Settings from "./models/Settings";
 import SharedEntity from "./models/SharedEntity";
 import StoragePreference from "./models/StoragePreference";
 import StorageResourceDescription from "./models/StorageResourceDescription";
@@ -24,7 +26,6 @@ import UnverifiedEmailUserProfile from "./models/UnverifiedEmailUserProfile";
 import UserProfile from "./models/UserProfile";
 import UserStoragePath from "./models/UserStoragePath";
 import WorkspacePreferences from "./models/WorkspacePreferences";
-import Notification from "./models/Notification";
 /*
 examples:
 
@@ -259,7 +260,7 @@ export default {
     url: "/api/log",
     methods: {
       send: {
-        url: '/api/log',
+        url: "/api/log",
         requestType: "post",
         bodyParams: {
           name: "data"
@@ -289,6 +290,16 @@ export default {
     queryParams: ["limit", "offset"],
     modelClass: Project
   },
+  Settings: {
+    url: "/api/settings/",
+    methods: {
+      get: {
+        url: "/api/settings/",
+        requestType: "get",
+        modelClass: Settings
+      }
+    }
+  },
   SharedEntities: {
     url: "/api/shared-entities",
     viewSet: ["retrieve", "update"],
@@ -358,5 +369,5 @@ export default {
     viewSet: true,
     pagination: false,
     modelClass: Notification
-  },
+  }
 };
diff --git a/django_airavata/apps/api/urls.py b/django_airavata/apps/api/urls.py
index d04b87f..61d8b6d 100644
--- a/django_airavata/apps/api/urls.py
+++ b/django_airavata/apps/api/urls.py
@@ -88,7 +88,8 @@ urlpatterns = [
         name="experiment-statistics"),
     url(r'ack-notifications/<slug:id>/', views.AckNotificationViewSet.as_view(), name="ack-notifications"),
     url(r'ack-notifications/', views.AckNotificationViewSet.as_view(), name="ack-notifications"),
-    url(r'^log', views.LogRecordConsumer.as_view(), name='log')
+    url(r'^log', views.LogRecordConsumer.as_view(), name='log'),
+    url(r'^settings', views.SettingsAPIView.as_view(), name='settings'),
 ]
 
 if logger.isEnabledFor(logging.DEBUG):
diff --git a/django_airavata/apps/api/views.py b/django_airavata/apps/api/views.py
index 1ae5f87..d24a562 100644
--- a/django_airavata/apps/api/views.py
+++ b/django_airavata/apps/api/views.py
@@ -1646,3 +1646,16 @@ class LogRecordConsumer(APIView):
                         json.dumps(log_record['details'], indent=4),
                         stacktrace))
         return Response(serializer.data)
+
+
+class SettingsAPIView(APIView):
+    serializer_class = serializers.SettingsSerializer
+
+    def get(self, request, format=None):
+        data = {
+            'fileUploadMaxFileSize': settings.FILE_UPLOAD_MAX_FILE_SIZE
+        }
+        serializer = self.serializer_class(
+            data, context={'request': request})
+        return Response(serializer.data)
+
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
index 5446dcd..6aec649 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/experiment/input-editors/FileInputEditor.vue
@@ -39,16 +39,26 @@
       class="d-flex align-items-baseline"
       v-if="!isSelectingFile && !isDataProductURI"
     >
-      <b-button @click="isSelectingFile=true" class="input-file-option">Select file from storage</b-button>
+      <b-button
+        @click="isSelectingFile=true"
+        class="input-file-option"
+      >Select file from storage</b-button>
       <span class="text-muted mx-3">OR</span>
-      <b-form-file
-        :id="id"
-        v-model="file"
-        v-if="!isDataProductURI"
-        placeholder="Upload file"
-        @input="fileChanged"
+      <b-form-group
+        :description="maxFileUploadSizeMessage"
+        :state="fileUploadState"
+        :invalid-feedback="fileUploadInvalidFeedback"
         class="input-file-option"
-      />
+      >
+        <b-form-file
+          :id="id"
+          v-model="file"
+          v-if="!isDataProductURI"
+          placeholder="Upload file"
+          @input="fileChanged"
+          :state="fileUploadState"
+        />
+      </b-form-group>
     </div>
   </div>
 </template>
@@ -70,7 +80,11 @@ export default {
   computed: {
     isDataProductURI() {
       // Just assume that if the value is a string then it's a data product URL
-      return this.value && typeof this.value === "string" && this.value.startsWith("airavata-dp://");
+      return (
+        this.value &&
+        typeof this.value === "string" &&
+        this.value.startsWith("airavata-dp://")
+      );
     },
     // When used in the MultiFileInputEditor, don't allow selecting the same
     // file more than once. This computed property creates an array of already
@@ -84,19 +98,61 @@ export default {
       } else {
         return [];
       }
+    },
+    maxFileUploadSizeMB() {
+      return this.settings
+        ? this.settings.fileUploadMaxFileSize / 1024 / 1024
+        : 0;
+    },
+    maxFileUploadSizeMessage() {
+      if (this.maxFileUploadSizeMB) {
+        return (
+          "Max file upload size is " +
+          Math.round(this.maxFileUploadSizeMB) +
+          " MB"
+        );
+      } else {
+        return null;
+      }
+    },
+    fileTooLarge() {
+      return (
+        this.settings &&
+        this.settings.fileUploadMaxFileSize &&
+        this.file &&
+        this.file.size > this.settings.fileUploadMaxFileSize
+      );
+    },
+    fileUploadState() {
+      if (this.fileTooLarge) {
+        return false;
+      } else {
+        return null;
+      }
+    },
+    fileUploadInvalidFeedback() {
+      if (this.fileTooLarge) {
+        return (
+          "File selected is larger than " + this.maxFileUploadSizeMB + " MB"
+        );
+      } else {
+        return null;
+      }
     }
   },
   data() {
     return {
       dataProduct: null,
       file: null,
-      isSelectingFile: false
+      isSelectingFile: false,
+      settings: null
     };
   },
   created() {
     if (this.isDataProductURI) {
       this.loadDataProduct(this.value);
     }
+    services.SettingsService.get().then(s => (this.settings = s));
   },
   methods: {
     loadDataProduct(dataProductURI) {
@@ -126,7 +182,7 @@ export default {
         .catch(utils.FetchUtils.reportError);
     },
     fileChanged() {
-      if (this.file) {
+      if (this.file && !this.fileTooLarge) {
         let data = new FormData();
         data.append("file", this.file);
         this.$emit("uploadstart");
diff --git a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
index 7e39fd2..567de2e 100644
--- a/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
+++ b/django_airavata/apps/workspace/static/django_airavata_workspace/js/containers/UserStorageContainer.vue
@@ -12,13 +12,20 @@
     </div>
     <div class="row">
       <div class="col">
-        <b-form-file
-          v-model="file"
-          ref="file-input"
-          placeholder="Add file"
-          @input="fileChanged"
-          class="mb-2"
-        ></b-form-file>
+        <b-form-group
+          :description="maxFileUploadSizeMessage"
+          :state="fileUploadState"
+          :invalid-feedback="fileUploadInvalidFeedback"
+        >
+          <b-form-file
+            v-model="file"
+            ref="file-input"
+            placeholder="Add file"
+            @input="fileChanged"
+            class="mb-2"
+            :state="fileUploadState"
+          ></b-form-file>
+        </b-form-group>
       </div>
       <div class="col">
         <b-input-group>
@@ -67,13 +74,54 @@ export default {
     },
     username() {
       return session.Session.username;
+    },
+    maxFileUploadSizeMB() {
+      return this.settings
+        ? this.settings.fileUploadMaxFileSize / 1024 / 1024
+        : 0;
+    },
+    maxFileUploadSizeMessage() {
+      if (this.maxFileUploadSizeMB) {
+        return (
+          "Max file upload size is " +
+          Math.round(this.maxFileUploadSizeMB) +
+          " MB"
+        );
+      } else {
+        return null;
+      }
+    },
+    fileTooLarge() {
+      return (
+        this.settings &&
+        this.settings.fileUploadMaxFileSize &&
+        this.file &&
+        this.file.size > this.settings.fileUploadMaxFileSize
+      );
+    },
+    fileUploadState() {
+      if (this.fileTooLarge) {
+        return false;
+      } else {
+        return null;
+      }
+    },
+    fileUploadInvalidFeedback() {
+      if (this.fileTooLarge) {
+        return (
+          "File selected is larger than " + this.maxFileUploadSizeMB + " MB"
+        );
+      } else {
+        return null;
+      }
     }
   },
   data() {
     return {
       userStoragePath: null,
       file: null,
-      dirName: null
+      dirName: null,
+      settings: null
     };
   },
   methods: {
@@ -105,7 +153,7 @@ export default {
       );
     },
     fileChanged() {
-      if (this.file) {
+      if (this.file && !this.fileTooLarge) {
         let data = new FormData();
         data.append("file", this.file);
         utils.FetchUtils.post(
@@ -125,12 +173,10 @@ export default {
           newDirPath = newDirPath + "/";
         }
         newDirPath = newDirPath + this.dirName;
-        utils.FetchUtils.post("/api/user-storage/" + newDirPath).then(
-         () => {
-            this.dirName = null;
-            this.loadUserStoragePath(this.storagePath);
-          }
-        );
+        utils.FetchUtils.post("/api/user-storage/" + newDirPath).then(() => {
+          this.dirName = null;
+          this.loadUserStoragePath(this.storagePath);
+        });
       }
     },
     deleteDir(path) {
@@ -156,6 +202,7 @@ export default {
     } else {
       this.loadUserStoragePath(this.storagePath);
     }
+    services.SettingsService.get().then(s => (this.settings = s));
   },
   watch: {
     $route() {
diff --git a/django_airavata/settings.py b/django_airavata/settings.py
index d8bb59e..a72317a 100644
--- a/django_airavata/settings.py
+++ b/django_airavata/settings.py
@@ -214,6 +214,11 @@ MEDIA_URL = '/media/'
 
 # Data storage
 FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o777
+FILE_UPLOAD_MAX_FILE_SIZE = 64 * 1024 * 1024  # 64 MB
+FILE_UPLOAD_HANDLERS = [
+    'django.core.files.uploadhandler.MemoryFileUploadHandler',
+    'django_airavata.uploadhandler.MaxFileSizeTemporaryFileUploadHandler',
+]
 
 # Django REST Framework configuration
 REST_FRAMEWORK = {
diff --git a/django_airavata/uploadhandler.py b/django_airavata/uploadhandler.py
new file mode 100644
index 0000000..dfa28a5
--- /dev/null
+++ b/django_airavata/uploadhandler.py
@@ -0,0 +1,22 @@
+from django.conf import settings
+from django.core.files.uploadhandler import (
+    StopUpload,
+    TemporaryFileUploadHandler
+)
+
+
+class MaxFileSizeTemporaryFileUploadHandler(TemporaryFileUploadHandler):
+
+    def handle_raw_input(self,
+                         input_data,
+                         META,
+                         content_length,
+                         boundary,
+                         encoding=None):
+        """
+        Use the content_length to enforce max size limit.
+        """
+        # Check the content-length header to see if we should
+        # If the post is too large, we cannot use the Memory handler.
+        if content_length > settings.FILE_UPLOAD_MAX_FILE_SIZE:
+            raise StopUpload