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

linxinyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/master by this push:
     new bab934ee77 feat: disallow dataset download (#3663)
bab934ee77 is described below

commit bab934ee77ff699bb0b80eeb4396ca3611d3334f
Author: Seongjin Yoon <[email protected]>
AuthorDate: Sat Aug 16 19:28:08 2025 -0700

    feat: disallow dataset download (#3663)
    
    Feature:
    users may disallow their datasets from being downloaded.
    
    For Developers:
    Please do the following steps to incorporate with new changes:
    - Apply core/scripts/sql/updates/11.sql to your local postgres instance
    - Run the command  under core to regenerate jooq code
    ```sbt "project DAO" "runMain edu.uci.ics.texera.dao.JooqCodeGenerator"```
    
    
https://github.com/user-attachments/assets/3f16c94e-3aa5-4050-8eab-60cdaa4586ad
---
 .../uci/ics/texera/dao/jooq/generated/Keys.java    |   3 +
 .../uci/ics/texera/dao/jooq/generated/Tables.java  |   6 +
 .../ics/texera/dao/jooq/generated/TexeraDb.java    |   7 +
 .../texera/dao/jooq/generated/tables/Dataset.java  |  13 +-
 .../dao/jooq/generated/tables/SiteSettings.java    | 163 ++++++++++++
 .../jooq/generated/tables/WorkflowExecutions.java  |  12 +-
 .../dao/jooq/generated/tables/daos/DatasetDao.java |  15 ++
 .../generated/tables/daos/SiteSettingsDao.java     | 133 ++++++++++
 .../tables/daos/WorkflowExecutionsDao.java         |  30 +--
 .../jooq/generated/tables/interfaces/IDataset.java |  10 +
 .../{IDataset.java => ISiteSettings.java}          |  62 ++---
 .../tables/interfaces/IWorkflowExecutions.java     |  20 +-
 .../dao/jooq/generated/tables/pojos/Dataset.java   |  24 +-
 .../jooq/generated/tables/pojos/SiteSettings.java  | 157 ++++++++++++
 .../generated/tables/pojos/WorkflowExecutions.java |  46 ++--
 .../generated/tables/records/DatasetRecord.java    |  61 ++++-
 .../tables/records/SiteSettingsRecord.java         | 278 +++++++++++++++++++++
 .../tables/records/WorkflowExecutionsRecord.java   | 208 +++++++--------
 .../texera/service/resource/DatasetResource.scala  |  97 ++++++-
 core/gui/src/app/common/type/dataset.ts            |   1 +
 .../dataset-detail.component.html                  |  33 ++-
 .../dataset-detail.component.scss                  |  30 +++
 .../dataset-detail.component.ts                    |  45 ++++
 .../user-dataset-version-creator.component.html    |  25 +-
 .../user-dataset-version-creator.component.scss    |  24 ++
 .../user-dataset-version-creator.component.ts      |  12 +
 .../service/user/dataset/dataset.service.ts        |   9 +
 core/scripts/sql/texera_ddl.sql                    |   1 +
 core/scripts/sql/updates/11.sql                    |  28 +++
 .../k8s/texera-helmchart/files/texera_ddl.sql      |   1 +
 30 files changed, 1321 insertions(+), 233 deletions(-)

diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Keys.java 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Keys.java
index 0fbee5a7e5..b07305f249 100644
--- a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Keys.java
+++ b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Keys.java
@@ -32,6 +32,7 @@ import 
edu.uci.ics.texera.dao.jooq.generated.tables.OperatorPortExecutions;
 import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
 import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
 import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
+import edu.uci.ics.texera.dao.jooq.generated.tables.SiteSettings;
 import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
 import edu.uci.ics.texera.dao.jooq.generated.tables.User;
 import edu.uci.ics.texera.dao.jooq.generated.tables.UserConfig;
@@ -56,6 +57,7 @@ import 
edu.uci.ics.texera.dao.jooq.generated.tables.records.OperatorPortExecutio
 import edu.uci.ics.texera.dao.jooq.generated.tables.records.ProjectRecord;
 import 
edu.uci.ics.texera.dao.jooq.generated.tables.records.ProjectUserAccessRecord;
 import 
edu.uci.ics.texera.dao.jooq.generated.tables.records.PublicProjectRecord;
+import edu.uci.ics.texera.dao.jooq.generated.tables.records.SiteSettingsRecord;
 import edu.uci.ics.texera.dao.jooq.generated.tables.records.TimeLogRecord;
 import edu.uci.ics.texera.dao.jooq.generated.tables.records.UserConfigRecord;
 import edu.uci.ics.texera.dao.jooq.generated.tables.records.UserRecord;
@@ -100,6 +102,7 @@ public class Keys {
     public static final UniqueKey<ProjectRecord> PROJECT_PKEY = 
Internal.createUniqueKey(Project.PROJECT, DSL.name("project_pkey"), new 
TableField[] { Project.PROJECT.PID }, true);
     public static final UniqueKey<ProjectUserAccessRecord> 
PROJECT_USER_ACCESS_PKEY = 
Internal.createUniqueKey(ProjectUserAccess.PROJECT_USER_ACCESS, 
DSL.name("project_user_access_pkey"), new TableField[] { 
ProjectUserAccess.PROJECT_USER_ACCESS.UID, 
ProjectUserAccess.PROJECT_USER_ACCESS.PID }, true);
     public static final UniqueKey<PublicProjectRecord> PUBLIC_PROJECT_PKEY = 
Internal.createUniqueKey(PublicProject.PUBLIC_PROJECT, 
DSL.name("public_project_pkey"), new TableField[] { 
PublicProject.PUBLIC_PROJECT.PID }, true);
+    public static final UniqueKey<SiteSettingsRecord> SITE_SETTINGS_PKEY = 
Internal.createUniqueKey(SiteSettings.SITE_SETTINGS, 
DSL.name("site_settings_pkey"), new TableField[] { 
SiteSettings.SITE_SETTINGS.KEY }, true);
     public static final UniqueKey<TimeLogRecord> TIME_LOG_PKEY = 
Internal.createUniqueKey(TimeLog.TIME_LOG, DSL.name("time_log_pkey"), new 
TableField[] { TimeLog.TIME_LOG.UID }, true);
     public static final UniqueKey<UserRecord> USER_EMAIL_KEY = 
Internal.createUniqueKey(User.USER, DSL.name("user_email_key"), new 
TableField[] { User.USER.EMAIL }, true);
     public static final UniqueKey<UserRecord> USER_GOOGLE_ID_KEY = 
Internal.createUniqueKey(User.USER, DSL.name("user_google_id_key"), new 
TableField[] { User.USER.GOOGLE_ID }, true);
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Tables.java 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Tables.java
index fe590e3f0c..39af6f8f8f 100644
--- a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Tables.java
+++ b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/Tables.java
@@ -32,6 +32,7 @@ import 
edu.uci.ics.texera.dao.jooq.generated.tables.OperatorPortExecutions;
 import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
 import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
 import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
+import edu.uci.ics.texera.dao.jooq.generated.tables.SiteSettings;
 import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
 import edu.uci.ics.texera.dao.jooq.generated.tables.User;
 import edu.uci.ics.texera.dao.jooq.generated.tables.UserActivity;
@@ -109,6 +110,11 @@ public class Tables {
      */
     public static final PublicProject PUBLIC_PROJECT = 
PublicProject.PUBLIC_PROJECT;
 
+    /**
+     * The table <code>texera_db.site_settings</code>.
+     */
+    public static final SiteSettings SITE_SETTINGS = 
SiteSettings.SITE_SETTINGS;
+
     /**
      * The table <code>texera_db.time_log</code>.
      */
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/TexeraDb.java 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/TexeraDb.java
index 800becf396..269aad41f3 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/TexeraDb.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/TexeraDb.java
@@ -32,6 +32,7 @@ import 
edu.uci.ics.texera.dao.jooq.generated.tables.OperatorPortExecutions;
 import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
 import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
 import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
+import edu.uci.ics.texera.dao.jooq.generated.tables.SiteSettings;
 import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
 import edu.uci.ics.texera.dao.jooq.generated.tables.User;
 import edu.uci.ics.texera.dao.jooq.generated.tables.UserActivity;
@@ -123,6 +124,11 @@ public class TexeraDb extends SchemaImpl {
      */
     public final PublicProject PUBLIC_PROJECT = PublicProject.PUBLIC_PROJECT;
 
+    /**
+     * The table <code>texera_db.site_settings</code>.
+     */
+    public final SiteSettings SITE_SETTINGS = SiteSettings.SITE_SETTINGS;
+
     /**
      * The table <code>texera_db.time_log</code>.
      */
@@ -220,6 +226,7 @@ public class TexeraDb extends SchemaImpl {
             Project.PROJECT,
             ProjectUserAccess.PROJECT_USER_ACCESS,
             PublicProject.PUBLIC_PROJECT,
+            SiteSettings.SITE_SETTINGS,
             TimeLog.TIME_LOG,
             User.USER,
             UserActivity.USER_ACTIVITY,
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/Dataset.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/Dataset.java
index 7b2831ad11..fe6283a03f 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/Dataset.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/Dataset.java
@@ -34,7 +34,7 @@ import org.jooq.ForeignKey;
 import org.jooq.Identity;
 import org.jooq.Name;
 import org.jooq.Record;
-import org.jooq.Row6;
+import org.jooq.Row7;
 import org.jooq.Schema;
 import org.jooq.Table;
 import org.jooq.TableField;
@@ -96,6 +96,11 @@ public class Dataset extends TableImpl<DatasetRecord> {
      */
     public final TableField<DatasetRecord, Timestamp> CREATION_TIME = 
createField(DSL.name("creation_time"), 
SQLDataType.TIMESTAMP(0).nullable(false).defaultValue(DSL.field("CURRENT_TIMESTAMP",
 SQLDataType.TIMESTAMP)), this, "");
 
+    /**
+     * The column <code>texera_db.dataset.is_downloadable</code>.
+     */
+    public final TableField<DatasetRecord, Boolean> IS_DOWNLOADABLE = 
createField(DSL.name("is_downloadable"), 
SQLDataType.BOOLEAN.nullable(false).defaultValue(DSL.field("true", 
SQLDataType.BOOLEAN)), this, "");
+
     private Dataset(Name alias, Table<DatasetRecord> aliased) {
         this(alias, aliased, null);
     }
@@ -188,11 +193,11 @@ public class Dataset extends TableImpl<DatasetRecord> {
     }
 
     // 
-------------------------------------------------------------------------
-    // Row6 type methods
+    // Row7 type methods
     // 
-------------------------------------------------------------------------
 
     @Override
-    public Row6<Integer, Integer, String, Boolean, String, Timestamp> 
fieldsRow() {
-        return (Row6) super.fieldsRow();
+    public Row7<Integer, Integer, String, Boolean, String, Timestamp, Boolean> 
fieldsRow() {
+        return (Row7) super.fieldsRow();
     }
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/SiteSettings.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/SiteSettings.java
new file mode 100644
index 0000000000..7c37805c79
--- /dev/null
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/SiteSettings.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ * This file is generated by jOOQ.
+ */
+package edu.uci.ics.texera.dao.jooq.generated.tables;
+
+
+import edu.uci.ics.texera.dao.jooq.generated.Keys;
+import edu.uci.ics.texera.dao.jooq.generated.TexeraDb;
+import edu.uci.ics.texera.dao.jooq.generated.tables.records.SiteSettingsRecord;
+
+import java.sql.Timestamp;
+
+import org.jooq.Field;
+import org.jooq.ForeignKey;
+import org.jooq.Name;
+import org.jooq.Record;
+import org.jooq.Row4;
+import org.jooq.Schema;
+import org.jooq.Table;
+import org.jooq.TableField;
+import org.jooq.TableOptions;
+import org.jooq.UniqueKey;
+import org.jooq.impl.DSL;
+import org.jooq.impl.SQLDataType;
+import org.jooq.impl.TableImpl;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes" })
+public class SiteSettings extends TableImpl<SiteSettingsRecord> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * The reference instance of <code>texera_db.site_settings</code>
+     */
+    public static final SiteSettings SITE_SETTINGS = new SiteSettings();
+
+    /**
+     * The class holding records for this type
+     */
+    @Override
+    public Class<SiteSettingsRecord> getRecordType() {
+        return SiteSettingsRecord.class;
+    }
+
+    /**
+     * The column <code>texera_db.site_settings.key</code>.
+     */
+    public final TableField<SiteSettingsRecord, String> KEY = 
createField(DSL.name("key"), SQLDataType.VARCHAR(255).nullable(false), this, 
"");
+
+    /**
+     * The column <code>texera_db.site_settings.value</code>.
+     */
+    public final TableField<SiteSettingsRecord, String> VALUE = 
createField(DSL.name("value"), SQLDataType.CLOB.nullable(false), this, "");
+
+    /**
+     * The column <code>texera_db.site_settings.updated_by</code>.
+     */
+    public final TableField<SiteSettingsRecord, String> UPDATED_BY = 
createField(DSL.name("updated_by"), SQLDataType.VARCHAR(50), this, "");
+
+    /**
+     * The column <code>texera_db.site_settings.updated_at</code>.
+     */
+    public final TableField<SiteSettingsRecord, Timestamp> UPDATED_AT = 
createField(DSL.name("updated_at"), 
SQLDataType.TIMESTAMP(0).defaultValue(DSL.field("CURRENT_TIMESTAMP", 
SQLDataType.TIMESTAMP)), this, "");
+
+    private SiteSettings(Name alias, Table<SiteSettingsRecord> aliased) {
+        this(alias, aliased, null);
+    }
+
+    private SiteSettings(Name alias, Table<SiteSettingsRecord> aliased, 
Field<?>[] parameters) {
+        super(alias, null, aliased, parameters, DSL.comment(""), 
TableOptions.table());
+    }
+
+    /**
+     * Create an aliased <code>texera_db.site_settings</code> table reference
+     */
+    public SiteSettings(String alias) {
+        this(DSL.name(alias), SITE_SETTINGS);
+    }
+
+    /**
+     * Create an aliased <code>texera_db.site_settings</code> table reference
+     */
+    public SiteSettings(Name alias) {
+        this(alias, SITE_SETTINGS);
+    }
+
+    /**
+     * Create a <code>texera_db.site_settings</code> table reference
+     */
+    public SiteSettings() {
+        this(DSL.name("site_settings"), null);
+    }
+
+    public <O extends Record> SiteSettings(Table<O> child, ForeignKey<O, 
SiteSettingsRecord> key) {
+        super(child, key, SITE_SETTINGS);
+    }
+
+    @Override
+    public Schema getSchema() {
+        return aliased() ? null : TexeraDb.TEXERA_DB;
+    }
+
+    @Override
+    public UniqueKey<SiteSettingsRecord> getPrimaryKey() {
+        return Keys.SITE_SETTINGS_PKEY;
+    }
+
+    @Override
+    public SiteSettings as(String alias) {
+        return new SiteSettings(DSL.name(alias), this);
+    }
+
+    @Override
+    public SiteSettings as(Name alias) {
+        return new SiteSettings(alias, this);
+    }
+
+    /**
+     * Rename this table
+     */
+    @Override
+    public SiteSettings rename(String name) {
+        return new SiteSettings(DSL.name(name), null);
+    }
+
+    /**
+     * Rename this table
+     */
+    @Override
+    public SiteSettings rename(Name name) {
+        return new SiteSettings(name, null);
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Row4 type methods
+    // 
-------------------------------------------------------------------------
+
+    @Override
+    public Row4<String, String, String, Timestamp> fieldsRow() {
+        return (Row4) super.fieldsRow();
+    }
+}
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/WorkflowExecutions.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/WorkflowExecutions.java
index 891e6b0d43..e5c0ee6715 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/WorkflowExecutions.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/WorkflowExecutions.java
@@ -81,6 +81,11 @@ public class WorkflowExecutions extends 
TableImpl<WorkflowExecutionsRecord> {
      */
     public final TableField<WorkflowExecutionsRecord, Integer> UID = 
createField(DSL.name("uid"), SQLDataType.INTEGER.nullable(false), this, "");
 
+    /**
+     * The column <code>texera_db.workflow_executions.cuid</code>.
+     */
+    public final TableField<WorkflowExecutionsRecord, Integer> CUID = 
createField(DSL.name("cuid"), SQLDataType.INTEGER, this, "");
+
     /**
      * The column <code>texera_db.workflow_executions.status</code>.
      */
@@ -132,11 +137,6 @@ public class WorkflowExecutions extends 
TableImpl<WorkflowExecutionsRecord> {
      */
     public final TableField<WorkflowExecutionsRecord, Integer> 
RUNTIME_STATS_SIZE = createField(DSL.name("runtime_stats_size"), 
SQLDataType.INTEGER.defaultValue(DSL.field("0", SQLDataType.INTEGER)), this, 
"");
 
-    /**
-     * The column <code>texera_db.workflow_executions.cuid</code>.
-     */
-    public final TableField<WorkflowExecutionsRecord, Integer> CUID = 
createField(DSL.name("cuid"), SQLDataType.INTEGER, this, "");
-
     private WorkflowExecutions(Name alias, Table<WorkflowExecutionsRecord> 
aliased) {
         this(alias, aliased, null);
     }
@@ -259,7 +259,7 @@ public class WorkflowExecutions extends 
TableImpl<WorkflowExecutionsRecord> {
     // 
-------------------------------------------------------------------------
 
     @Override
-    public Row14<Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer, Integer> 
fieldsRow() {
+    public Row14<Integer, Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer> fieldsRow() {
         return (Row14) super.fieldsRow();
     }
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/DatasetDao.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/DatasetDao.java
index 7ccf653b6e..2a03a24415 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/DatasetDao.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/DatasetDao.java
@@ -160,4 +160,19 @@ public class DatasetDao extends DAOImpl<DatasetRecord, 
edu.uci.ics.texera.dao.jo
     public List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.Dataset> 
fetchByCreationTime(Timestamp... values) {
         return fetch(Dataset.DATASET.CREATION_TIME, values);
     }
+
+    /**
+     * Fetch records that have <code>is_downloadable BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.Dataset> 
fetchRangeOfIsDownloadable(Boolean lowerInclusive, Boolean upperInclusive) {
+        return fetchRange(Dataset.DATASET.IS_DOWNLOADABLE, lowerInclusive, 
upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>is_downloadable IN (values)</code>
+     */
+    public List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.Dataset> 
fetchByIsDownloadable(Boolean... values) {
+        return fetch(Dataset.DATASET.IS_DOWNLOADABLE, values);
+    }
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/SiteSettingsDao.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/SiteSettingsDao.java
new file mode 100644
index 0000000000..1648f6ebb5
--- /dev/null
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/SiteSettingsDao.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ * This file is generated by jOOQ.
+ */
+package edu.uci.ics.texera.dao.jooq.generated.tables.daos;
+
+
+import edu.uci.ics.texera.dao.jooq.generated.tables.SiteSettings;
+import edu.uci.ics.texera.dao.jooq.generated.tables.records.SiteSettingsRecord;
+
+import java.sql.Timestamp;
+import java.util.List;
+import java.util.Optional;
+
+import org.jooq.Configuration;
+import org.jooq.impl.DAOImpl;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes" })
+public class SiteSettingsDao extends DAOImpl<SiteSettingsRecord, 
edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings, String> {
+
+    /**
+     * Create a new SiteSettingsDao without any configuration
+     */
+    public SiteSettingsDao() {
+        super(SiteSettings.SITE_SETTINGS, 
edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings.class);
+    }
+
+    /**
+     * Create a new SiteSettingsDao with an attached configuration
+     */
+    public SiteSettingsDao(Configuration configuration) {
+        super(SiteSettings.SITE_SETTINGS, 
edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings.class, 
configuration);
+    }
+
+    @Override
+    public String 
getId(edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings object) {
+        return object.getKey();
+    }
+
+    /**
+     * Fetch records that have <code>key BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchRangeOfKey(String lowerInclusive, String upperInclusive) {
+        return fetchRange(SiteSettings.SITE_SETTINGS.KEY, lowerInclusive, 
upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>key IN (values)</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchByKey(String... values) {
+        return fetch(SiteSettings.SITE_SETTINGS.KEY, values);
+    }
+
+    /**
+     * Fetch a unique record that has <code>key = value</code>
+     */
+    public edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings 
fetchOneByKey(String value) {
+        return fetchOne(SiteSettings.SITE_SETTINGS.KEY, value);
+    }
+
+    /**
+     * Fetch a unique record that has <code>key = value</code>
+     */
+    public 
Optional<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchOptionalByKey(String value) {
+        return fetchOptional(SiteSettings.SITE_SETTINGS.KEY, value);
+    }
+
+    /**
+     * Fetch records that have <code>value BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchRangeOfValue(String lowerInclusive, String upperInclusive) {
+        return fetchRange(SiteSettings.SITE_SETTINGS.VALUE, lowerInclusive, 
upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>value IN (values)</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchByValue(String... values) {
+        return fetch(SiteSettings.SITE_SETTINGS.VALUE, values);
+    }
+
+    /**
+     * Fetch records that have <code>updated_by BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchRangeOfUpdatedBy(String lowerInclusive, String upperInclusive) {
+        return fetchRange(SiteSettings.SITE_SETTINGS.UPDATED_BY, 
lowerInclusive, upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>updated_by IN (values)</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchByUpdatedBy(String... values) {
+        return fetch(SiteSettings.SITE_SETTINGS.UPDATED_BY, values);
+    }
+
+    /**
+     * Fetch records that have <code>updated_at BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchRangeOfUpdatedAt(Timestamp lowerInclusive, Timestamp upperInclusive) {
+        return fetchRange(SiteSettings.SITE_SETTINGS.UPDATED_AT, 
lowerInclusive, upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>updated_at IN (values)</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings> 
fetchByUpdatedAt(Timestamp... values) {
+        return fetch(SiteSettings.SITE_SETTINGS.UPDATED_AT, values);
+    }
+}
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/WorkflowExecutionsDao.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/WorkflowExecutionsDao.java
index e725fc7d77..076c9c076a 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/WorkflowExecutionsDao.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/daos/WorkflowExecutionsDao.java
@@ -116,6 +116,21 @@ public class WorkflowExecutionsDao extends 
DAOImpl<WorkflowExecutionsRecord, edu
         return fetch(WorkflowExecutions.WORKFLOW_EXECUTIONS.UID, values);
     }
 
+    /**
+     * Fetch records that have <code>cuid BETWEEN lowerInclusive AND
+     * upperInclusive</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowExecutions> 
fetchRangeOfCuid(Integer lowerInclusive, Integer upperInclusive) {
+        return fetchRange(WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID, 
lowerInclusive, upperInclusive);
+    }
+
+    /**
+     * Fetch records that have <code>cuid IN (values)</code>
+     */
+    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowExecutions> 
fetchByCuid(Integer... values) {
+        return fetch(WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID, values);
+    }
+
     /**
      * Fetch records that have <code>status BETWEEN lowerInclusive AND
      * upperInclusive</code>
@@ -265,19 +280,4 @@ public class WorkflowExecutionsDao extends 
DAOImpl<WorkflowExecutionsRecord, edu
     public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowExecutions> 
fetchByRuntimeStatsSize(Integer... values) {
         return 
fetch(WorkflowExecutions.WORKFLOW_EXECUTIONS.RUNTIME_STATS_SIZE, values);
     }
-
-    /**
-     * Fetch records that have <code>cuid BETWEEN lowerInclusive AND
-     * upperInclusive</code>
-     */
-    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowExecutions> 
fetchRangeOfCuid(Integer lowerInclusive, Integer upperInclusive) {
-        return fetchRange(WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID, 
lowerInclusive, upperInclusive);
-    }
-
-    /**
-     * Fetch records that have <code>cuid IN (values)</code>
-     */
-    public 
List<edu.uci.ics.texera.dao.jooq.generated.tables.pojos.WorkflowExecutions> 
fetchByCuid(Integer... values) {
-        return fetch(WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID, values);
-    }
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
index 734267c66f..cf07efa094 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
@@ -91,6 +91,16 @@ public interface IDataset extends Serializable {
      */
     public Timestamp getCreationTime();
 
+    /**
+     * Setter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    public void setIsDownloadable(Boolean value);
+
+    /**
+     * Getter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    public Boolean getIsDownloadable();
+
     // 
-------------------------------------------------------------------------
     // FROM and INTO
     // 
-------------------------------------------------------------------------
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/ISiteSettings.java
similarity index 52%
copy from 
core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
copy to 
core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/ISiteSettings.java
index 734267c66f..7e37208a2e 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IDataset.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/ISiteSettings.java
@@ -29,67 +29,47 @@ import java.sql.Timestamp;
  * This class is generated by jOOQ.
  */
 @SuppressWarnings({ "all", "unchecked", "rawtypes" })
-public interface IDataset extends Serializable {
+public interface ISiteSettings extends Serializable {
 
     /**
-     * Setter for <code>texera_db.dataset.did</code>.
+     * Setter for <code>texera_db.site_settings.key</code>.
      */
-    public void setDid(Integer value);
+    public void setKey(String value);
 
     /**
-     * Getter for <code>texera_db.dataset.did</code>.
+     * Getter for <code>texera_db.site_settings.key</code>.
      */
-    public Integer getDid();
+    public String getKey();
 
     /**
-     * Setter for <code>texera_db.dataset.owner_uid</code>.
+     * Setter for <code>texera_db.site_settings.value</code>.
      */
-    public void setOwnerUid(Integer value);
+    public void setValue(String value);
 
     /**
-     * Getter for <code>texera_db.dataset.owner_uid</code>.
+     * Getter for <code>texera_db.site_settings.value</code>.
      */
-    public Integer getOwnerUid();
+    public String getValue();
 
     /**
-     * Setter for <code>texera_db.dataset.name</code>.
+     * Setter for <code>texera_db.site_settings.updated_by</code>.
      */
-    public void setName(String value);
+    public void setUpdatedBy(String value);
 
     /**
-     * Getter for <code>texera_db.dataset.name</code>.
+     * Getter for <code>texera_db.site_settings.updated_by</code>.
      */
-    public String getName();
+    public String getUpdatedBy();
 
     /**
-     * Setter for <code>texera_db.dataset.is_public</code>.
+     * Setter for <code>texera_db.site_settings.updated_at</code>.
      */
-    public void setIsPublic(Boolean value);
+    public void setUpdatedAt(Timestamp value);
 
     /**
-     * Getter for <code>texera_db.dataset.is_public</code>.
+     * Getter for <code>texera_db.site_settings.updated_at</code>.
      */
-    public Boolean getIsPublic();
-
-    /**
-     * Setter for <code>texera_db.dataset.description</code>.
-     */
-    public void setDescription(String value);
-
-    /**
-     * Getter for <code>texera_db.dataset.description</code>.
-     */
-    public String getDescription();
-
-    /**
-     * Setter for <code>texera_db.dataset.creation_time</code>.
-     */
-    public void setCreationTime(Timestamp value);
-
-    /**
-     * Getter for <code>texera_db.dataset.creation_time</code>.
-     */
-    public Timestamp getCreationTime();
+    public Timestamp getUpdatedAt();
 
     // 
-------------------------------------------------------------------------
     // FROM and INTO
@@ -97,13 +77,13 @@ public interface IDataset extends Serializable {
 
     /**
      * Load data from another generated Record/POJO implementing the common
-     * interface IDataset
+     * interface ISiteSettings
      */
-    public void from(IDataset from);
+    public void from(ISiteSettings from);
 
     /**
      * Copy data into another generated Record/POJO implementing the common
-     * interface IDataset
+     * interface ISiteSettings
      */
-    public <E extends IDataset> E into(E into);
+    public <E extends ISiteSettings> E into(E into);
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IWorkflowExecutions.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IWorkflowExecutions.java
index 295fd8be3d..11c3e5a927 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IWorkflowExecutions.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/interfaces/IWorkflowExecutions.java
@@ -61,6 +61,16 @@ public interface IWorkflowExecutions extends Serializable {
      */
     public Integer getUid();
 
+    /**
+     * Setter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    public void setCuid(Integer value);
+
+    /**
+     * Getter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    public Integer getCuid();
+
     /**
      * Setter for <code>texera_db.workflow_executions.status</code>.
      */
@@ -163,16 +173,6 @@ public interface IWorkflowExecutions extends Serializable {
      */
     public Integer getRuntimeStatsSize();
 
-    /**
-     * Setter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    public void setCuid(Integer value);
-
-    /**
-     * Getter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    public Integer getCuid();
-
     // 
-------------------------------------------------------------------------
     // FROM and INTO
     // 
-------------------------------------------------------------------------
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/Dataset.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/Dataset.java
index f45e57517a..15be7dace4 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/Dataset.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/Dataset.java
@@ -40,6 +40,7 @@ public class Dataset implements IDataset {
     private Boolean   isPublic;
     private String    description;
     private Timestamp creationTime;
+    private Boolean   isDownloadable;
 
     public Dataset() {}
 
@@ -50,6 +51,7 @@ public class Dataset implements IDataset {
         this.isPublic = value.getIsPublic();
         this.description = value.getDescription();
         this.creationTime = value.getCreationTime();
+        this.isDownloadable = value.getIsDownloadable();
     }
 
     public Dataset(
@@ -58,7 +60,8 @@ public class Dataset implements IDataset {
         String    name,
         Boolean   isPublic,
         String    description,
-        Timestamp creationTime
+        Timestamp creationTime,
+        Boolean   isDownloadable
     ) {
         this.did = did;
         this.ownerUid = ownerUid;
@@ -66,6 +69,7 @@ public class Dataset implements IDataset {
         this.isPublic = isPublic;
         this.description = description;
         this.creationTime = creationTime;
+        this.isDownloadable = isDownloadable;
     }
 
     /**
@@ -164,6 +168,22 @@ public class Dataset implements IDataset {
         this.creationTime = creationTime;
     }
 
+    /**
+     * Getter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    @Override
+    public Boolean getIsDownloadable() {
+        return this.isDownloadable;
+    }
+
+    /**
+     * Setter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    @Override
+    public void setIsDownloadable(Boolean isDownloadable) {
+        this.isDownloadable = isDownloadable;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("Dataset (");
@@ -174,6 +194,7 @@ public class Dataset implements IDataset {
         sb.append(", ").append(isPublic);
         sb.append(", ").append(description);
         sb.append(", ").append(creationTime);
+        sb.append(", ").append(isDownloadable);
 
         sb.append(")");
         return sb.toString();
@@ -191,6 +212,7 @@ public class Dataset implements IDataset {
         setIsPublic(from.getIsPublic());
         setDescription(from.getDescription());
         setCreationTime(from.getCreationTime());
+        setIsDownloadable(from.getIsDownloadable());
     }
 
     @Override
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/SiteSettings.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/SiteSettings.java
new file mode 100644
index 0000000000..8cd646f404
--- /dev/null
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/SiteSettings.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ * This file is generated by jOOQ.
+ */
+package edu.uci.ics.texera.dao.jooq.generated.tables.pojos;
+
+
+import edu.uci.ics.texera.dao.jooq.generated.tables.interfaces.ISiteSettings;
+
+import java.sql.Timestamp;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes" })
+public class SiteSettings implements ISiteSettings {
+
+    private static final long serialVersionUID = 1L;
+
+    private String    key;
+    private String    value;
+    private String    updatedBy;
+    private Timestamp updatedAt;
+
+    public SiteSettings() {}
+
+    public SiteSettings(ISiteSettings value) {
+        this.key = value.getKey();
+        this.value = value.getValue();
+        this.updatedBy = value.getUpdatedBy();
+        this.updatedAt = value.getUpdatedAt();
+    }
+
+    public SiteSettings(
+        String    key,
+        String    value,
+        String    updatedBy,
+        Timestamp updatedAt
+    ) {
+        this.key = key;
+        this.value = value;
+        this.updatedBy = updatedBy;
+        this.updatedAt = updatedAt;
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.key</code>.
+     */
+    @Override
+    public String getKey() {
+        return this.key;
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.key</code>.
+     */
+    @Override
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.value</code>.
+     */
+    @Override
+    public String getValue() {
+        return this.value;
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.value</code>.
+     */
+    @Override
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.updated_by</code>.
+     */
+    @Override
+    public String getUpdatedBy() {
+        return this.updatedBy;
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.updated_by</code>.
+     */
+    @Override
+    public void setUpdatedBy(String updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.updated_at</code>.
+     */
+    @Override
+    public Timestamp getUpdatedAt() {
+        return this.updatedAt;
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.updated_at</code>.
+     */
+    @Override
+    public void setUpdatedAt(Timestamp updatedAt) {
+        this.updatedAt = updatedAt;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("SiteSettings (");
+
+        sb.append(key);
+        sb.append(", ").append(value);
+        sb.append(", ").append(updatedBy);
+        sb.append(", ").append(updatedAt);
+
+        sb.append(")");
+        return sb.toString();
+    }
+
+    // 
-------------------------------------------------------------------------
+    // FROM and INTO
+    // 
-------------------------------------------------------------------------
+
+    @Override
+    public void from(ISiteSettings from) {
+        setKey(from.getKey());
+        setValue(from.getValue());
+        setUpdatedBy(from.getUpdatedBy());
+        setUpdatedAt(from.getUpdatedAt());
+    }
+
+    @Override
+    public <E extends ISiteSettings> E into(E into) {
+        into.from(this);
+        return into;
+    }
+}
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/WorkflowExecutions.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/WorkflowExecutions.java
index d463014600..63d531abeb 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/WorkflowExecutions.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/pojos/WorkflowExecutions.java
@@ -37,6 +37,7 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
     private Integer   eid;
     private Integer   vid;
     private Integer   uid;
+    private Integer   cuid;
     private Short     status;
     private String    result;
     private Timestamp startingTime;
@@ -47,7 +48,6 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
     private String    logLocation;
     private String    runtimeStatsUri;
     private Integer   runtimeStatsSize;
-    private Integer   cuid;
 
     public WorkflowExecutions() {}
 
@@ -55,6 +55,7 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         this.eid = value.getEid();
         this.vid = value.getVid();
         this.uid = value.getUid();
+        this.cuid = value.getCuid();
         this.status = value.getStatus();
         this.result = value.getResult();
         this.startingTime = value.getStartingTime();
@@ -65,13 +66,13 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         this.logLocation = value.getLogLocation();
         this.runtimeStatsUri = value.getRuntimeStatsUri();
         this.runtimeStatsSize = value.getRuntimeStatsSize();
-        this.cuid = value.getCuid();
     }
 
     public WorkflowExecutions(
         Integer   eid,
         Integer   vid,
         Integer   uid,
+        Integer   cuid,
         Short     status,
         String    result,
         Timestamp startingTime,
@@ -81,12 +82,12 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         String    environmentVersion,
         String    logLocation,
         String    runtimeStatsUri,
-        Integer   runtimeStatsSize,
-        Integer   cuid
+        Integer   runtimeStatsSize
     ) {
         this.eid = eid;
         this.vid = vid;
         this.uid = uid;
+        this.cuid = cuid;
         this.status = status;
         this.result = result;
         this.startingTime = startingTime;
@@ -97,7 +98,6 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         this.logLocation = logLocation;
         this.runtimeStatsUri = runtimeStatsUri;
         this.runtimeStatsSize = runtimeStatsSize;
-        this.cuid = cuid;
     }
 
     /**
@@ -148,6 +148,22 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         this.uid = uid;
     }
 
+    /**
+     * Getter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    @Override
+    public Integer getCuid() {
+        return this.cuid;
+    }
+
+    /**
+     * Setter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    @Override
+    public void setCuid(Integer cuid) {
+        this.cuid = cuid;
+    }
+
     /**
      * Getter for <code>texera_db.workflow_executions.status</code>.
      */
@@ -310,22 +326,6 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         this.runtimeStatsSize = runtimeStatsSize;
     }
 
-    /**
-     * Getter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    @Override
-    public Integer getCuid() {
-        return this.cuid;
-    }
-
-    /**
-     * Setter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    @Override
-    public void setCuid(Integer cuid) {
-        this.cuid = cuid;
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("WorkflowExecutions (");
@@ -333,6 +333,7 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         sb.append(eid);
         sb.append(", ").append(vid);
         sb.append(", ").append(uid);
+        sb.append(", ").append(cuid);
         sb.append(", ").append(status);
         sb.append(", ").append(result);
         sb.append(", ").append(startingTime);
@@ -343,7 +344,6 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         sb.append(", ").append(logLocation);
         sb.append(", ").append(runtimeStatsUri);
         sb.append(", ").append(runtimeStatsSize);
-        sb.append(", ").append(cuid);
 
         sb.append(")");
         return sb.toString();
@@ -358,6 +358,7 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         setEid(from.getEid());
         setVid(from.getVid());
         setUid(from.getUid());
+        setCuid(from.getCuid());
         setStatus(from.getStatus());
         setResult(from.getResult());
         setStartingTime(from.getStartingTime());
@@ -368,7 +369,6 @@ public class WorkflowExecutions implements 
IWorkflowExecutions {
         setLogLocation(from.getLogLocation());
         setRuntimeStatsUri(from.getRuntimeStatsUri());
         setRuntimeStatsSize(from.getRuntimeStatsSize());
-        setCuid(from.getCuid());
     }
 
     @Override
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/DatasetRecord.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/DatasetRecord.java
index 0f1234dcb0..7070836def 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/DatasetRecord.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/DatasetRecord.java
@@ -28,8 +28,8 @@ import java.sql.Timestamp;
 
 import org.jooq.Field;
 import org.jooq.Record1;
-import org.jooq.Record6;
-import org.jooq.Row6;
+import org.jooq.Record7;
+import org.jooq.Row7;
 import org.jooq.impl.UpdatableRecordImpl;
 
 
@@ -37,7 +37,7 @@ import org.jooq.impl.UpdatableRecordImpl;
  * This class is generated by jOOQ.
  */
 @SuppressWarnings({ "all", "unchecked", "rawtypes" })
-public class DatasetRecord extends UpdatableRecordImpl<DatasetRecord> 
implements Record6<Integer, Integer, String, Boolean, String, Timestamp>, 
IDataset {
+public class DatasetRecord extends UpdatableRecordImpl<DatasetRecord> 
implements Record7<Integer, Integer, String, Boolean, String, Timestamp, 
Boolean>, IDataset {
 
     private static final long serialVersionUID = 1L;
 
@@ -137,6 +137,22 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         return (Timestamp) get(5);
     }
 
+    /**
+     * Setter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    @Override
+    public void setIsDownloadable(Boolean value) {
+        set(6, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.dataset.is_downloadable</code>.
+     */
+    @Override
+    public Boolean getIsDownloadable() {
+        return (Boolean) get(6);
+    }
+
     // 
-------------------------------------------------------------------------
     // Primary key information
     // 
-------------------------------------------------------------------------
@@ -147,17 +163,17 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
     }
 
     // 
-------------------------------------------------------------------------
-    // Record6 type implementation
+    // Record7 type implementation
     // 
-------------------------------------------------------------------------
 
     @Override
-    public Row6<Integer, Integer, String, Boolean, String, Timestamp> 
fieldsRow() {
-        return (Row6) super.fieldsRow();
+    public Row7<Integer, Integer, String, Boolean, String, Timestamp, Boolean> 
fieldsRow() {
+        return (Row7) super.fieldsRow();
     }
 
     @Override
-    public Row6<Integer, Integer, String, Boolean, String, Timestamp> 
valuesRow() {
-        return (Row6) super.valuesRow();
+    public Row7<Integer, Integer, String, Boolean, String, Timestamp, Boolean> 
valuesRow() {
+        return (Row7) super.valuesRow();
     }
 
     @Override
@@ -190,6 +206,11 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         return Dataset.DATASET.CREATION_TIME;
     }
 
+    @Override
+    public Field<Boolean> field7() {
+        return Dataset.DATASET.IS_DOWNLOADABLE;
+    }
+
     @Override
     public Integer component1() {
         return getDid();
@@ -220,6 +241,11 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         return getCreationTime();
     }
 
+    @Override
+    public Boolean component7() {
+        return getIsDownloadable();
+    }
+
     @Override
     public Integer value1() {
         return getDid();
@@ -250,6 +276,11 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         return getCreationTime();
     }
 
+    @Override
+    public Boolean value7() {
+        return getIsDownloadable();
+    }
+
     @Override
     public DatasetRecord value1(Integer value) {
         setDid(value);
@@ -287,13 +318,20 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
     }
 
     @Override
-    public DatasetRecord values(Integer value1, Integer value2, String value3, 
Boolean value4, String value5, Timestamp value6) {
+    public DatasetRecord value7(Boolean value) {
+        setIsDownloadable(value);
+        return this;
+    }
+
+    @Override
+    public DatasetRecord values(Integer value1, Integer value2, String value3, 
Boolean value4, String value5, Timestamp value6, Boolean value7) {
         value1(value1);
         value2(value2);
         value3(value3);
         value4(value4);
         value5(value5);
         value6(value6);
+        value7(value7);
         return this;
     }
 
@@ -309,6 +347,7 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         setIsPublic(from.getIsPublic());
         setDescription(from.getDescription());
         setCreationTime(from.getCreationTime());
+        setIsDownloadable(from.getIsDownloadable());
     }
 
     @Override
@@ -331,7 +370,7 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
     /**
      * Create a detached, initialised DatasetRecord
      */
-    public DatasetRecord(Integer did, Integer ownerUid, String name, Boolean 
isPublic, String description, Timestamp creationTime) {
+    public DatasetRecord(Integer did, Integer ownerUid, String name, Boolean 
isPublic, String description, Timestamp creationTime, Boolean isDownloadable) {
         super(Dataset.DATASET);
 
         setDid(did);
@@ -340,6 +379,7 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
         setIsPublic(isPublic);
         setDescription(description);
         setCreationTime(creationTime);
+        setIsDownloadable(isDownloadable);
     }
 
     /**
@@ -355,6 +395,7 @@ public class DatasetRecord extends 
UpdatableRecordImpl<DatasetRecord> implements
             setIsPublic(value.getIsPublic());
             setDescription(value.getDescription());
             setCreationTime(value.getCreationTime());
+            setIsDownloadable(value.getIsDownloadable());
         }
     }
 }
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/SiteSettingsRecord.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/SiteSettingsRecord.java
new file mode 100644
index 0000000000..839fdb4480
--- /dev/null
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/SiteSettingsRecord.java
@@ -0,0 +1,278 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * 
+ * This file is generated by jOOQ.
+ */
+package edu.uci.ics.texera.dao.jooq.generated.tables.records;
+
+
+import edu.uci.ics.texera.dao.jooq.generated.tables.SiteSettings;
+import edu.uci.ics.texera.dao.jooq.generated.tables.interfaces.ISiteSettings;
+
+import java.sql.Timestamp;
+
+import org.jooq.Field;
+import org.jooq.Record1;
+import org.jooq.Record4;
+import org.jooq.Row4;
+import org.jooq.impl.UpdatableRecordImpl;
+
+
+/**
+ * This class is generated by jOOQ.
+ */
+@SuppressWarnings({ "all", "unchecked", "rawtypes" })
+public class SiteSettingsRecord extends 
UpdatableRecordImpl<SiteSettingsRecord> implements Record4<String, String, 
String, Timestamp>, ISiteSettings {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Setter for <code>texera_db.site_settings.key</code>.
+     */
+    @Override
+    public void setKey(String value) {
+        set(0, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.key</code>.
+     */
+    @Override
+    public String getKey() {
+        return (String) get(0);
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.value</code>.
+     */
+    @Override
+    public void setValue(String value) {
+        set(1, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.value</code>.
+     */
+    @Override
+    public String getValue() {
+        return (String) get(1);
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.updated_by</code>.
+     */
+    @Override
+    public void setUpdatedBy(String value) {
+        set(2, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.updated_by</code>.
+     */
+    @Override
+    public String getUpdatedBy() {
+        return (String) get(2);
+    }
+
+    /**
+     * Setter for <code>texera_db.site_settings.updated_at</code>.
+     */
+    @Override
+    public void setUpdatedAt(Timestamp value) {
+        set(3, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.site_settings.updated_at</code>.
+     */
+    @Override
+    public Timestamp getUpdatedAt() {
+        return (Timestamp) get(3);
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Primary key information
+    // 
-------------------------------------------------------------------------
+
+    @Override
+    public Record1<String> key() {
+        return (Record1) super.key();
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Record4 type implementation
+    // 
-------------------------------------------------------------------------
+
+    @Override
+    public Row4<String, String, String, Timestamp> fieldsRow() {
+        return (Row4) super.fieldsRow();
+    }
+
+    @Override
+    public Row4<String, String, String, Timestamp> valuesRow() {
+        return (Row4) super.valuesRow();
+    }
+
+    @Override
+    public Field<String> field1() {
+        return SiteSettings.SITE_SETTINGS.KEY;
+    }
+
+    @Override
+    public Field<String> field2() {
+        return SiteSettings.SITE_SETTINGS.VALUE;
+    }
+
+    @Override
+    public Field<String> field3() {
+        return SiteSettings.SITE_SETTINGS.UPDATED_BY;
+    }
+
+    @Override
+    public Field<Timestamp> field4() {
+        return SiteSettings.SITE_SETTINGS.UPDATED_AT;
+    }
+
+    @Override
+    public String component1() {
+        return getKey();
+    }
+
+    @Override
+    public String component2() {
+        return getValue();
+    }
+
+    @Override
+    public String component3() {
+        return getUpdatedBy();
+    }
+
+    @Override
+    public Timestamp component4() {
+        return getUpdatedAt();
+    }
+
+    @Override
+    public String value1() {
+        return getKey();
+    }
+
+    @Override
+    public String value2() {
+        return getValue();
+    }
+
+    @Override
+    public String value3() {
+        return getUpdatedBy();
+    }
+
+    @Override
+    public Timestamp value4() {
+        return getUpdatedAt();
+    }
+
+    @Override
+    public SiteSettingsRecord value1(String value) {
+        setKey(value);
+        return this;
+    }
+
+    @Override
+    public SiteSettingsRecord value2(String value) {
+        setValue(value);
+        return this;
+    }
+
+    @Override
+    public SiteSettingsRecord value3(String value) {
+        setUpdatedBy(value);
+        return this;
+    }
+
+    @Override
+    public SiteSettingsRecord value4(Timestamp value) {
+        setUpdatedAt(value);
+        return this;
+    }
+
+    @Override
+    public SiteSettingsRecord values(String value1, String value2, String 
value3, Timestamp value4) {
+        value1(value1);
+        value2(value2);
+        value3(value3);
+        value4(value4);
+        return this;
+    }
+
+    // 
-------------------------------------------------------------------------
+    // FROM and INTO
+    // 
-------------------------------------------------------------------------
+
+    @Override
+    public void from(ISiteSettings from) {
+        setKey(from.getKey());
+        setValue(from.getValue());
+        setUpdatedBy(from.getUpdatedBy());
+        setUpdatedAt(from.getUpdatedAt());
+    }
+
+    @Override
+    public <E extends ISiteSettings> E into(E into) {
+        into.from(this);
+        return into;
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Constructors
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Create a detached SiteSettingsRecord
+     */
+    public SiteSettingsRecord() {
+        super(SiteSettings.SITE_SETTINGS);
+    }
+
+    /**
+     * Create a detached, initialised SiteSettingsRecord
+     */
+    public SiteSettingsRecord(String key, String value, String updatedBy, 
Timestamp updatedAt) {
+        super(SiteSettings.SITE_SETTINGS);
+
+        setKey(key);
+        setValue(value);
+        setUpdatedBy(updatedBy);
+        setUpdatedAt(updatedAt);
+    }
+
+    /**
+     * Create a detached, initialised SiteSettingsRecord
+     */
+    public 
SiteSettingsRecord(edu.uci.ics.texera.dao.jooq.generated.tables.pojos.SiteSettings
 value) {
+        super(SiteSettings.SITE_SETTINGS);
+
+        if (value != null) {
+            setKey(value.getKey());
+            setValue(value.getValue());
+            setUpdatedBy(value.getUpdatedBy());
+            setUpdatedAt(value.getUpdatedAt());
+        }
+    }
+}
diff --git 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/WorkflowExecutionsRecord.java
 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/WorkflowExecutionsRecord.java
index 96469bea95..e1b43baf68 100644
--- 
a/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/WorkflowExecutionsRecord.java
+++ 
b/core/dao/src/main/scala/edu/uci/ics/texera/dao/jooq/generated/tables/records/WorkflowExecutionsRecord.java
@@ -37,7 +37,7 @@ import org.jooq.impl.UpdatableRecordImpl;
  * This class is generated by jOOQ.
  */
 @SuppressWarnings({ "all", "unchecked", "rawtypes" })
-public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecutionsRecord> implements Record14<Integer, 
Integer, Integer, Short, String, Timestamp, Timestamp, Boolean, String, String, 
String, String, Integer, Integer>, IWorkflowExecutions {
+public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecutionsRecord> implements Record14<Integer, 
Integer, Integer, Integer, Short, String, Timestamp, Timestamp, Boolean, 
String, String, String, String, Integer>, IWorkflowExecutions {
 
     private static final long serialVersionUID = 1L;
 
@@ -89,12 +89,28 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
         return (Integer) get(2);
     }
 
+    /**
+     * Setter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    @Override
+    public void setCuid(Integer value) {
+        set(3, value);
+    }
+
+    /**
+     * Getter for <code>texera_db.workflow_executions.cuid</code>.
+     */
+    @Override
+    public Integer getCuid() {
+        return (Integer) get(3);
+    }
+
     /**
      * Setter for <code>texera_db.workflow_executions.status</code>.
      */
     @Override
     public void setStatus(Short value) {
-        set(3, value);
+        set(4, value);
     }
 
     /**
@@ -102,7 +118,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public Short getStatus() {
-        return (Short) get(3);
+        return (Short) get(4);
     }
 
     /**
@@ -110,7 +126,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setResult(String value) {
-        set(4, value);
+        set(5, value);
     }
 
     /**
@@ -118,7 +134,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public String getResult() {
-        return (String) get(4);
+        return (String) get(5);
     }
 
     /**
@@ -126,7 +142,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setStartingTime(Timestamp value) {
-        set(5, value);
+        set(6, value);
     }
 
     /**
@@ -134,7 +150,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public Timestamp getStartingTime() {
-        return (Timestamp) get(5);
+        return (Timestamp) get(6);
     }
 
     /**
@@ -142,7 +158,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setLastUpdateTime(Timestamp value) {
-        set(6, value);
+        set(7, value);
     }
 
     /**
@@ -150,7 +166,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public Timestamp getLastUpdateTime() {
-        return (Timestamp) get(6);
+        return (Timestamp) get(7);
     }
 
     /**
@@ -158,7 +174,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setBookmarked(Boolean value) {
-        set(7, value);
+        set(8, value);
     }
 
     /**
@@ -166,7 +182,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public Boolean getBookmarked() {
-        return (Boolean) get(7);
+        return (Boolean) get(8);
     }
 
     /**
@@ -174,7 +190,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setName(String value) {
-        set(8, value);
+        set(9, value);
     }
 
     /**
@@ -182,7 +198,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public String getName() {
-        return (String) get(8);
+        return (String) get(9);
     }
 
     /**
@@ -191,7 +207,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setEnvironmentVersion(String value) {
-        set(9, value);
+        set(10, value);
     }
 
     /**
@@ -200,7 +216,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public String getEnvironmentVersion() {
-        return (String) get(9);
+        return (String) get(10);
     }
 
     /**
@@ -208,7 +224,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setLogLocation(String value) {
-        set(10, value);
+        set(11, value);
     }
 
     /**
@@ -216,7 +232,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public String getLogLocation() {
-        return (String) get(10);
+        return (String) get(11);
     }
 
     /**
@@ -224,7 +240,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setRuntimeStatsUri(String value) {
-        set(11, value);
+        set(12, value);
     }
 
     /**
@@ -232,7 +248,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public String getRuntimeStatsUri() {
-        return (String) get(11);
+        return (String) get(12);
     }
 
     /**
@@ -240,7 +256,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public void setRuntimeStatsSize(Integer value) {
-        set(12, value);
+        set(13, value);
     }
 
     /**
@@ -248,22 +264,6 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
      */
     @Override
     public Integer getRuntimeStatsSize() {
-        return (Integer) get(12);
-    }
-
-    /**
-     * Setter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    @Override
-    public void setCuid(Integer value) {
-        set(13, value);
-    }
-
-    /**
-     * Getter for <code>texera_db.workflow_executions.cuid</code>.
-     */
-    @Override
-    public Integer getCuid() {
         return (Integer) get(13);
     }
 
@@ -281,12 +281,12 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     // 
-------------------------------------------------------------------------
 
     @Override
-    public Row14<Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer, Integer> 
fieldsRow() {
+    public Row14<Integer, Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer> fieldsRow() {
         return (Row14) super.fieldsRow();
     }
 
     @Override
-    public Row14<Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer, Integer> 
valuesRow() {
+    public Row14<Integer, Integer, Integer, Integer, Short, String, Timestamp, 
Timestamp, Boolean, String, String, String, String, Integer> valuesRow() {
         return (Row14) super.valuesRow();
     }
 
@@ -306,58 +306,58 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     }
 
     @Override
-    public Field<Short> field4() {
+    public Field<Integer> field4() {
+        return WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID;
+    }
+
+    @Override
+    public Field<Short> field5() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.STATUS;
     }
 
     @Override
-    public Field<String> field5() {
+    public Field<String> field6() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.RESULT;
     }
 
     @Override
-    public Field<Timestamp> field6() {
+    public Field<Timestamp> field7() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.STARTING_TIME;
     }
 
     @Override
-    public Field<Timestamp> field7() {
+    public Field<Timestamp> field8() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.LAST_UPDATE_TIME;
     }
 
     @Override
-    public Field<Boolean> field8() {
+    public Field<Boolean> field9() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.BOOKMARKED;
     }
 
     @Override
-    public Field<String> field9() {
+    public Field<String> field10() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.NAME;
     }
 
     @Override
-    public Field<String> field10() {
+    public Field<String> field11() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.ENVIRONMENT_VERSION;
     }
 
     @Override
-    public Field<String> field11() {
+    public Field<String> field12() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.LOG_LOCATION;
     }
 
     @Override
-    public Field<String> field12() {
+    public Field<String> field13() {
         return WorkflowExecutions.WORKFLOW_EXECUTIONS.RUNTIME_STATS_URI;
     }
 
-    @Override
-    public Field<Integer> field13() {
-        return WorkflowExecutions.WORKFLOW_EXECUTIONS.RUNTIME_STATS_SIZE;
-    }
-
     @Override
     public Field<Integer> field14() {
-        return WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID;
+        return WorkflowExecutions.WORKFLOW_EXECUTIONS.RUNTIME_STATS_SIZE;
     }
 
     @Override
@@ -376,58 +376,58 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     }
 
     @Override
-    public Short component4() {
+    public Integer component4() {
+        return getCuid();
+    }
+
+    @Override
+    public Short component5() {
         return getStatus();
     }
 
     @Override
-    public String component5() {
+    public String component6() {
         return getResult();
     }
 
     @Override
-    public Timestamp component6() {
+    public Timestamp component7() {
         return getStartingTime();
     }
 
     @Override
-    public Timestamp component7() {
+    public Timestamp component8() {
         return getLastUpdateTime();
     }
 
     @Override
-    public Boolean component8() {
+    public Boolean component9() {
         return getBookmarked();
     }
 
     @Override
-    public String component9() {
+    public String component10() {
         return getName();
     }
 
     @Override
-    public String component10() {
+    public String component11() {
         return getEnvironmentVersion();
     }
 
     @Override
-    public String component11() {
+    public String component12() {
         return getLogLocation();
     }
 
     @Override
-    public String component12() {
+    public String component13() {
         return getRuntimeStatsUri();
     }
 
-    @Override
-    public Integer component13() {
-        return getRuntimeStatsSize();
-    }
-
     @Override
     public Integer component14() {
-        return getCuid();
+        return getRuntimeStatsSize();
     }
 
     @Override
@@ -446,58 +446,58 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     }
 
     @Override
-    public Short value4() {
+    public Integer value4() {
+        return getCuid();
+    }
+
+    @Override
+    public Short value5() {
         return getStatus();
     }
 
     @Override
-    public String value5() {
+    public String value6() {
         return getResult();
     }
 
     @Override
-    public Timestamp value6() {
+    public Timestamp value7() {
         return getStartingTime();
     }
 
     @Override
-    public Timestamp value7() {
+    public Timestamp value8() {
         return getLastUpdateTime();
     }
 
     @Override
-    public Boolean value8() {
+    public Boolean value9() {
         return getBookmarked();
     }
 
     @Override
-    public String value9() {
+    public String value10() {
         return getName();
     }
 
     @Override
-    public String value10() {
+    public String value11() {
         return getEnvironmentVersion();
     }
 
     @Override
-    public String value11() {
+    public String value12() {
         return getLogLocation();
     }
 
     @Override
-    public String value12() {
+    public String value13() {
         return getRuntimeStatsUri();
     }
 
-    @Override
-    public Integer value13() {
-        return getRuntimeStatsSize();
-    }
-
     @Override
     public Integer value14() {
-        return getCuid();
+        return getRuntimeStatsSize();
     }
 
     @Override
@@ -519,73 +519,73 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     }
 
     @Override
-    public WorkflowExecutionsRecord value4(Short value) {
+    public WorkflowExecutionsRecord value4(Integer value) {
+        setCuid(value);
+        return this;
+    }
+
+    @Override
+    public WorkflowExecutionsRecord value5(Short value) {
         setStatus(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value5(String value) {
+    public WorkflowExecutionsRecord value6(String value) {
         setResult(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value6(Timestamp value) {
+    public WorkflowExecutionsRecord value7(Timestamp value) {
         setStartingTime(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value7(Timestamp value) {
+    public WorkflowExecutionsRecord value8(Timestamp value) {
         setLastUpdateTime(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value8(Boolean value) {
+    public WorkflowExecutionsRecord value9(Boolean value) {
         setBookmarked(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value9(String value) {
+    public WorkflowExecutionsRecord value10(String value) {
         setName(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value10(String value) {
+    public WorkflowExecutionsRecord value11(String value) {
         setEnvironmentVersion(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value11(String value) {
+    public WorkflowExecutionsRecord value12(String value) {
         setLogLocation(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord value12(String value) {
+    public WorkflowExecutionsRecord value13(String value) {
         setRuntimeStatsUri(value);
         return this;
     }
 
-    @Override
-    public WorkflowExecutionsRecord value13(Integer value) {
-        setRuntimeStatsSize(value);
-        return this;
-    }
-
     @Override
     public WorkflowExecutionsRecord value14(Integer value) {
-        setCuid(value);
+        setRuntimeStatsSize(value);
         return this;
     }
 
     @Override
-    public WorkflowExecutionsRecord values(Integer value1, Integer value2, 
Integer value3, Short value4, String value5, Timestamp value6, Timestamp 
value7, Boolean value8, String value9, String value10, String value11, String 
value12, Integer value13, Integer value14) {
+    public WorkflowExecutionsRecord values(Integer value1, Integer value2, 
Integer value3, Integer value4, Short value5, String value6, Timestamp value7, 
Timestamp value8, Boolean value9, String value10, String value11, String 
value12, String value13, Integer value14) {
         value1(value1);
         value2(value2);
         value3(value3);
@@ -612,6 +612,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
         setEid(from.getEid());
         setVid(from.getVid());
         setUid(from.getUid());
+        setCuid(from.getCuid());
         setStatus(from.getStatus());
         setResult(from.getResult());
         setStartingTime(from.getStartingTime());
@@ -622,7 +623,6 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
         setLogLocation(from.getLogLocation());
         setRuntimeStatsUri(from.getRuntimeStatsUri());
         setRuntimeStatsSize(from.getRuntimeStatsSize());
-        setCuid(from.getCuid());
     }
 
     @Override
@@ -645,12 +645,13 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
     /**
      * Create a detached, initialised WorkflowExecutionsRecord
      */
-    public WorkflowExecutionsRecord(Integer eid, Integer vid, Integer uid, 
Short status, String result, Timestamp startingTime, Timestamp lastUpdateTime, 
Boolean bookmarked, String name, String environmentVersion, String logLocation, 
String runtimeStatsUri, Integer runtimeStatsSize, Integer cuid) {
+    public WorkflowExecutionsRecord(Integer eid, Integer vid, Integer uid, 
Integer cuid, Short status, String result, Timestamp startingTime, Timestamp 
lastUpdateTime, Boolean bookmarked, String name, String environmentVersion, 
String logLocation, String runtimeStatsUri, Integer runtimeStatsSize) {
         super(WorkflowExecutions.WORKFLOW_EXECUTIONS);
 
         setEid(eid);
         setVid(vid);
         setUid(uid);
+        setCuid(cuid);
         setStatus(status);
         setResult(result);
         setStartingTime(startingTime);
@@ -661,7 +662,6 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
         setLogLocation(logLocation);
         setRuntimeStatsUri(runtimeStatsUri);
         setRuntimeStatsSize(runtimeStatsSize);
-        setCuid(cuid);
     }
 
     /**
@@ -674,6 +674,7 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
             setEid(value.getEid());
             setVid(value.getVid());
             setUid(value.getUid());
+            setCuid(value.getCuid());
             setStatus(value.getStatus());
             setResult(value.getResult());
             setStartingTime(value.getStartingTime());
@@ -684,7 +685,6 @@ public class WorkflowExecutionsRecord extends 
UpdatableRecordImpl<WorkflowExecut
             setLogLocation(value.getLogLocation());
             setRuntimeStatsUri(value.getRuntimeStatsUri());
             setRuntimeStatsSize(value.getRuntimeStatsSize());
-            setCuid(value.getCuid());
         }
     }
 }
diff --git 
a/core/file-service/src/main/scala/edu/uci/ics/texera/service/resource/DatasetResource.scala
 
b/core/file-service/src/main/scala/edu/uci/ics/texera/service/resource/DatasetResource.scala
index 55722395aa..d279cfe12c 100644
--- 
a/core/file-service/src/main/scala/edu/uci/ics/texera/service/resource/DatasetResource.scala
+++ 
b/core/file-service/src/main/scala/edu/uci/ics/texera/service/resource/DatasetResource.scala
@@ -39,8 +39,7 @@ import edu.uci.ics.texera.dao.jooq.generated.tables.daos.{
 import edu.uci.ics.texera.dao.jooq.generated.tables.pojos.{
   Dataset,
   DatasetUserAccess,
-  DatasetVersion,
-  User
+  DatasetVersion
 }
 import edu.uci.ics.texera.service.`type`.DatasetFileNode
 import edu.uci.ics.texera.service.resource.DatasetAccessResource.{
@@ -172,7 +171,8 @@ object DatasetResource {
   case class CreateDatasetRequest(
       datasetName: String,
       datasetDescription: String,
-      isDatasetPublic: Boolean
+      isDatasetPublic: Boolean,
+      isDatasetDownloadable: Boolean
   )
 
   case class Diff(
@@ -245,6 +245,12 @@ class DatasetResource {
       val datasetName = request.datasetName
       val datasetDescription = request.datasetDescription
       val isDatasetPublic = request.isDatasetPublic
+      val isDatasetDownloadable = request.isDatasetDownloadable
+
+      // Validate business rule: downloadable can only be true if dataset is 
public
+      if (isDatasetDownloadable && !isDatasetPublic) {
+        throw new BadRequestException("Dataset can only be downloadable if it 
is public")
+      }
 
       // Check if a dataset with the same name already exists
       if (!datasetDao.fetchByName(datasetName).isEmpty) {
@@ -266,6 +272,7 @@ class DatasetResource {
       dataset.setName(datasetName)
       dataset.setDescription(datasetDescription)
       dataset.setIsPublic(isDatasetPublic)
+      dataset.setIsDownloadable(isDatasetDownloadable)
       dataset.setOwnerUid(uid)
 
       val createdDataset = ctx
@@ -288,7 +295,8 @@ class DatasetResource {
           createdDataset.getName,
           createdDataset.getIsPublic,
           createdDataset.getDescription,
-          createdDataset.getCreationTime
+          createdDataset.getCreationTime,
+          createdDataset.getIsDownloadable
         ),
         user.getEmail,
         PrivilegeEnum.WRITE,
@@ -781,7 +789,43 @@ class DatasetResource {
       }
 
       val existedDataset = getDatasetByID(ctx, did)
-      existedDataset.setIsPublic(!existedDataset.getIsPublic)
+      val newPublicStatus = !existedDataset.getIsPublic
+      existedDataset.setIsPublic(newPublicStatus)
+
+      // If dataset becomes private, it must not be downloadable
+      if (!newPublicStatus) {
+        existedDataset.setIsDownloadable(false)
+      }
+
+      datasetDao.update(existedDataset)
+      Response.ok().build()
+    }
+  }
+
+  @POST
+  @RolesAllowed(Array("REGULAR", "ADMIN"))
+  @Path("/{did}/update/downloadable")
+  def toggleDatasetDownloadable(
+      @PathParam("did") did: Integer,
+      @Auth sessionUser: SessionUser
+  ): Response = {
+    withTransaction(context) { ctx =>
+      val datasetDao = new DatasetDao(ctx.configuration())
+      val uid = sessionUser.getUid
+
+      if (!userOwnDataset(ctx, did, uid)) {
+        throw new ForbiddenException("Only dataset owners can modify download 
permissions")
+      }
+
+      val existedDataset = getDatasetByID(ctx, did)
+      val newDownloadableStatus = !existedDataset.getIsDownloadable
+
+      // Validate business rule: can only set downloadable to true if dataset 
is public
+      if (newDownloadableStatus && !existedDataset.getIsPublic) {
+        throw new BadRequestException("Dataset can only be downloadable if it 
is public")
+      }
+
+      existedDataset.setIsDownloadable(newDownloadableStatus)
 
       datasetDao.update(existedDataset)
       Response.ok().build()
@@ -1015,6 +1059,19 @@ class DatasetResource {
         throw new BadRequestException("Specify exactly one: dvid=<ID> OR 
latest=true")
       }
 
+      // Check read access and download permission
+      val uid = user.getUid
+      if (!userHasReadAccess(ctx, did, uid)) {
+        throw new ForbiddenException(ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE)
+      }
+
+      // Retrieve dataset and check download permission
+      val dataset = getDatasetByID(ctx, did)
+      // Non-owners can only download public and downloadable datasets
+      if (!userOwnDataset(ctx, did, uid) && (!dataset.getIsPublic || 
!dataset.getIsDownloadable)) {
+        throw new ForbiddenException("Dataset download is not allowed")
+      }
+
       // Determine which version to retrieve
       val datasetVersion = if (dvid != null) {
         getDatasetVersionByID(ctx, dvid)
@@ -1027,7 +1084,6 @@ class DatasetResource {
       }
 
       // Retrieve dataset and version details
-      val dataset = getDatasetByID(ctx, did)
       val datasetName = dataset.getName
       val versionHash = datasetVersion.getVersionHash
       val objects = LakeFSStorageClient.retrieveObjectsOfVersion(datasetName, 
versionHash)
@@ -1248,6 +1304,27 @@ class DatasetResource {
         errorResponse
 
       case Right((resolvedDatasetName, resolvedCommitHash, resolvedFilePath)) 
=>
+        // Additional download permission check for S3 downloads
+        if (uid != null) {
+          withTransaction(context) { ctx =>
+            val datasetDao = new DatasetDao(ctx.configuration())
+            val datasets = 
datasetDao.fetchByName(resolvedDatasetName).asScala.toList
+            if (datasets.nonEmpty) {
+              val dataset = datasets.head
+              // Non-owners can only download public and downloadable datasets
+              if (
+                !userOwnDataset(
+                  ctx,
+                  dataset.getDid,
+                  uid
+                ) && (!dataset.getIsPublic || !dataset.getIsDownloadable)
+              ) {
+                throw new ForbiddenException("Dataset download is not allowed")
+              }
+            }
+          }
+        }
+
         val fileName = 
resolvedFilePath.split("/").lastOption.getOrElse("download")
         val contentType = "application/octet-stream"
         val url = S3StorageClient.getFilePresignedUrl(
@@ -1292,6 +1369,10 @@ class DatasetResource {
           if (datasets.isEmpty || !userHasReadAccess(ctx, 
datasets.head.getDid, uid))
             throw new 
ForbiddenException(ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE)
 
+          val dataset = datasets.head
+          // Standard read access check only - download restrictions handled 
per endpoint
+          // Non-download operations (viewing) should work for all public 
datasets
+
           (dsName, commit, decodedPathStr)
         }
         Right(response)
@@ -1307,6 +1388,10 @@ class DatasetResource {
           if (datasets.isEmpty || !userHasReadAccess(ctx, 
datasets.head.getDid, uid))
             throw new 
ForbiddenException(ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE)
 
+          val dataset = datasets.head
+          // Standard read access check only - download restrictions handled 
per endpoint
+          // Non-download operations (viewing) should work for all public 
datasets
+
           (
             document.getDatasetName(),
             document.getVersionHash(),
diff --git a/core/gui/src/app/common/type/dataset.ts 
b/core/gui/src/app/common/type/dataset.ts
index 01f2db5348..7825ca2797 100644
--- a/core/gui/src/app/common/type/dataset.ts
+++ b/core/gui/src/app/common/type/dataset.ts
@@ -34,6 +34,7 @@ export interface Dataset {
   ownerUid: number | undefined;
   name: string;
   isPublic: boolean;
+  isDownloadable: boolean;
   storagePath: string | undefined;
   description: string;
   creationTime: number | undefined;
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.html
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.html
index a762930851..ad982cf99a 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.html
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.html
@@ -29,13 +29,30 @@
     <div
       class="workflow-panel"
       style="padding-top: 30px">
-      <nz-switch
-        [ngModel]="datasetIsPublic"
-        (ngModelChange)="onPublicStatusChange($event)"
-        [nzDisabled]="userDatasetAccessLevel !== 'WRITE'"
+      <div
         *ngIf="!(userDatasetAccessLevel === 'NONE')"
-        nzCheckedChildren="public"
-        nzUnCheckedChildren="private"></nz-switch>
+        class="dataset-controls">
+        <div class="control-row">
+          <label>Visibility:</label>
+          <nz-switch
+            [ngModel]="datasetIsPublic"
+            (ngModelChange)="onPublicStatusChange($event)"
+            [nzDisabled]="userDatasetAccessLevel !== 'WRITE'"
+            nzCheckedChildren="public"
+            nzUnCheckedChildren="private"></nz-switch>
+        </div>
+        <div
+          class="control-row"
+          *ngIf="isOwner">
+          <label>Downloads:</label>
+          <nz-switch
+            [ngModel]="datasetIsDownloadable"
+            (ngModelChange)="onDownloadableStatusChange($event)"
+            [nzDisabled]="!datasetIsPublic"
+            nzCheckedChildren="allowed"
+            nzUnCheckedChildren="blocked"></nz-switch>
+        </div>
+      </div>
       <button
         nz-button
         nzType="text"
@@ -92,7 +109,7 @@
             nz-button
             *ngIf="selectedVersion"
             nz-tooltip="Download the file"
-            [disabled]="!isLogin"
+            [disabled]="!isLogin || !isDownloadAllowed()"
             (click)="onClickDownloadCurrentFile()">
             <i
               nz-icon
@@ -205,7 +222,7 @@
                 nz-tooltip="Download Dataset"
                 (click)="onClickDownloadVersionAsZip()"
                 *ngIf="selectedVersion"
-                [disabled]="!isLogin"
+                [disabled]="!isLogin || !isDownloadAllowed()"
                 class="spaced-button">
                 <i
                   nz-icon
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.scss
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.scss
index a6a0cbcdfe..3bddc6de15 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.scss
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.scss
@@ -193,3 +193,33 @@ nz-select {
 .version-input {
   padding: 6px;
 }
+
+.dataset-controls {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  margin-right: 20px;
+}
+
+.control-row {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+
+  label {
+    min-width: 80px;
+    font-weight: 500;
+    color: #666;
+  }
+
+  nz-switch {
+    margin-left: auto;
+  }
+
+  .help-text {
+    margin-left: 8px;
+    font-size: 12px;
+    color: #999;
+    font-style: italic;
+  }
+}
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.ts
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.ts
index c223f7385b..f563142e0c 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.ts
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.ts
@@ -56,7 +56,9 @@ export class DatasetDetailComponent implements OnInit {
   public datasetDescription: string = "";
   public datasetCreationTime: string = "";
   public datasetIsPublic: boolean = false;
+  public datasetIsDownloadable: boolean = true;
   public userDatasetAccessLevel: "READ" | "WRITE" | "NONE" = "NONE";
+  public isOwner: boolean = false;
 
   public currentDisplayedFileName: string = "";
   public currentFileSize: number | undefined;
@@ -216,6 +218,10 @@ export class DatasetDetailComponent implements OnInit {
         .subscribe({
           next: (res: Response) => {
             this.datasetIsPublic = checked;
+            // If dataset becomes private, it cannot be downloadable
+            if (!checked) {
+              this.datasetIsDownloadable = false;
+            }
             let state = "public";
             if (!this.datasetIsPublic) {
               state = "private";
@@ -229,6 +235,34 @@ export class DatasetDetailComponent implements OnInit {
     }
   }
 
+  onDownloadableStatusChange(checked: boolean): void {
+    // Only allow downloadable change if dataset is public
+    if (checked && !this.datasetIsPublic) {
+      this.notificationService.error("Dataset can only be downloadable if it 
is public");
+      return;
+    }
+
+    // Handle the change in dataset downloadable status
+    if (this.did) {
+      this.datasetService
+        .updateDatasetDownloadable(this.did)
+        .pipe(untilDestroyed(this))
+        .subscribe({
+          next: (res: Response) => {
+            this.datasetIsDownloadable = checked;
+            let state = "allowed";
+            if (!this.datasetIsDownloadable) {
+              state = "not allowed";
+            }
+            this.notificationService.success(`Dataset downloads are now 
${state}`);
+          },
+          error: (err: unknown) => {
+            this.notificationService.error("Failed to change the dataset 
download permission");
+          },
+        });
+    }
+  }
+
   retrieveDatasetInfo() {
     if (this.did) {
       this.datasetService
@@ -240,6 +274,8 @@ export class DatasetDetailComponent implements OnInit {
           this.datasetDescription = dataset.description;
           this.userDatasetAccessLevel = dashboardDataset.accessPrivilege;
           this.datasetIsPublic = dataset.isPublic;
+          this.datasetIsDownloadable = dataset.isDownloadable;
+          this.isOwner = dashboardDataset.isOwner;
           if (typeof dataset.creationTime === "number") {
             this.datasetCreationTime = new 
Date(dataset.creationTime).toString();
           }
@@ -311,6 +347,15 @@ export class DatasetDetailComponent implements OnInit {
     return this.userDatasetAccessLevel == "WRITE";
   }
 
+  isDownloadAllowed(): boolean {
+    // Owners can always download
+    if (this.isOwner) {
+      return true;
+    }
+    // Non-owners can only download if dataset is public and downloadable
+    return this.datasetIsPublic && this.datasetIsDownloadable;
+  }
+
   // Track multiple file by unique key
   trackByTask(_: number, task: MultipartUploadProgress & { filePath: string 
}): string {
     return task.filePath;
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.html
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.html
index b629d14ec0..cfc530b65a 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.html
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.html
@@ -29,12 +29,27 @@
         [form]="form"
         [fields]="fields"
         [model]="model"></formly-form>
-      <nz-switch
+      <div
         *ngIf="!isCreatingVersion"
-        [ngModel]="isDatasetPublic"
-        (ngModelChange)="onPublicStatusChange($event)"
-        nzCheckedChildren="public"
-        nzUnCheckedChildren="private"></nz-switch>
+        class="dataset-toggles">
+        <div class="toggle-item">
+          <label>Visibility:</label>
+          <nz-switch
+            [ngModel]="isDatasetPublic"
+            (ngModelChange)="onPublicStatusChange($event)"
+            nzCheckedChildren="public"
+            nzUnCheckedChildren="private"></nz-switch>
+        </div>
+        <div class="toggle-item">
+          <label>Downloadable:</label>
+          <nz-switch
+            [ngModel]="isDatasetDownloadable"
+            (ngModelChange)="onDownloadableStatusChange($event)"
+            [nzDisabled]="!isDatasetPublic"
+            nzCheckedChildren="enabled"
+            nzUnCheckedChildren="disabled"></nz-switch>
+        </div>
+      </div>
     </div>
 
     <button
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.scss
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.scss
index 1b6fde0c30..c6d9dda556 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.scss
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.scss
@@ -60,3 +60,27 @@
   width: 90%;
   margin: auto;
 }
+
+.dataset-toggles {
+  margin-top: 16px;
+  margin-bottom: 16px;
+}
+
+.toggle-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 12px;
+
+  label {
+    margin-right: 12px;
+    min-width: 80px;
+    font-weight: 500;
+  }
+
+  .help-text {
+    margin-left: 8px;
+    font-size: 12px;
+    color: #999;
+    font-style: italic;
+  }
+}
diff --git 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts
 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts
index 40aa469e8f..f5912d4af1 100644
--- 
a/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts
+++ 
b/core/gui/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component.ts
@@ -44,6 +44,7 @@ export class UserDatasetVersionCreatorComponent implements 
OnInit {
   model: any = {};
   fields: FormlyFieldConfig[] = [];
   isDatasetPublic: boolean = false;
+  isDatasetDownloadable: boolean = false;
 
   // used when creating the dataset
   isDatasetNameSanitized: boolean = false;
@@ -168,6 +169,7 @@ export class UserDatasetVersionCreatorComponent implements 
OnInit {
         name: sanitizedName,
         description: this.form.get("description")?.value,
         isPublic: this.isDatasetPublic,
+        isDownloadable: this.isDatasetDownloadable,
         did: undefined,
         ownerUid: undefined,
         storagePath: undefined,
@@ -201,5 +203,15 @@ export class UserDatasetVersionCreatorComponent implements 
OnInit {
   onPublicStatusChange(newValue: boolean): void {
     // Handle the change in dataset public status
     this.isDatasetPublic = newValue;
+
+    // If dataset becomes private, disable downloads for security
+    if (!newValue) {
+      this.isDatasetDownloadable = false;
+    }
+  }
+
+  onDownloadableStatusChange(newValue: boolean): void {
+    // Handle the change in dataset downloadable status
+    this.isDatasetDownloadable = newValue;
   }
 }
diff --git a/core/gui/src/app/dashboard/service/user/dataset/dataset.service.ts 
b/core/gui/src/app/dashboard/service/user/dataset/dataset.service.ts
index 7a239a0df8..e4c9fbbf20 100644
--- a/core/gui/src/app/dashboard/service/user/dataset/dataset.service.ts
+++ b/core/gui/src/app/dashboard/service/user/dataset/dataset.service.ts
@@ -34,6 +34,7 @@ export const DATASET_UPDATE_BASE_URL = DATASET_BASE_URL + 
"/update";
 export const DATASET_UPDATE_NAME_URL = DATASET_UPDATE_BASE_URL + "/name";
 export const DATASET_UPDATE_DESCRIPTION_URL = DATASET_UPDATE_BASE_URL + 
"/description";
 export const DATASET_UPDATE_PUBLICITY_URL = "update/publicity";
+export const DATASET_UPDATE_DOWNLOADABLE_URL = "update/downloadable";
 export const DATASET_LIST_URL = DATASET_BASE_URL + "/list";
 export const DATASET_SEARCH_URL = DATASET_BASE_URL + "/search";
 export const DATASET_DELETE_URL = DATASET_BASE_URL + "/delete";
@@ -68,6 +69,7 @@ export class DatasetService {
       datasetName: dataset.name,
       datasetDescription: dataset.description,
       isDatasetPublic: dataset.isPublic,
+      isDatasetDownloadable: dataset.isDownloadable,
     });
   }
 
@@ -461,6 +463,13 @@ export class DatasetService {
     );
   }
 
+  public updateDatasetDownloadable(did: number): Observable<Response> {
+    return this.http.post<Response>(
+      
`${AppSettings.getApiEndpoint()}/${DATASET_BASE_URL}/${did}/${DATASET_UPDATE_DOWNLOADABLE_URL}`,
+      {}
+    );
+  }
+
   public retrieveOwners(): Observable<string[]> {
     return 
this.http.get<string[]>(`${AppSettings.getApiEndpoint()}/${DATASET_GET_OWNERS_URL}`);
   }
diff --git a/core/scripts/sql/texera_ddl.sql b/core/scripts/sql/texera_ddl.sql
index a19004712c..62d531d7f4 100644
--- a/core/scripts/sql/texera_ddl.sql
+++ b/core/scripts/sql/texera_ddl.sql
@@ -231,6 +231,7 @@ CREATE TABLE IF NOT EXISTS dataset
     owner_uid      INT NOT NULL,
     name           VARCHAR(128) NOT NULL,
     is_public      BOOLEAN NOT NULL DEFAULT TRUE,
+    is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
     description    VARCHAR(512) NOT NULL,
     creation_time  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
     FOREIGN KEY (owner_uid) REFERENCES "user"(uid) ON DELETE CASCADE
diff --git a/core/scripts/sql/updates/11.sql b/core/scripts/sql/updates/11.sql
new file mode 100644
index 0000000000..01798b9715
--- /dev/null
+++ b/core/scripts/sql/updates/11.sql
@@ -0,0 +1,28 @@
+-- Licensed to the Apache Software Foundation (ASF) under one
+-- or more contributor license agreements.  See the NOTICE file
+-- distributed with this work for additional information
+-- regarding copyright ownership.  The ASF licenses this file
+-- to you under the Apache License, Version 2.0 (the
+-- "License"); you may not use this file except in compliance
+-- with the License.  You may obtain a copy of the License at
+--
+--   http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing,
+-- software distributed under the License is distributed on an
+-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+-- KIND, either express or implied.  See the License for the
+-- specific language governing permissions and limitations
+-- under the License.
+
+\c texera_db
+
+SET search_path TO texera_db;
+
+BEGIN;
+-- Add downloadable field to dataset table
+ALTER TABLE dataset ADD COLUMN IF NOT EXISTS is_downloadable BOOLEAN NOT NULL 
DEFAULT TRUE;
+
+-- Update existing datasets: downloadable can only be true if dataset is public
+UPDATE dataset SET is_downloadable = is_public WHERE is_downloadable != 
is_public;
+COMMIT;
\ No newline at end of file
diff --git a/deployment/k8s/texera-helmchart/files/texera_ddl.sql 
b/deployment/k8s/texera-helmchart/files/texera_ddl.sql
index 73606c011e..0366adc4dd 100644
--- a/deployment/k8s/texera-helmchart/files/texera_ddl.sql
+++ b/deployment/k8s/texera-helmchart/files/texera_ddl.sql
@@ -265,6 +265,7 @@ CREATE TABLE IF NOT EXISTS dataset
     owner_uid      INT NOT NULL,
     name           VARCHAR(128) NOT NULL,
     is_public      BOOLEAN NOT NULL DEFAULT TRUE,
+    is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
     description    VARCHAR(512) NOT NULL,
     creation_time  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
     FOREIGN KEY (owner_uid) REFERENCES "user"(uid) ON DELETE CASCADE

Reply via email to