Skip to content
Merged
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 @@ -4,7 +4,7 @@
[control]="renameForm.controls['name']"
[label]="'files.dialogs.renameFile.newName'"
[placeholder]="'files.dialogs.renameFile.enterNewName'"
[maxLength]="nameLimit"
[maxLength]="nameMaxLength"
[minLength]="nameMinLength"
>
</osf-text-input>
Expand All @@ -14,7 +14,7 @@
type="button"
severity="secondary"
[label]="'common.buttons.cancel' | translate"
(click)="onCancel()"
(onClick)="onCancel()"
></p-button>
<p-button type="submit" [label]="'common.buttons.rename' | translate" [disabled]="renameForm.invalid"></p-button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';

import { TextInputComponent } from '@osf/shared/components';
import { InputLimits } from '@osf/shared/constants';
import { forbiddenFileNameCharacters, InputLimits } from '@osf/shared/constants';
import { CustomValidators } from '@osf/shared/helpers';

@Component({
Expand All @@ -20,13 +20,16 @@ export class RenameFileDialogComponent {
private readonly dialogRef = inject(DynamicDialogRef);
private readonly config = inject(DynamicDialogConfig);

readonly nameLimit = InputLimits.name.maxLength;
readonly nameMinLength = InputLimits.name.minLength;
readonly nameMaxLength = InputLimits.title.maxLength;
readonly nameMinLength = InputLimits.title.minLength;

readonly renameForm = new FormGroup({
name: new FormControl(this.config.data?.currentName ?? '', {
nonNullable: true,
validators: [CustomValidators.requiredTrimmed()],
validators: [
CustomValidators.requiredTrimmed(),
CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters),
],
}),
});

Expand Down
1 change: 1 addition & 0 deletions src/app/features/files/pages/files/files.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ export class FilesComponent {
finalize(() => {
this.updateFilesList();
this.fileIsUploading.set(false);
this.toastService.showSuccess('files.dialogs.createFolder.success');
})
)
.subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ <h3 class="mb-3">{{ 'project.metadata.license.dialog.chooseLicense.label' | tran
[isSubmitting]="isSubmitting()"
[showInternalButtons]="false"
[fullWidthSelect]="true"
[appendTo]="'body'"
(selectLicense)="onSelectLicense($event)"
(createLicense)="onCreateLicense($event)"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { CreateDeveloperApp, DeveloperAppsSelectors, UpdateDeveloperApp } from '
templateUrl: './developer-app-add-edit-form.component.html',
styleUrl: './developer-app-add-edit-form.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [DynamicDialogRef],
})
export class DeveloperAppAddEditFormComponent implements OnInit {
readonly isEditMode = input(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { FormBuilder } from '@angular/forms';

import { UpdateProfileSettingsUser, UserSelectors } from '@osf/core/store/user';
import { forbiddenFileNameCharacters } from '@osf/shared/constants';
import { CustomValidators } from '@osf/shared/helpers';
import { UserModel } from '@osf/shared/models';
import { CustomConfirmationService, LoaderService, ToastService } from '@osf/shared/services';
Expand Down Expand Up @@ -39,10 +40,25 @@ export class NameComponent {

readonly fb = inject(FormBuilder);
readonly form = this.fb.group<NameForm>({
fullName: this.fb.control('', { nonNullable: true, validators: [CustomValidators.requiredTrimmed()] }),
givenName: this.fb.control('', { nonNullable: true }),
middleNames: this.fb.control('', { nonNullable: true }),
familyName: this.fb.control('', { nonNullable: true }),
fullName: this.fb.control('', {
nonNullable: true,
validators: [
CustomValidators.requiredTrimmed(),
CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters),
],
}),
givenName: this.fb.control('', {
nonNullable: true,
validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters),
}),
middleNames: this.fb.control('', {
nonNullable: true,
validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters),
}),
familyName: this.fb.control('', {
nonNullable: true,
validators: CustomValidators.forbiddenCharactersValidator(forbiddenFileNameCharacters),
}),
suffix: this.fb.control('', { nonNullable: true }),
});

Expand Down
13 changes: 11 additions & 2 deletions src/app/shared/components/files-tree/files-tree.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,9 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {

deleteEntry(link: string): void {
this.actions().setFilesIsLoading?.(true);
this.actions().deleteEntry?.(this.resourceId(), link);
this.actions()
.deleteEntry?.(this.resourceId(), link)
.subscribe(() => this.toastService.showSuccess('files.dialogs.deleteFile.success'));
}

confirmRename(file: OsfFile): void {
Expand All @@ -338,7 +340,10 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
renameEntry(newName: string, file: OsfFile): void {
if (newName.trim() && file.links.upload) {
this.actions().setFilesIsLoading?.(true);
this.actions().renameEntry?.(this.resourceId(), file.links.upload, newName);

this.actions()
.renameEntry?.(this.resourceId(), file.links.upload, newName)
.subscribe(() => this.toastService.showSuccess('files.dialogs.renameFile.success'));
}
}

Expand Down Expand Up @@ -385,6 +390,10 @@ export class FilesTreeComponent implements OnDestroy, AfterViewInit {
this.resetPagination();
if (foldersStack) {
this.foldersStack = [...foldersStack];

if (action === 'copy') {
this.toastService.showSuccess('files.dialogs.copyFile.success');
}
}
});
});
Expand Down
5 changes: 3 additions & 2 deletions src/app/shared/components/license/license.component.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<p-select
class="mt-4 w-full"
data-test-select-license
[options]="licenses()"
[(ngModel)]="selectedLicense"
optionLabel="name"
[class.md:w-full]="fullWidthSelect()"
[class]="fullWidthSelect() ? 'md:w-full' : 'md:w-6'"
[placeholder]="'shared.license.selectLicense' | translate"
[appendTo]="appendTo()"
(onChange)="onSelectLicense($event.value)"
class="mt-4 w-full md:w-6"
/>
@if (selectedLicense()) {
<p-divider styleClass="mt-3 mb-3" />
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/components/license/license.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class LicenseComponent {
isSubmitting = input<boolean>(false);
showInternalButtons = input<boolean>(true);
fullWidthSelect = input<boolean>(false);
appendTo = input<string | null>(null);
selectedLicense = model<LicenseModel | null>(null);
createLicense = output<{ id: string; licenseOptions: LicenseOptions }>();
selectLicense = output<LicenseModel>();
Expand Down
22 changes: 13 additions & 9 deletions src/app/shared/components/wiki/wiki-list/wiki-list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@
} @else {
@if (expanded()) {
<p-panel showHeader="false">
<div class="flex align-items-center justify-content-between mt-3 mb-2">
<p-button
[label]="'project.wiki.addNewWiki' | translate"
icon="fas fa-plus"
severity="success"
outlined
[disabled]="!canEdit()"
(onClick)="openAddWikiDialog()"
/>
<div
class="flex align-items-center mt-3 mb-2"
[class]="canEdit() ? 'justify-content-between' : 'justify-content-end'"
>
@if (canEdit()) {
<p-button
[label]="'project.wiki.addNewWiki' | translate"
icon="fas fa-plus"
severity="success"
outlined
(onClick)="openAddWikiDialog()"
/>
}

<p-button
class="btn-icon-only"
Expand Down
3 changes: 3 additions & 0 deletions src/app/shared/constants/input-limits.const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export const InputLimits = {
maxLength: 6,
},
title: {
minLength: 2,
maxLength: 512,
},
};

export const forbiddenFileNameCharacters = /[()<>~!@$&*:;,"'\\|/?]/;
12 changes: 12 additions & 0 deletions src/app/shared/helpers/custom-form-validators.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,16 @@ export class CustomValidators {
return null;
};
}

static forbiddenCharactersValidator(pattern: RegExp): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value = control.value;
if (!value) {
return null;
}

const hasForbiddenCharacters = pattern.test(value);
return hasForbiddenCharacters ? { forbiddenCharacters: true } : null;
};
}
}
21 changes: 14 additions & 7 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
"donate": "Donate",
"profileSettings": "Profile Settings",
"accountSettings": "Account Settings",
"configureAddonAccounts": "Configure Addon Accounts",
"configureAddonAccounts": "Configure add-on & link service accounts",
"notifications": "Notifications",
"developerApps": "Developer Apps",
"personalAccessTokens": "Personal Access Tokens",
Expand Down Expand Up @@ -1082,32 +1082,39 @@
"createFolder": {
"title": "Create folder",
"folderName": "New folder name",
"folderNamePlaceholder": "Please enter a folder name"
"folderNamePlaceholder": "Please enter a folder name",
"success": "Folder successfully created."
},
"renameFile": {
"title": "Rename file",
"newName": "New name",
"enterNewName": "Enter new name",
"renameLabel": "Please rename the file"
"renameLabel": "Please rename the file",
"success": "File successfully renamed."
},
"moveFile": {
"cannotMove": "Cannot move to the same folder",
"title": "Move file",
"message": "Are you sure you want to move <span class=\"font-bold\">{{dragNodeName}}</span> to <span class=\"font-bold\">{{dropNodeName}}</span> ?",
"storage": "OSF Storage",
"pathError": "Path is not specified!"
"pathError": "Path is not specified!",
"success": "File successfully moved."
},
"copyFile": {
"title": "Select location to copy file to"
"title": "Select location to copy file to",
"success": "File successfully copied."
},
"deleteFile": {
"title": "Delete File",
"message": "Are you sure you want to delete <span class=\"font-bold\">{{name}}</span>?"
"message": "Are you sure you want to delete <span class=\"font-bold\">{{name}}</span>?",
"success": "File successfully deleted."
},
"replaceFile": {
"single": "Replace file",
"multiple": "Replace files",
"message": "Are you sure you want to replace <span class=\"font-bold\">{{name}}</span>?"
"message": "Are you sure you want to replace <span class=\"font-bold\">{{name}}</span>?",
"successOne": "File successfully replaced.",
"successMultiple": "File successfully replaced."
}
},
"filesBrowserDialog": {
Expand Down
Loading