diff --git a/README.md b/README.md index 57ef53cd842..6088ef576ad 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,7 @@ This project is supported by the National Science F ## Citation Please cite Texera as ``` + @article{DBLP:journals/pvldb/WangHNKALLDL24, author = {Zuozhi Wang and Yicong Huang and diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Workflow.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Workflow.java index bec6659b827..fb8037b41a3 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Workflow.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/Workflow.java @@ -19,7 +19,7 @@ import org.jooq.Index; 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; @@ -35,7 +35,7 @@ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) public class Workflow extends TableImpl { - private static final long serialVersionUID = -908784271; + private static final long serialVersionUID = -1770381178; /** * The reference instance of texera_db.workflow @@ -80,6 +80,11 @@ public Class getRecordType() { */ public final TableField LAST_MODIFIED_TIME = createField(DSL.name("last_modified_time"), org.jooq.impl.SQLDataType.TIMESTAMP.nullable(false).defaultValue(org.jooq.impl.DSL.field("CURRENT_TIMESTAMP", org.jooq.impl.SQLDataType.TIMESTAMP)), this, ""); + /** + * The column texera_db.workflow.is_published. + */ + public final TableField IS_PUBLISHED = createField(DSL.name("is_published"), org.jooq.impl.SQLDataType.TINYINT.nullable(false).defaultValue(org.jooq.impl.DSL.inline("0", org.jooq.impl.SQLDataType.TINYINT)), this, ""); + /** * Create a texera_db.workflow table reference */ @@ -165,11 +170,11 @@ public Workflow rename(Name name) { } // ------------------------------------------------------------------------- - // Row6 type methods + // Row7 type methods // ------------------------------------------------------------------------- @Override - public Row6 fieldsRow() { - return (Row6) super.fieldsRow(); + public Row7 fieldsRow() { + return (Row7) super.fieldsRow(); } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/WorkflowDao.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/WorkflowDao.java index f5fb65ba286..6ba5b23af30 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/WorkflowDao.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/daos/WorkflowDao.java @@ -130,4 +130,18 @@ public List f public List fetchByLastModifiedTime(Timestamp... values) { return fetch(Workflow.WORKFLOW.LAST_MODIFIED_TIME, values); } + + /** + * Fetch records that have is_published BETWEEN lowerInclusive AND upperInclusive + */ + public List fetchRangeOfIsPublished(Byte lowerInclusive, Byte upperInclusive) { + return fetchRange(Workflow.WORKFLOW.IS_PUBLISHED, lowerInclusive, upperInclusive); + } + + /** + * Fetch records that have is_published IN (values) + */ + public List fetchByIsPublished(Byte... values) { + return fetch(Workflow.WORKFLOW.IS_PUBLISHED, values); + } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IWorkflow.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IWorkflow.java index fcd188fe700..003210d08e0 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IWorkflow.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/interfaces/IWorkflow.java @@ -76,6 +76,16 @@ public interface IWorkflow extends Serializable { */ public Timestamp getLastModifiedTime(); + /** + * Setter for texera_db.workflow.is_published. + */ + public void setIsPublished(Byte value); + + /** + * Getter for texera_db.workflow.is_published. + */ + public Byte getIsPublished(); + // ------------------------------------------------------------------------- // FROM and INTO // ------------------------------------------------------------------------- diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Workflow.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Workflow.java index 98aa423b443..b23054885b2 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Workflow.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/pojos/Workflow.java @@ -17,7 +17,7 @@ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) public class Workflow implements IWorkflow { - private static final long serialVersionUID = -380738907; + private static final long serialVersionUID = 171585219; private String name; private String description; @@ -25,6 +25,7 @@ public class Workflow implements IWorkflow { private String content; private Timestamp creationTime; private Timestamp lastModifiedTime; + private Byte isPublished; public Workflow() {} @@ -35,6 +36,7 @@ public Workflow(IWorkflow value) { this.content = value.getContent(); this.creationTime = value.getCreationTime(); this.lastModifiedTime = value.getLastModifiedTime(); + this.isPublished = value.getIsPublished(); } public Workflow( @@ -43,7 +45,8 @@ public Workflow( UInteger wid, String content, Timestamp creationTime, - Timestamp lastModifiedTime + Timestamp lastModifiedTime, + Byte isPublished ) { this.name = name; this.description = description; @@ -51,6 +54,7 @@ public Workflow( this.content = content; this.creationTime = creationTime; this.lastModifiedTime = lastModifiedTime; + this.isPublished = isPublished; } @Override @@ -113,6 +117,16 @@ public void setLastModifiedTime(Timestamp lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; } + @Override + public Byte getIsPublished() { + return this.isPublished; + } + + @Override + public void setIsPublished(Byte isPublished) { + this.isPublished = isPublished; + } + @Override public String toString() { StringBuilder sb = new StringBuilder("Workflow ("); @@ -123,6 +137,7 @@ public String toString() { sb.append(", ").append(content); sb.append(", ").append(creationTime); sb.append(", ").append(lastModifiedTime); + sb.append(", ").append(isPublished); sb.append(")"); return sb.toString(); @@ -140,6 +155,7 @@ public void from(IWorkflow from) { setContent(from.getContent()); setCreationTime(from.getCreationTime()); setLastModifiedTime(from.getLastModifiedTime()); + setIsPublished(from.getIsPublished()); } @Override diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/WorkflowRecord.java b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/WorkflowRecord.java index 1f16bfd5ef3..403bd5e6090 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/WorkflowRecord.java +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/model/jooq/generated/tables/records/WorkflowRecord.java @@ -11,8 +11,8 @@ 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; import org.jooq.types.UInteger; @@ -21,9 +21,9 @@ * This class is generated by jOOQ. */ @SuppressWarnings({ "all", "unchecked", "rawtypes" }) -public class WorkflowRecord extends UpdatableRecordImpl implements Record6, IWorkflow { +public class WorkflowRecord extends UpdatableRecordImpl implements Record7, IWorkflow { - private static final long serialVersionUID = 948369234; + private static final long serialVersionUID = 702670612; /** * Setter for texera_db.workflow.name. @@ -121,6 +121,22 @@ public Timestamp getLastModifiedTime() { return (Timestamp) get(5); } + /** + * Setter for texera_db.workflow.is_published. + */ + @Override + public void setIsPublished(Byte value) { + set(6, value); + } + + /** + * Getter for texera_db.workflow.is_published. + */ + @Override + public Byte getIsPublished() { + return (Byte) get(6); + } + // ------------------------------------------------------------------------- // Primary key information // ------------------------------------------------------------------------- @@ -131,17 +147,17 @@ public Record1 key() { } // ------------------------------------------------------------------------- - // Record6 type implementation + // Record7 type implementation // ------------------------------------------------------------------------- @Override - public Row6 fieldsRow() { - return (Row6) super.fieldsRow(); + public Row7 fieldsRow() { + return (Row7) super.fieldsRow(); } @Override - public Row6 valuesRow() { - return (Row6) super.valuesRow(); + public Row7 valuesRow() { + return (Row7) super.valuesRow(); } @Override @@ -174,6 +190,11 @@ public Field field6() { return Workflow.WORKFLOW.LAST_MODIFIED_TIME; } + @Override + public Field field7() { + return Workflow.WORKFLOW.IS_PUBLISHED; + } + @Override public String component1() { return getName(); @@ -204,6 +225,11 @@ public Timestamp component6() { return getLastModifiedTime(); } + @Override + public Byte component7() { + return getIsPublished(); + } + @Override public String value1() { return getName(); @@ -234,6 +260,11 @@ public Timestamp value6() { return getLastModifiedTime(); } + @Override + public Byte value7() { + return getIsPublished(); + } + @Override public WorkflowRecord value1(String value) { setName(value); @@ -271,13 +302,20 @@ public WorkflowRecord value6(Timestamp value) { } @Override - public WorkflowRecord values(String value1, String value2, UInteger value3, String value4, Timestamp value5, Timestamp value6) { + public WorkflowRecord value7(Byte value) { + setIsPublished(value); + return this; + } + + @Override + public WorkflowRecord values(String value1, String value2, UInteger value3, String value4, Timestamp value5, Timestamp value6, Byte value7) { value1(value1); value2(value2); value3(value3); value4(value4); value5(value5); value6(value6); + value7(value7); return this; } @@ -293,6 +331,7 @@ public void from(IWorkflow from) { setContent(from.getContent()); setCreationTime(from.getCreationTime()); setLastModifiedTime(from.getLastModifiedTime()); + setIsPublished(from.getIsPublished()); } @Override @@ -315,7 +354,7 @@ public WorkflowRecord() { /** * Create a detached, initialised WorkflowRecord */ - public WorkflowRecord(String name, String description, UInteger wid, String content, Timestamp creationTime, Timestamp lastModifiedTime) { + public WorkflowRecord(String name, String description, UInteger wid, String content, Timestamp creationTime, Timestamp lastModifiedTime, Byte isPublished) { super(Workflow.WORKFLOW); set(0, name); @@ -324,5 +363,6 @@ public WorkflowRecord(String name, String description, UInteger wid, String cont set(3, content); set(4, creationTime); set(5, lastModifiedTime); + set(6, isPublished); } } diff --git a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala index dc27c3212b5..89c67f1c823 100644 --- a/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala +++ b/core/amber/src/main/scala/edu/uci/ics/texera/web/resource/dashboard/user/workflow/WorkflowResource.scala @@ -89,6 +89,7 @@ object WorkflowResource { content: String, creationTime: Timestamp, lastModifiedTime: Timestamp, + isPublished: Byte, readonly: Boolean ) @@ -265,6 +266,7 @@ class WorkflowResource extends LazyLogging { workflow.getContent, workflow.getCreationTime, workflow.getLastModifiedTime, + workflow.getIsPublished, !WorkflowAccessResource.hasWriteAccess(wid, user.getUid) ) } else { @@ -350,7 +352,8 @@ class WorkflowResource extends LazyLogging { null, workflow.getContent, null, - null + null, + 0.toByte ), sessionUser ) @@ -378,6 +381,28 @@ class WorkflowResource extends LazyLogging { resultWorkflows.toList } + @POST + @Consumes(Array(MediaType.APPLICATION_JSON)) + @Produces(Array(MediaType.APPLICATION_JSON)) + @Path("/clone/{wid}") + def cloneWorkflow(@PathParam("wid") wid: UInteger, @Auth sessionUser: SessionUser): UInteger = { + val workflow: Workflow = workflowDao.fetchOneByWid(wid) + val newWorkflow: DashboardWorkflow = createWorkflow( + new Workflow( + workflow.getName + "_clone", + workflow.getDescription, + null, + workflow.getContent, + null, + null, + 0.toByte + ), + sessionUser + ) + //TODO: copy the environment as well + newWorkflow.workflow.getWid + } + /** * This method creates and insert a new workflow to database * @@ -482,4 +507,34 @@ class WorkflowResource extends LazyLogging { workflowDao.update(userWorkflow) } } + + @PUT + @RolesAllowed(Array("REGULAR", "ADMIN")) + @Path("/public/{wid}") + def makePublic(@PathParam("wid") wid: UInteger, @Auth user: SessionUser): Unit = { + println(wid + " is public now") + val workflow: Workflow = workflowDao.fetchOneByWid(wid) + workflow.setIsPublished(1.toByte) + workflowDao.update(workflow) + } + + @PUT + @RolesAllowed(Array("REGULAR", "ADMIN")) + @Path("/private/{wid}") + def makePrivate(@PathParam("wid") wid: UInteger): Unit = { + println(wid + " is private now") + val workflow: Workflow = workflowDao.fetchOneByWid(wid) + workflow.setIsPublished(0.toByte) + workflowDao.update(workflow) + } + + @GET + @Path("/type/{wid}") + def getWorkflowType(@PathParam("wid") wid: UInteger): String = { + val workflow: Workflow = workflowDao.fetchOneByWid(wid) + if (workflow.getIsPublished() == 1.toByte) + "Public" + else + "Private" + } } diff --git a/core/gui/src/app/common/service/workflow-persist/workflow-persist.service.ts b/core/gui/src/app/common/service/workflow-persist/workflow-persist.service.ts index cfc499b67e2..57ef6512745 100644 --- a/core/gui/src/app/common/service/workflow-persist/workflow-persist.service.ts +++ b/core/gui/src/app/common/service/workflow-persist/workflow-persist.service.ts @@ -46,6 +46,7 @@ export class WorkflowPersistService { name: workflow.name, description: workflow.description, content: JSON.stringify(workflow.content), + isPublished: workflow.isPublished, }) .pipe( filter((updatedWorkflow: Workflow) => updatedWorkflow != null), @@ -169,6 +170,18 @@ export class WorkflowPersistService { ); } + public getWorkflowIsPublished(wid: number): Observable { + return this.http.get(`${AppSettings.getApiEndpoint()}/${WORKFLOW_BASE_URL}/type/${wid}`, { responseType: "text" }); + } + + public updateWorkflowIsPublished(wid: number, isPublished: boolean): Observable { + if (isPublished) { + return this.http.put(`${AppSettings.getApiEndpoint()}/${WORKFLOW_BASE_URL}/public/${wid}`, null); + } else { + return this.http.put(`${AppSettings.getApiEndpoint()}/${WORKFLOW_BASE_URL}/private/${wid}`, null); + } + } + public setWorkflowPersistFlag(flag: boolean): void { this.workflowPersistFlag = flag; } diff --git a/core/gui/src/app/dashboard/component/admin/execution/admin-execution.component.ts b/core/gui/src/app/dashboard/component/admin/execution/admin-execution.component.ts index e6c7f5d1199..fb68550f651 100644 --- a/core/gui/src/app/dashboard/component/admin/execution/admin-execution.component.ts +++ b/core/gui/src/app/dashboard/component/admin/execution/admin-execution.component.ts @@ -122,6 +122,7 @@ export class AdminExecutionComponent implements OnInit, OnDestroy { description: "", creationTime: 0, lastModifiedTime: 0, + isPublished: 0, readonly: false, }; diff --git a/core/gui/src/app/dashboard/component/user-dashboard-test-fixtures.ts b/core/gui/src/app/dashboard/component/user-dashboard-test-fixtures.ts index bb7e88344a5..b91e0c3b3d8 100644 --- a/core/gui/src/app/dashboard/component/user-dashboard-test-fixtures.ts +++ b/core/gui/src/app/dashboard/component/user-dashboard-test-fixtures.ts @@ -32,6 +32,7 @@ export const testWorkflow1: Workflow = { content: testWorkflowContent(["Aggregation", "NlpSentiment", "SimpleSink"]), creationTime: januaryFirst1970, lastModifiedTime: januaryFirst1970 + 2, + isPublished: 0, readonly: false, }; @@ -42,6 +43,7 @@ export const testWorkflow2: Workflow = { content: testWorkflowContent(["Aggregation", "NlpSentiment", "SimpleSink"]), creationTime: januaryFirst1970 + (oneDay + 3), lastModifiedTime: januaryFirst1970 + (oneDay + 3), + isPublished: 0, readonly: false, }; @@ -52,6 +54,7 @@ export const testWorkflow3: Workflow = { content: testWorkflowContent(["Aggregation", "NlpSentiment"]), creationTime: januaryFirst1970 + oneDay, lastModifiedTime: januaryFirst1970 + (oneDay + 4), + isPublished: 0, readonly: false, }; @@ -62,6 +65,7 @@ export const testWorkflow4: Workflow = { content: testWorkflowContent([]), creationTime: januaryFirst1970 + (oneDay + 3) * 2, lastModifiedTime: januaryFirst1970 + oneDay * 2 + 6, + isPublished: 0, readonly: false, }; @@ -72,6 +76,7 @@ export const testWorkflow5: Workflow = { content: testWorkflowContent([]), creationTime: januaryFirst1970 + oneDay * 2, lastModifiedTime: januaryFirst1970 + oneDay * 2 + 8, + isPublished: 0, readonly: false, }; @@ -82,6 +87,7 @@ export const testDownloadWorkflow1: Workflow = { content: testWorkflowContent([]), creationTime: januaryFirst1970, //januaryFirst1970 is 1970-01-01 in PST lastModifiedTime: januaryFirst1970 + 2, + isPublished: 0, readonly: false, }; @@ -92,6 +98,7 @@ export const testDownloadWorkflow2: Workflow = { content: testWorkflowContent([]), creationTime: januaryFirst1970 + (oneDay + 3), // oneDay is the number of milliseconds in a day lastModifiedTime: januaryFirst1970 + (oneDay + 3), + isPublished: 0, readonly: false, }; @@ -102,6 +109,7 @@ export const testDownloadWorkflow3: Workflow = { content: testWorkflowContent([]), creationTime: januaryFirst1970 + oneDay, lastModifiedTime: januaryFirst1970 + (oneDay + 4), + isPublished: 0, readonly: false, }; diff --git a/core/gui/src/app/dashboard/component/user/list-item/list-item.component.ts b/core/gui/src/app/dashboard/component/user/list-item/list-item.component.ts index 8c79f7896c2..35779bf0671 100644 --- a/core/gui/src/app/dashboard/component/user/list-item/list-item.component.ts +++ b/core/gui/src/app/dashboard/component/user/list-item/list-item.component.ts @@ -101,10 +101,12 @@ export class ListItemComponent implements OnInit, OnChanges { type: this.entry.type, id: this.entry.id, allOwners: await firstValueFrom(this.workflowPersistService.retrieveOwners()), + inWorkspace: false, }, nzFooter: null, nzTitle: "Share this workflow with others", nzCentered: true, + nzWidth: "700px", }); } else if (this.entry.type === "dataset") { this.modalService.create({ @@ -117,6 +119,7 @@ export class ListItemComponent implements OnInit, OnChanges { nzFooter: null, nzTitle: "Share this dataset with others", nzCentered: true, + nzWidth: "700px", }); } } diff --git a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.html b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.html index 851464b6dff..5a520a50857 100644 --- a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.html +++ b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.html @@ -1,3 +1,41 @@ +
+ + + +
+
diff --git a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.scss b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.scss index e25f9ce23c7..6beaacc500f 100644 --- a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.scss +++ b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.scss @@ -11,8 +11,45 @@ .add-button { margin-left: 8px; white-space: nowrap; - padding: 8 18px; + padding: 8px 18px; height: 25px; font-size: 14px; line-height: 1; } + +.access-button-group { + display: flex; + flex-direction: row; + justify-content: space-evenly; + align-items: center; +} + +.access-button { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-evenly; + height: 150px; + width: 300px; + padding: 20px; + margin-bottom: 24px; + border-radius: 10px; +} + +.button-icon { + font-size: 40px; +} + +.button-text > p { + font-size: 12px; + font-weight: lighter; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; +} + +.button-text-header { + font-size: 25px; + font-weight: bold; +} diff --git a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.ts b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.ts index 5bf4a6e7583..8d8f21b2ef0 100644 --- a/core/gui/src/app/dashboard/component/user/share-access/share-access.component.ts +++ b/core/gui/src/app/dashboard/component/user/share-access/share-access.component.ts @@ -1,19 +1,22 @@ -import { Component, inject, OnInit } from "@angular/core"; +import { Component, inject, Input, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms"; import { ShareAccessService } from "../../../service/user/share-access/share-access.service"; import { ShareAccess } from "../../../type/share-access.interface"; import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { UserService } from "../../../../common/service/user/user.service"; import { GmailService } from "../../../../common/service/gmail/gmail.service"; -import { NZ_MODAL_DATA } from "ng-zorro-antd/modal"; +import { NZ_MODAL_DATA, NzModalRef, NzModalService } from "ng-zorro-antd/modal"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { HttpErrorResponse } from "@angular/common/http"; import { NzMessageService } from "ng-zorro-antd/message"; +import { DatasetService } from "../../../service/user/dataset/dataset.service"; +import { WorkflowPersistService } from "src/app/common/service/workflow-persist/workflow-persist.service"; +import { WorkflowActionService } from "src/app/workspace/service/workflow-graph/model/workflow-action.service"; @UntilDestroy() @Component({ - templateUrl: "share-access.component.html", selector: "texera-share-access", + templateUrl: "share-access.component.html", styleUrls: ["./share-access.component.scss"], }) export class ShareAccessComponent implements OnInit { @@ -23,6 +26,7 @@ export class ShareAccessComponent implements OnInit { readonly id: number = this.nzModalData.id; readonly allOwners: string[] = this.nzModalData.allOwners; + readonly inWorkspace: boolean = this.nzModalData.inWorkspace; public validateForm: FormGroup; public accessList: ReadonlyArray = []; public owner: string = ""; @@ -30,6 +34,7 @@ export class ShareAccessComponent implements OnInit { public ownerSearchValue?: string; public emailTags: string[] = []; currentEmail: string | undefined = ""; + isPublic: boolean | null = null; constructor( private accessService: ShareAccessService, @@ -37,7 +42,11 @@ export class ShareAccessComponent implements OnInit { private userService: UserService, private gmailService: GmailService, private notificationService: NotificationService, - private message: NzMessageService + private message: NzMessageService, + private modalService: NzModalService, + private workflowPersistService: WorkflowPersistService, + private datasetService: DatasetService, + private workflowActionService: WorkflowActionService ) { this.validateForm = this.formBuilder.group({ email: [null, Validators.email], @@ -57,6 +66,21 @@ export class ShareAccessComponent implements OnInit { .subscribe(name => { this.owner = name; }); + if (this.type === "workflow") { + this.workflowPersistService + .getWorkflowIsPublished(this.id) + .pipe(untilDestroyed(this)) + .subscribe(dashboardWorkflow => { + this.isPublic = dashboardWorkflow === "Public"; + }); + } else if (this.type === "dataset") { + this.datasetService + .getDataset(this.id) + .pipe(untilDestroyed(this)) + .subscribe(dashboardDataset => { + this.isPublic = dashboardDataset.dataset.isPublic === 1; + }); + } } public handleInputConfirm(event?: Event): void { @@ -148,4 +172,124 @@ export class ShareAccessComponent implements OnInit { .pipe(untilDestroyed(this)) .subscribe(() => this.ngOnInit()); } + verifyPublish(): void { + if (!this.isPublic) { + const modal: NzModalRef = this.modalService.create({ + nzTitle: "Notice", + nzContent: `Publishing your ${this.type} would grant all Texera users read access to your ${this.type} along with the right to clone your work.`, + nzFooter: [ + { + label: "Cancel", + onClick: () => modal.close(), + }, + { + label: "Publish", + type: "primary", + onClick: () => { + if (this.type === "workflow") { + this.publishWorkflow(); + + if (this.inWorkspace) { + this.workflowActionService.setWorkflowIsPublished(1); + } + } else if (this.type === "dataset") { + this.publishDataset(); + } + modal.close(); + }, + }, + ], + }); + } + } + + verifyUnpublish(): void { + if (this.isPublic) { + const modal: NzModalRef = this.modalService.create({ + nzTitle: "Notice", + nzContent: `All other users would lose access to your ${this.type} if you unpublish it.`, + nzFooter: [ + { + label: "Cancel", + onClick: () => modal.close(), + }, + { + label: "Unpublish", + type: "primary", + onClick: () => { + if (this.type === "workflow") { + this.unpublishWorkflow(); + if (this.inWorkspace) { + this.workflowActionService.setWorkflowIsPublished(0); + } + } else if (this.type === "dataset") { + this.unpublishDataset(); + } + modal.close(); + }, + }, + ], + }); + } + } + + public publishWorkflow(): void { + if (!this.isPublic) { + console.log("Workflow " + this.id + " is published"); + this.workflowPersistService + .updateWorkflowIsPublished(this.id, true) + .pipe(untilDestroyed(this)) + .subscribe(() => (this.isPublic = true)); + } else { + console.log("Workflow " + this.id + " is already published"); + } + } + + public unpublishWorkflow(): void { + if (this.isPublic) { + console.log("Workflow " + this.id + " is unpublished"); + this.workflowPersistService + .updateWorkflowIsPublished(this.id, false) + .pipe(untilDestroyed(this)) + .subscribe(() => (this.isPublic = false)); + } else { + console.log("Workflow " + this.id + " is already private"); + } + } + + public publishDataset(): void { + if (!this.isPublic) { + this.datasetService + .updateDatasetPublicity(this.id) + .pipe(untilDestroyed(this)) + .subscribe({ + next: (res: Response) => { + this.isPublic = true; + }, + error: (err: unknown) => { + this.notificationService.error("Failed to publish the dataset"); + }, + }); + } else { + console.log("Dataset " + this.id + " is already private"); + } + } + + public unpublishDataset(): void { + if (this.isPublic) { + this.datasetService + .updateDatasetPublicity(this.id) + .pipe(untilDestroyed(this)) + .subscribe({ + next: (res: Response) => { + this.isPublic = false; + }, + error: (err: unknown) => { + this.notificationService.error("Failed to unpublish the dataset"); + }, + }); + } else { + console.log("Dataset " + this.id + " is already private"); + } + } } diff --git a/core/gui/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.html b/core/gui/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.html index 7e9beb55dc9..4800805f523 100644 --- a/core/gui/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.html +++ b/core/gui/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.html @@ -153,17 +153,6 @@
    - -
    - -
    -
    +