Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1326,4 +1326,44 @@ class DatasetResource {
Right(response)
}
}

@POST
@RolesAllowed(Array("REGULAR", "ADMIN"))
@Path("/{did}/update/cover")
def updateDatasetCoverImage(
@PathParam("did") did: Integer,
coverImage: String,
@Auth sessionUser: SessionUser
): Response = {
withTransaction(context) { ctx =>
val uid = sessionUser.getUid
val dataset = getDatasetByID(ctx, did)
if (!userHasWriteAccess(ctx, did, uid)) {
throw new ForbiddenException(ERR_USER_HAS_NO_ACCESS_TO_DATASET_MESSAGE)
}

val document = DocumentFactory
.openReadonlyDocument(
FileResolver.resolve(s"${getOwner(ctx, did).getEmail}/${dataset.getName}/$coverImage")
)
.asInstanceOf[OnDataset]

val file = LakeFSStorageClient.getFileFromRepo(
document.getRepositoryName(),
document.getVersionHash(),
document.getFileRelativePath()
)
val coverSizeLimit = 10 * 1024 * 1024 // 10 MB

if (file.length() > coverSizeLimit) {
throw new BadRequestException(
s"Cover image must be less than ${coverSizeLimit / (1024 * 1024)} MB"
)
}

dataset.setCoverImage(coverImage)
new DatasetDao(ctx.configuration()).update(dataset)
Response.ok().build()
}
}
}
1 change: 1 addition & 0 deletions frontend/src/app/common/type/dataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ export interface Dataset {
storagePath: string | undefined;
description: string;
creationTime: number | undefined;
coverImage: string | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ <h6 style="font-weight: lighter; font-size: 0.9em">Choose a Version:</h6>
[fileTreeNodes]="fileTreeNodeList"
[isTreeNodeDeletable]="true"
(selectedTreeNode)="onVersionFileTreeNodeSelected($event)"
(deletedTreeNode)="onPreviouslyUploadedFileDeleted($event)">
(deletedTreeNode)="onPreviouslyUploadedFileDeleted($event)"
(setCoverImage)="onSetCoverImage($event)">
</texera-user-dataset-version-filetree>
</nz-collapse-panel>
</nz-collapse>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,4 +657,22 @@ export class DatasetDetailComponent implements OnInit {
changeViewDisplayStyle() {
this.displayPreciseViewCount = !this.displayPreciseViewCount;
}

onSetCoverImage(filePath: string): void {
if (!this.did || !this.selectedVersion) {
return;
}

this.datasetService
.updateDatasetCoverImage(this.did, `${this.selectedVersion.name}/${filePath}`)
.pipe(untilDestroyed(this))
.subscribe({
next: () => {
this.notificationService.success("Cover image set successfully");
},
error: (err: HttpErrorResponse) => {
this.notificationService.error(err.error?.message || "Failed to set cover image");
},
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export class UserDatasetVersionCreatorComponent implements OnInit {
ownerUid: undefined,
storagePath: undefined,
creationTime: undefined,
coverImage: undefined,
};
this.datasetService
.createDataset(ds)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,26 @@
nz-button
nzType="link"
*ngIf="isTreeNodeDeletable && !node.data.children"
class="delete-button"
class="icon-button"
(click)="onNodeDeleted(node.data)">
<i
nz-icon
nzType="delete"
nzTheme="outline"></i>
</button>

<button
nz-button
nzType="link"
*ngIf="!node.data.children && isImageFile(node.data.name)"
class="icon-button"
nz-tooltip="Set as cover"
(click)="onSetCover(node.data)">
<i
nz-icon
nzType="picture"
nzTheme="outline"></i>
</button>
</span>
</ng-template>
</tree-root>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}

/* Styles for the delete button */
.delete-button {
.icon-button {
width: 15px;
margin-left: 5px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@

import { UntilDestroy } from "@ngneat/until-destroy";
import { AfterViewInit, Component, EventEmitter, Input, Output, ViewChild } from "@angular/core";
import { DatasetFileNode } from "../../../../../../common/type/datasetVersionFileTree";
import {
DatasetFileNode,
getRelativePathFromDatasetFileNode,
} from "../../../../../../common/type/datasetVersionFileTree";
import { ITreeOptions, TREE_ACTIONS } from "@ali-hm/angular-tree-component";

@UntilDestroy()
Expand All @@ -40,6 +43,9 @@ export class UserDatasetVersionFiletreeComponent implements AfterViewInit {

@ViewChild("tree") tree: any;

@Output()
setCoverImage = new EventEmitter<string>();

public fileTreeDisplayOptions: ITreeOptions = {
displayField: "name",
hasChildrenField: "children",
Expand Down Expand Up @@ -74,4 +80,14 @@ export class UserDatasetVersionFiletreeComponent implements AfterViewInit {
this.tree.treeModel.expandAll();
}
}

isImageFile(fileName: string): boolean {
const imageExts = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add constant on top of file

return imageExts.some(ext => fileName.toLowerCase().endsWith(ext));
}

onSetCover(nodeData: DatasetFileNode): void {
const path = getRelativePathFromDatasetFileNode(nodeData);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be written in one line

this.setCoverImage.emit(path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,8 @@ export class DatasetService {
public retrieveOwners(): Observable<string[]> {
return this.http.get<string[]>(`${AppSettings.getApiEndpoint()}/${DATASET_GET_OWNERS_URL}`);
}

public updateDatasetCoverImage(did: number, coverImagePath: string): Observable<Response> {
return this.http.post<Response>(`${AppSettings.getApiEndpoint()}/dataset/${did}/update/cover`, coverImagePath);
}
}
2 changes: 2 additions & 0 deletions frontend/src/app/dashboard/type/dashboard-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class DashboardEntry {
likeCount: number;
isLiked: boolean;
accessibleUserIds: number[];
coverImageUrl?: string;

constructor(public value: DashboardWorkflow | DashboardProject | DashboardFile | DashboardDataset) {
if (isDashboardWorkflow(value)) {
Expand Down Expand Up @@ -122,6 +123,7 @@ export class DashboardEntry {
this.likeCount = 0;
this.isLiked = false;
this.accessibleUserIds = [];
this.coverImageUrl = value.dataset.coverImage;
} else {
throw new Error("Unexpected type in DashboardEntry.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ <h1 class="card-title">{{ entity.name }}</h1>
<img
alt="example"
class="card-cover-image"
[src]="defaultBackground" />
[src]="getCoverImage(entity)"
(error)="$any($event.target).src = defaultBackground" />
<nz-avatar
class="entity-avatar"
[ngStyle]="{ 'background-color': 'grey', 'vertical-align': 'middle' }"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,12 @@ export class BrowseSectionComponent implements OnInit, OnChanges {
throw new Error("Unexpected type in DashboardEntry.");
}
}

getCoverImage(entity: DashboardEntry): string {
if (entity.type === "dataset" && entity.coverImageUrl) {
const fullPath = `${entity.ownerEmail}/${entity.name}/${entity.coverImageUrl}`;
return `/api/dataset/file?path=${encodeURIComponent(fullPath)}`;
}
return this.defaultBackground;
}
}
1 change: 1 addition & 0 deletions sql/texera_ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ CREATE TABLE IF NOT EXISTS dataset
is_downloadable BOOLEAN NOT NULL DEFAULT TRUE,
description VARCHAR(512) NOT NULL,
creation_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
cover_image varchar(255),
FOREIGN KEY (owner_uid) REFERENCES "user"(uid) ON DELETE CASCADE
);

Expand Down