diff --git a/app/adapters/workflow-config.ts b/app/adapters/workflow-config.ts
new file mode 100644
index 000000000..c8a013730
--- /dev/null
+++ b/app/adapters/workflow-config.ts
@@ -0,0 +1,33 @@
+import DS from 'ember-data';
+import config from 'ember-get-config';
+import OsfAdapter from './osf-adapter';
+
+const {
+ OSF: {
+ url: host,
+ webApiNamespace: namespace,
+ },
+} = config;
+
+export default class WorkFlowConfigAdapter extends OsfAdapter {
+ host = host.replace(/\/+$/, '');
+ namespace = namespace;
+
+ buildURL(
+ _: string | undefined,
+ id: string | null,
+ __: DS.Snapshot | null,
+ ___: string,
+ ____?: {},
+ ): string {
+ const nodeUrl = super.buildURL('node', null, null, 'findRecord', {});
+ const url = nodeUrl.replace(/\/nodes\/$/, '/project/');
+ return `${url}${id}/workflow/config`;
+ }
+}
+
+declare module 'ember-data/types/registries/adapter' {
+ export default interface AdapterRegistry {
+ 'workflow-config': WorkFlowConfigAdapter;
+ } // eslint-disable-line semi
+}
diff --git a/app/guid-node/styles.scss b/app/guid-node/styles.scss
new file mode 100644
index 000000000..177581dc4
--- /dev/null
+++ b/app/guid-node/styles.scss
@@ -0,0 +1,52 @@
+@media screen and (max-width: 768px) {
+ .WorkFlow {
+ margin: 0;
+ padding: 0;
+ }
+}
+
+h4 {
+ margin-top: 40px;
+}
+
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 9999;
+}
+
+.workflow-dialog {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ width: 500px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+ position: relative;
+}
+
+.tabs {
+ margin-top: 60px;
+}
+
+.tab-content {
+ height: 400px;
+ width: 100%;
+ overflow-y: auto;
+ overflow-x: auto;
+ white-space: nowrap;
+}
+
+.tab-pane {
+ display: none;
+}
+
+.tab-pane.active {
+ display: block;
+}
diff --git a/app/guid-node/workflow/controller.ts b/app/guid-node/workflow/controller.ts
new file mode 100644
index 000000000..9e1a399b4
--- /dev/null
+++ b/app/guid-node/workflow/controller.ts
@@ -0,0 +1,738 @@
+import Controller from '@ember/controller';
+import EmberError from '@ember/error';
+import { action, computed, set } from '@ember/object';
+import { reads } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+import { tracked } from '@glimmer/tracking';
+import DS from 'ember-data';
+import Intl from 'ember-intl/services/intl';
+import Node from 'ember-osf-web/models/node';
+import WorkFlowConfigModel from 'ember-osf-web/models/workflow-config';
+import Analytics from 'ember-osf-web/services/analytics';
+import StatusMessages from 'ember-osf-web/services/status-messages';
+import Toast from 'ember-toastr/services/toast';
+
+export default class GuidNodeWorkFlow extends Controller {
+ [x: string]: any;
+ @service toast!: Toast;
+ @service intl!: Intl;
+ @service statusMessages!: StatusMessages;
+ @service analytics!: Analytics;
+
+ @reads('model.taskInstance.value')
+ node?: Node;
+
+ isPageDirty = false;
+ configCache?: DS.PromiseObject;
+
+ @computed('config.isFulfilled')
+ get loading(): boolean {
+ return !this.config || !this.config.get('isFulfilled');
+ }
+
+ activeTab = '';
+ isStopProcess = false;
+ selectedWorkflow = {};
+ filePath = {};
+ isRegistering = false;
+ isEditing = false;
+ isProcessDialogVisible = false;
+ workflowEngines: string[] = [];
+ @tracked selectedWorkflowEngine: string | null = null;
+ @tracked workflowName = '';
+ @tracked workflowID: string | null = null;
+ @tracked creatorToken = 'none';
+ @tracked adminToken = 'none';
+ @tracked executorToken = 'none';
+ @tracked editingWorkflowId: string | null = null;
+
+ workflowsManage: Array<{ id: string; name: string; suspended: boolean }> = [];
+ workflowsProcess: Array<{
+ engine: string;
+ processDefinitionName: string;
+ id: string;
+ startUserId: string;
+ startTime: string;
+ endTime: string;
+ statusofprocessing: string;
+ ended: string;
+ }> = [];
+
+ workflowsTask: Array<{
+ id: string;
+ processInstanceId: string;
+ name: string;
+ startTime: string;
+ endTime: string;
+ assignee: string;
+ }> = [];
+
+ workflowsManageInfo: Array<{
+ engine: string;
+ name: string;
+ id: string;
+ creatorToken: string;
+ adminToken: string;
+ executorToken: string;
+ processId: string;
+ filePath: string;
+ }> = [];
+
+ workflowsProcessInfo: Array<{
+ processDefinitionName: string;
+ id: string;
+ startUserId: string;
+ startTime: string;
+ endTime: string;
+ }> = [];
+
+ taskDetails: Array<{
+ id: string;
+ name: string;
+ startTime: string;
+ endTime: string;
+ }> = [];
+
+ constructor(...args: any[]) {
+ super(...args);
+ this.executeWorkflow();
+ }
+
+ async executeWorkflow() {
+ await this.loadWorkflowConfig();
+ await this.loadDataManage();
+ await this.loadDataProcess();
+ await this.loadDataTask();
+ await this.loadDataManageInfo();
+ await this.loadDataProcessInfo();
+ }
+
+ async loadWorkflowConfig() {
+ const currentUrl = window.location.href;
+ const parts = currentUrl.split('/');
+ const pid = parts[3];
+ const response = await fetch(`/api/v1/project/${pid}/workflow/workflow_connection`);
+ const data = await response.json();
+ const engineNames: string[] = data.data.map((engine: { name: string }) => engine.name);
+ const engineUrls: string[] = data.data.map((engine: { url: string }) => engine.url);
+ const engineAccounts: string[] = data.data.map((engine: { account: string }) => engine.account);
+ const enginePasswords: string[] = data.data.map((engine: { password: string }) => engine.password);
+
+ this.set('workflowEngines', engineNames);
+ this.set('workflowUrls', engineUrls);
+ this.set('workflowAccounts', engineAccounts);
+ this.set('workflowPasswords', enginePasswords);
+ }
+
+ async fetchWorkflowData(
+ url: string,
+ method: string = 'GET',
+ body: any = null,
+ ) {
+ try {
+ const username = this.get('workflowAccounts')[0];
+ const password = this.get('workflowPasswords')[0];
+ const encodedCredentials = btoa(`${username}:${password}`);
+
+ const headers: HeadersInit = {
+ Authorization: `Basic ${encodedCredentials}`,
+ };
+
+ if (method === 'POST' && body instanceof FormData) {
+ delete headers['Content-Type'];
+ } else if (body) {
+ headers['Content-Type'] = 'application/json';
+ }
+
+ let requestBody = null;
+ if (body) {
+ if (body instanceof FormData) {
+ requestBody = body;
+ } else {
+ requestBody = JSON.stringify(body);
+ }
+ }
+
+ const response = await fetch(url, {
+ method,
+ headers,
+ body: requestBody,
+ });
+
+ if (!response.ok) {
+ if (response.status === 404) {
+ return null;
+ }
+ throw new Error(`Failed to ${method} request`);
+ }
+
+ if (response.status === 204) {
+ return null;
+ }
+
+ return await response.json();
+ } catch (error) {
+ // Error during request
+ this.toast.error('Failed to load workflow data');
+ throw error;
+ }
+ }
+
+ async loadDataManage() {
+ const workflowUrls = this.get('workflowUrls');
+ const pathToAppend = '/process-api/repository/process-definitions';
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ const fullUrl = `${workflowUrls[0]}${pathToAppend}`;
+ const workflowData = await this.fetchWorkflowData(fullUrl);
+
+ const processedData = workflowData.data.map((item: any) => ({
+ id: item.id,
+ name: item.name,
+ suspended: item.suspended,
+ }));
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ this.set('workflowsManage', processedData);
+ }
+ }
+ }
+
+ async loadDataProcess() {
+ const workflowUrls = this.get('workflowUrls');
+ const pathToAppend = '/process-api/history/historic-process-instances';
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ const fullUrl = `${workflowUrls[0]}${pathToAppend}`;
+ const workflowData2 = await this.fetchWorkflowData(fullUrl);
+
+ const processedData2 = workflowData2.data.map((item: any) => ({
+ id: item.id,
+ processDefinitionName: item.processDefinitionName,
+ startUserId: item.startUserId || '',
+ startTime: item.startTime || '',
+ endTime: item.endTime || '',
+ statusofprocessing: item.status || '',
+ ended: item.ended || '',
+ }));
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ this.set('workflowsProcess', processedData2);
+ }
+ }
+ }
+
+ async loadDataTask() {
+ const workflowUrls = this.get('workflowUrls');
+ const runtimePath = '/process-api/runtime/process-instances';
+ const historicPath = '/process-api/history/historic-task-instances';
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ try {
+ const [runtimeData, historicData] = await Promise.all([
+ this.fetchWorkflowData(`${workflowUrls[0]}${runtimePath}`),
+ this.fetchWorkflowData(`${workflowUrls[0]}${historicPath}`),
+ ]);
+
+ const validIds = new Set(runtimeData.data.map((item: any) => item.id));
+
+ const processedData = historicData.data
+ .filter((item: any) => validIds.has(item.processInstanceId))
+ .map((item: any) => ({
+ id: item.id,
+ processInstanceId: item.processInstanceId,
+ name: item.name,
+ startTime: item.startTime || '',
+ endTime: item.endTime || '',
+ assignee: item.assignee || '',
+ }));
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ this.set('workflowsTask', processedData);
+ }
+
+ if (processedData.length > 0) {
+ this.set('activeTab', 'tab1');
+ }
+ } catch (error) {
+ // Data acquisition error
+ }
+ }
+ }
+
+ async loadDataManageInfo() {
+ const response = await fetch('/api/v1/addons/workflow/registered_workflows/');
+ const data = await response.json();
+ this.set('workflowsManageInfo', data.data);
+ }
+
+ async loadDataProcessInfo() {
+ const workflowUrls = this.get('workflowUrls');
+ const pathToAppend = '/process-api/history/historic-process-instances';
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ try {
+ const workflowData5 = await this.fetchWorkflowData(`${workflowUrls[0]}${pathToAppend}`);
+ const workflowsProcess = this.get('workflowsProcess') || [];
+ const validIds = new Set(workflowsProcess.map((item: any) => item.id));
+
+ const processedData5 = workflowData5.data
+ .filter((item: any) => validIds.has(item.id))
+ .map((item: any) => ({
+ processDefinitionName: item.processDefinitionName,
+ id: item.id,
+ startUserId: item.startUserId || '',
+ startTime: item.startTime || '',
+ endTime: item.endTime || '',
+ }));
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ this.set('workflowsProcessInfo', processedData5);
+ }
+ } catch (error) {
+ // Data acquisition error
+ }
+ }
+ }
+
+ @action
+ async toggleActivate(workflowId: string) {
+ const workflowUrls = this.get('workflowUrls');
+ const pathToAppend = `/process-api/repository/process-definitions/${workflowId}`;
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ try {
+ const url = `${workflowUrls[0]}${pathToAppend}`;
+ const body = { action: 'activate' };
+
+ await this.fetchWorkflowData(url, 'PUT', body);
+
+ const workflow = this.workflowsManage.find(w => w.id === workflowId);
+ if (workflow) {
+ if (!this.isDestroyed && !this.isDestroying) {
+ set(workflow, 'suspended', false);
+ }
+ }
+ } catch (error) {
+ // Error activating workflow
+ }
+ }
+ }
+
+ @action
+ async toggleDeactivate(workflowId: string) {
+ const workflowUrls = this.get('workflowUrls');
+ const pathToAppend = `/process-api/repository/process-definitions/${workflowId}`;
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ try {
+ const url = `${workflowUrls[0]}${pathToAppend}`;
+ const body = { action: 'suspend' };
+
+ await this.fetchWorkflowData(url, 'PUT', body);
+
+ const workflow = this.workflowsManage.find(w => w.id === workflowId);
+ if (workflow) {
+ if (!this.isDestroyed && !this.isDestroying) {
+ set(workflow, 'suspended', true);
+ }
+ }
+ } catch (error) {
+ // Error suspending workflow
+ }
+ }
+ }
+
+ @action
+ async stopProcess(workflowId: string) {
+ this.set('isStopProcess', true);
+ const workflowUrls = this.get('workflowUrls');
+ const runtimePath = `/process-api/runtime/process-instances/${workflowId}`;
+ const historyPath = `/process-api/history/historic-process-instances/${workflowId}`;
+
+ if (workflowUrls && workflowUrls.length > 0) {
+ try {
+ let processExists = true;
+
+ try {
+ await this.fetchWorkflowData(`${workflowUrls[0]}${runtimePath}`, 'GET');
+ } catch (error) {
+ if (error.status === 404) {
+ processExists = false;
+ } else {
+ throw error;
+ }
+ }
+
+ if (processExists) {
+ await this.fetchWorkflowData(`${workflowUrls[0]}${runtimePath}`, 'DELETE');
+ }
+
+ await this.fetchWorkflowData(`${workflowUrls[0]}${historyPath}`, 'DELETE');
+
+ await this.loadDataProcess();
+ await this.loadDataProcessInfo();
+ } catch (error) {
+ // Error deleting process
+ }
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ this.set('isStopProcess', false);
+ }
+ }
+ }
+
+ @action
+ async registerWorkflow2() {
+ const currentUrl = window.location.href;
+ const parts = currentUrl.split('/');
+ const pid = parts[3];
+
+ const data = {
+ workflowEngine: this.selectedWorkflowEngine,
+ workflowName: this.workflowName,
+ workflowID: this.workflowID,
+ creatorToken: this.creatorToken,
+ adminToken: this.adminToken,
+ executorToken: this.executorToken,
+ };
+
+ const missingFields = Object.keys(data).filter(key => {
+ const value = data[key as keyof typeof data];
+ return !value;
+ });
+
+ if (missingFields.length > 0) {
+ this.toast.error(`Missing required fields: ${missingFields.join(', ')}`);
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/v1/project/${pid}/workflow/register_workflow`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(`Failed to register workflow: ${JSON.stringify(errorData)}`);
+ }
+
+ this.toast.success('Workflow registered successfully.');
+ this.set('isRegistering', false);
+ await this.loadDataManageInfo();
+ } catch (error) {
+ // Error registering workflow
+ this.toast.error(`Failed to register workflow: ${error.message}`);
+ this.set('isRegistering', false);
+ }
+ }
+
+ @action
+ async processRegisterWorkflow() {
+ const currentUrl = window.location.href;
+ const parts = currentUrl.split('/');
+ const pid = parts[3];
+
+ const data = {
+ workflowEngine: this.selectedWorkflowEngine,
+ workflowName: this.workflowName,
+ workflowID: this.workflowID,
+ creatorToken: this.creatorToken,
+ adminToken: this.adminToken,
+ executorToken: this.executorToken,
+ };
+
+ const missingFields = Object.keys(data).filter(key => {
+ const value = data[key as keyof typeof data];
+ return !value;
+ });
+
+ if (missingFields.length > 0) {
+ this.toast.error(`Missing required fields: ${missingFields.join(', ')}`);
+ return;
+ }
+
+ try {
+ const response = await fetch(`/api/v1/project/${pid}/workflow/register_workflow`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(`Failed to register workflow: ${JSON.stringify(errorData)}`);
+ }
+
+ this.toast.success('Workflow registered successfully.');
+ this.set('isRegistering', false);
+ await this.loadDataManageInfo();
+ } catch (error) {
+ // Error registering workflow
+ this.toast.error(`Failed to register workflow: ${error.message}`);
+ this.set('isRegistering', false);
+ }
+ }
+
+ @action
+ async removeworkflow(workflowId: string) {
+ const currentUrl = window.location.href;
+ const parts = currentUrl.split('/');
+ const pid = parts[3];
+ try {
+ const response = await fetch(`/api/v1/project/${pid}/workflow/remove_workflow/${workflowId}`, {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(`Failed to remove workflow: ${JSON.stringify(errorData)}`);
+ }
+
+ this.toast.success('Workflow removed successfully.');
+ await this.loadDataManageInfo();
+ } catch (error) {
+ // Error removing workflow
+ this.toast.error(`Failed to remove workflow: ${error.message}`);
+ }
+ }
+
+ @action
+ async updateWorkflow() {
+ const currentUrl = window.location.href;
+ const parts = currentUrl.split('/');
+ const pid = parts[3];
+ const data = {
+ workflowName: this.workflowName,
+ creatorToken: this.creatorToken,
+ adminToken: this.adminToken,
+ executorToken: this.executorToken,
+ };
+
+ const missingFields = Object.keys(data).filter(key => {
+ const value = data[key as keyof typeof data];
+ return !value;
+ });
+
+ if (missingFields.length > 0) {
+ this.toast.error(`Missing required fields: ${missingFields.join(', ')}`);
+ return;
+ }
+
+ try {
+ const response = await fetch(
+ `/api/v1/project/${pid}/workflow/register_workflow/${this.editingWorkflowId}`,
+ {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ },
+ );
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(`Failed to update workflow: ${JSON.stringify(errorData)}`);
+ }
+
+ this.toast.success('Workflow updated successfully.');
+ this.set('isRegistering', false);
+ this.set('isEditing', false);
+ await this.loadDataManageInfo();
+ } catch (error) {
+ // Error updating workflow
+ this.toast.error(`Failed to update workflow: ${error.message}`);
+ this.set('isRegistering', false);
+ this.set('isEditing', false);
+ }
+ }
+
+ @action
+ editWorkflow(workflowId: string) {
+ const workflow = this.workflowsManageInfo.find((w: any) => w.id === workflowId);
+ if (workflow) {
+ this.set('isRegistering', true);
+ this.set('isEditing', true);
+ this.editingWorkflowId = workflowId;
+ this.selectedWorkflowEngine = workflow.engine;
+ this.workflowName = workflow.name;
+ this.workflowID = workflow.id;
+ this.creatorToken = workflow.creatorToken;
+ this.adminToken = workflow.adminToken;
+ this.executorToken = workflow.executorToken;
+ }
+ }
+
+ @action
+ setActiveTab(tab: string) {
+ this.set('activeTab', tab);
+ }
+
+ @action
+ registerWorkflow() {
+ this.set('isRegistering', true);
+ this.set('isEditing', false);
+ this.selectedWorkflowEngine = null;
+ this.workflowName = '';
+ this.workflowID = null;
+ this.creatorToken = 'none';
+ this.adminToken = 'none';
+ this.executorToken = 'none';
+ }
+
+ @action
+ closeRegisterDialog() {
+ this.set('isRegistering', false);
+ this.set('isEditing', false);
+ }
+
+ @action
+ async openProcessDialog(workflowId: string) {
+ if (this.isStopProcess) {
+ return;
+ }
+
+ const workflowUrls = this.get('workflowUrls');
+ const pathToProcess = `/process-api/history/historic-task-instances?processInstanceId=${workflowId}`;
+ const pathToTaskDetail = '/process-api/history/historic-task-instances/';
+
+ const selectedWorkflow = this.get('workflowsProcessInfo').find(workflow => workflow.id === workflowId);
+ this.set('selectedWorkflow', selectedWorkflow);
+ this.set('isProcessDialogVisible', true);
+
+ try {
+ const response = await fetch('/api/v1/addons/workflow/registered_workflows/');
+ const data = await response.json();
+ this.set('workflowsManageInfo', data.data);
+
+ const tasks = await this.fetchWorkflowData(`${workflowUrls[0]}${pathToProcess}`);
+
+ if (!tasks || !tasks.data || tasks.data.length === 0) {
+ this.set('taskDetails', []);
+ return;
+ }
+
+ const taskDetails = await Promise.all(
+ tasks.data.map(async (task: any) => {
+ const detail = await this.fetchWorkflowData(
+ `${workflowUrls[0]}${pathToTaskDetail}${task.id}`, 'GET', null,
+ );
+ return {
+ id: detail.id,
+ name: detail.name || '',
+ startTime: detail.startTime || '',
+ endTime: detail.endTime || '',
+ };
+ }),
+ );
+
+ this.set('taskDetails', taskDetails);
+
+ const matchingWorkflow = this.get('workflowsManageInfo').find(
+ workflow => workflow.processId === workflowId,
+ );
+ if (matchingWorkflow) {
+ this.set('filePath', matchingWorkflow);
+ } else {
+ this.set('filePath', '');
+ }
+ } catch (error) {
+ // Failed to load task details
+ this.set('taskDetails', []);
+ }
+ }
+
+ @action
+ closeProcessDialog() {
+ this.set('isProcessDialogVisible', false);
+ }
+
+ @action
+ async updatebutton() {
+ await this.loadDataProcess();
+ await this.loadDataProcessInfo();
+ }
+
+ @action
+ async updatebuttonTask() {
+ await this.loadDataProcess();
+ await this.loadDataTask();
+ }
+
+ @action
+ navigateToTasks(taskId: string): void {
+ const workflowUrls = this.get('workflowUrls');
+ const baseUrl = workflowUrls && workflowUrls.length > 0 ? workflowUrls[0] : '';
+
+ if (!this.isDestroyed && !this.isDestroying && baseUrl) {
+ const taskUrl = `/workflow/#/apps/Workflow_Task_App/tasks/${taskId}`;
+ window.open(`${baseUrl}${taskUrl}`, '_blank');
+ }
+ }
+
+ @computed('config.param1')
+ get param1() {
+ if (!this.config || !this.config.get('isFulfilled')) {
+ return '';
+ }
+ const config = this.config.content as WorkFlowConfigModel;
+ return config.param1;
+ }
+
+ set param1(v: string) {
+ if (!this.config) {
+ throw new EmberError('Illegal config');
+ }
+ const config = this.config.content as WorkFlowConfigModel;
+ config.set('param1', v);
+ this.set('isPageDirty', true);
+ }
+
+ @action
+ updateWorkflowEngine(event: Event) {
+ this.selectedWorkflowEngine = (event.target as HTMLSelectElement).value;
+ }
+
+ @action
+ updateToken(tokenType: string, event: Event) {
+ (this as any)[tokenType] = (event.target as HTMLInputElement).value;
+ }
+
+ @action
+ updateWorkflowName(event: Event) {
+ this.workflowName = (event.target as HTMLInputElement).value;
+ }
+
+ @action
+ updateWorkflowID(event: Event) {
+ this.workflowID = (event.target as HTMLInputElement).value;
+ }
+
+ @computed('node')
+ get config(): DS.PromiseObject | undefined {
+ if (this.configCache) {
+ return this.configCache;
+ }
+ if (!this.node) {
+ return undefined;
+ }
+ this.configCache = this.store.findRecord('workflow-config', this.node.id);
+ return this.configCache!;
+ }
+}
+
+declare module '@ember/controller' {
+ interface Registry {
+ 'guid-node/workflow': GuidNodeWorkFlow;
+ }
+}
diff --git a/app/guid-node/workflow/route.ts b/app/guid-node/workflow/route.ts
new file mode 100644
index 000000000..1acc8ac67
--- /dev/null
+++ b/app/guid-node/workflow/route.ts
@@ -0,0 +1,33 @@
+import { action, computed } from '@ember/object';
+import Route from '@ember/routing/route';
+import { inject as service } from '@ember/service';
+import ConfirmationMixin from 'ember-onbeforeunload/mixins/confirmation';
+
+import GuidNodeWorkFlow from 'ember-osf-web/guid-node/workflow/controller';
+import Node from 'ember-osf-web/models/node';
+import { GuidRouteModel } from 'ember-osf-web/resolve-guid/guid-route';
+import Analytics from 'ember-osf-web/services/analytics';
+
+export default class GuidNodeWorkFlowRoute extends Route.extend(ConfirmationMixin, {}) {
+ @service analytics!: Analytics;
+
+ model(this: GuidNodeWorkFlowRoute) {
+ return this.modelFor('guid-node');
+ }
+
+ @action
+ async didTransition() {
+ const { taskInstance } = this.controller.model as GuidRouteModel;
+ await taskInstance;
+ const node = taskInstance.value;
+
+ this.analytics.trackPage(node ? node.public : undefined, 'nodes');
+ }
+
+ // This tells ember-onbeforeunload's ConfirmationMixin whether or not to stop transitions
+ @computed('controller.isPageDirty')
+ get isPageDirty() {
+ const controller = this.controller as GuidNodeWorkFlow;
+ return () => controller.isPageDirty;
+ }
+}
diff --git a/app/guid-node/workflow/styles.scss b/app/guid-node/workflow/styles.scss
new file mode 100644
index 000000000..177581dc4
--- /dev/null
+++ b/app/guid-node/workflow/styles.scss
@@ -0,0 +1,52 @@
+@media screen and (max-width: 768px) {
+ .WorkFlow {
+ margin: 0;
+ padding: 0;
+ }
+}
+
+h4 {
+ margin-top: 40px;
+}
+
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 9999;
+}
+
+.workflow-dialog {
+ background-color: #fff;
+ padding: 20px;
+ border-radius: 8px;
+ width: 500px;
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+ position: relative;
+}
+
+.tabs {
+ margin-top: 60px;
+}
+
+.tab-content {
+ height: 400px;
+ width: 100%;
+ overflow-y: auto;
+ overflow-x: auto;
+ white-space: nowrap;
+}
+
+.tab-pane {
+ display: none;
+}
+
+.tab-pane.active {
+ display: block;
+}
diff --git a/app/guid-node/workflow/template.hbs b/app/guid-node/workflow/template.hbs
new file mode 100644
index 000000000..2cda01873
--- /dev/null
+++ b/app/guid-node/workflow/template.hbs
@@ -0,0 +1,319 @@
+{{title (t 'workflow.page_title' nodeTitle=this.model.taskInstance.value.unsafeTitle)}}
+
+
+ {{#if this.loading}}
+ {{t 'workflow.loading'}}
+ {{else}}
+
+
+
+
+
+
{{t 'workflow.tab1_content'}}
+
+ {{t 'workflow.update_button2'}}
+
+
+
{{t 'workflow.workflow_task_list'}}
+
+
+
+ | {{t 'workflow.processid'}} |
+ {{t 'workflow.workflow2_name'}} |
+ {{t 'workflow.status'}} |
+ {{t 'workflow.starting time'}} |
+ {{t 'workflow.completion time'}} |
+ {{t 'workflow.request_targer'}} |
+
+
+
+ {{#each (sortAndFilterData this.workflowsTask) as |workflowsTask|}}
+
+ | {{workflowsTask.processInstanceId}} |
+ {{workflowsTask.name}} |
+
+ {{#if workflowsTask.endTime}}
+ 完了
+ {{else}}
+ 実行中
+ {{/if}}
+ |
+ {{formatDateToJST workflowsTask.startTime}} |
+ {{formatDateToJST workflowsTask.endTime}} |
+ {{workflowsTask.assignee}} |
+
+ {{/each}}
+
+
+
+
+
+
+
{{t 'workflow.tab2_content'}}
+
+
+ {{t 'workflow.update_button'}}
+
+
+
{{t 'workflow.available workflow processes'}}
+
+
+
+ | {{t 'workflow.workflow2_name'}} |
+ {{t 'workflow.workflow_processid'}} |
+ {{t 'workflow.initiator'}} |
+ {{t 'workflow.starting time'}} |
+ {{t 'workflow.completion time'}} |
+ {{t 'workflow.status of processing'}} |
+ {{t 'workflow.execution state'}} |
+
+
+
+ {{#each this.workflowsProcessInfo as |workflowsProcessInfo|}}
+
+ | {{workflowsProcessInfo.processDefinitionName}} |
+ {{workflowsProcessInfo.id}} |
+ {{workflowsProcessInfo.startUserId}} |
+ {{formatDateToJST workflowsProcessInfo.startTime}} |
+ {{formatDateToJST workflowsProcessInfo.endTime}} |
+
+ {{#if workflowsProcessInfo.endTime}}
+ 終了
+ {{else}}
+ 開始
+ {{/if}}
+ |
+
+ {{#if workflowsProcessInfo.endTime}}
+ 完了
+ {{else}}
+ 実行中
+ {{/if}}
+ |
+
+ {{#if workflowsProcessInfo}}
+
+ {{t 'workflow.stop_button'}}
+
+ {{/if}}
+ |
+
+ {{/each}}
+
+
+
+ {{#if this.isProcessDialogVisible}}
+
+
+
{{t 'workflow.workflowprocessinformation'}}
+
+ {{this.selectedWorkflow.processDefinitionName}}
+
+ {{this.selectedWorkflow.id}}
+
+ {{this.filePath.filePath}}
+
+
+ {{#each this.taskDetails as |task|}}
+ {{#if task.endTime}}
+
{{task.name}}
+ {{/if}}
+ {{/each}}
+
+
+ {{#each this.taskDetails as |task|}}
+ {{#unless task.endTime}}
+
{{task.name}}
+ {{/unless}}
+ {{/each}}
+
+
+
+ {{t 'workflow.cancel_button'}}
+
+
+
+
+ {{/if}}
+
+
+
+
+
{{t 'workflow.tab3_content'}}
+
+ {{t 'workflow.register_button'}}
+
+
+
{{t 'workflow.Available_Workflows'}}
+
+ {{#each this.workflowsManage as |workflowsManage|}}
+ {{#each this.workflowsManageInfo as |match|}}
+ {{#if (eq workflowsManage.id match.id)}}
+ -
+ {{match.name}}
+ {{#if workflowsManage.suspended}}
+
+ {{t 'workflow.activate'}}
+
+ {{else}}
+
+ {{t 'workflow.deactivate'}}
+
+ {{/if}}
+
+ {{t 'workflow.edit'}}
+
+
+ {{t 'workflow.remove'}}
+
+
+ {{break}}
+ {{/if}}
+ {{/each}}
+ {{/each}}
+
+
+ {{#if this.isRegistering}}
+
+
+
{{if this.isEditing (t 'workflow.workflow_edit') (t 'workflow.workflow_registration')}}
+
+
+
+ {{/if}}
+
+
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/app/helpers/extractOuterParentheses.js b/app/helpers/extractOuterParentheses.js
new file mode 100644
index 000000000..edd1d3fe5
--- /dev/null
+++ b/app/helpers/extractOuterParentheses.js
@@ -0,0 +1,25 @@
+import { helper } from '@ember/component/helper';
+
+export function extractOuterParentheses([name]) {
+ if (typeof name !== 'string') return '';
+ let start = -1;
+ let depth = 0;
+ let outerContent = '';
+ for (let i = 0; i < name.length; i++) {
+ const char = name[i];
+ if (char === '' || char === '(') {
+ if (depth === 0) {
+ start = i + 1;
+ }
+ depth++;
+ } else if (char === '' || char === ')') {
+ depth--;
+ if (depth === 0 && start !== -1) {
+ outerContent = name.slice(start, i);
+ break;
+ }
+ }
+ }
+ return outerContent;
+}
+export default helper(extractOuterParentheses);
diff --git a/app/helpers/formatDateToJST.js b/app/helpers/formatDateToJST.js
new file mode 100644
index 000000000..114caa299
--- /dev/null
+++ b/app/helpers/formatDateToJST.js
@@ -0,0 +1,17 @@
+import { helper } from '@ember/component/helper';
+
+export function formatDateToJST([date]) {
+ if (!date) {
+ return '';
+ }
+ const d = new Date(date);
+ const year = d.getFullYear();
+ const month = String(d.getMonth() + 1).padStart(2, '0');
+ const day = String(d.getDate()).padStart(2, '0');
+ const hours = String(d.getHours()).padStart(2, '0');
+ const minutes = String(d.getMinutes()).padStart(2, '0');
+
+ return `${year}/${month}/${day} ${hours}:${minutes}`;
+}
+
+export default helper(formatDateToJST);
diff --git a/app/helpers/sortAndFilterData.js b/app/helpers/sortAndFilterData.js
new file mode 100644
index 000000000..09d2637bc
--- /dev/null
+++ b/app/helpers/sortAndFilterData.js
@@ -0,0 +1,20 @@
+import { helper } from '@ember/component/helper';
+
+export function sortAndFilterData([data = []]) {
+ if (!Array.isArray(data)) {
+ return [];
+ }
+
+ const sortedData = [...data].sort((a, b) => {
+ const timeA = a.startTime ? new Date(a.startTime).getTime() : 0;
+ const timeB = b.startTime ? new Date(b.startTime).getTime() : 0;
+ return timeA - timeB;
+ });
+
+ const inProgressData = sortedData.filter(item => !item.endTime);
+ const completedData = sortedData.filter(item => item.endTime);
+
+ return [...inProgressData, ...completedData];
+}
+
+export default helper(sortAndFilterData);
diff --git a/app/models/registration-schema.ts b/app/models/registration-schema.ts
index 25194f299..c264e3c09 100644
--- a/app/models/registration-schema.ts
+++ b/app/models/registration-schema.ts
@@ -31,6 +31,7 @@ export interface Page {
questions: Question[];
type?: 'object';
description?: string;
+ clipboardCopyPaste: boolean;
}
export interface Schema {
diff --git a/app/models/schema-block.ts b/app/models/schema-block.ts
index ede4f06f5..e9213e04a 100644
--- a/app/models/schema-block.ts
+++ b/app/models/schema-block.ts
@@ -22,9 +22,15 @@ export default class SchemaBlockModel extends OsfModel implements SchemaBlock {
@attr('number') index?: number;
@attr('string') pattern?: string;
@attr('boolean') spaceNormalization?: boolean;
- @attr('boolean') autoDate?: boolean;
- @attr('boolean') autoTitle?: boolean;
- @attr('boolean') hideProjectmetadata?: boolean;
+ @attr('string') retrievalTitle?: string;
+ @attr('string') retrievalDate?: string;
+ @attr('boolean') concealmentPageNavigator?: boolean;
+ @attr('string') requiredAllCheck?: string;
+ @attr('boolean') multiLanguage?: boolean;
+ @attr('string') retrievalVersion?: string;
+ @attr('boolean') readonly?: boolean;
+ @attr('boolean') sentence?: boolean;
+ @attr('string') rowAdditionCaption?: string;
@belongsTo('registration-schema', { inverse: 'schemaBlocks', async: false })
schema?: RegistrationSchemaModel;
diff --git a/app/models/workflow-config.ts b/app/models/workflow-config.ts
new file mode 100644
index 000000000..4f1471861
--- /dev/null
+++ b/app/models/workflow-config.ts
@@ -0,0 +1,14 @@
+import DS from 'ember-data';
+import OsfModel from './osf-model';
+
+const { attr } = DS;
+
+export default class WorkFlowConfigModel extends OsfModel {
+ @attr('string') param1!: string;
+}
+
+declare module 'ember-data/types/registries/model' {
+ export default interface ModelRegistry {
+ 'workflow-config': WorkFlowConfigModel;
+ } // eslint-disable-line semi
+}
diff --git a/app/packages/registration-schema/get-pages.ts b/app/packages/registration-schema/get-pages.ts
index c458942be..de58f2762 100644
--- a/app/packages/registration-schema/get-pages.ts
+++ b/app/packages/registration-schema/get-pages.ts
@@ -3,15 +3,16 @@ import { SchemaBlock } from 'ember-osf-web/packages/registration-schema';
export function getPages(blocks: SchemaBlock[]) {
const pageArray = blocks.reduce(
(pages, block) => {
+ // instantiate first page if the schema doesn't start with a page-heading
if (pages.length === 0 && block.blockType !== 'page-heading'
- && (block.hideProjectmetadata === true || block.hideProjectmetadata === undefined)) {
+ && (block.concealmentPageNavigator === true || block.concealmentPageNavigator === undefined)) {
const blankPage: SchemaBlock[] = [];
pages.push(blankPage);
}
- const lastPage: SchemaBlock[] = pages.slice(-1)[0] || [];
+ const lastPage: SchemaBlock[] = pages.slice(-1)[0];
if (block.blockType === 'page-heading'
- && (block.hideProjectmetadata === false || block.hideProjectmetadata === undefined)) {
+ && (block.concealmentPageNavigator === false || block.concealmentPageNavigator === undefined)) {
pages.push([block]);
} else {
lastPage.push(block);
diff --git a/app/packages/registration-schema/get-schema-block-group.ts b/app/packages/registration-schema/get-schema-block-group.ts
index d459c262f..207275d62 100644
--- a/app/packages/registration-schema/get-schema-block-group.ts
+++ b/app/packages/registration-schema/get-schema-block-group.ts
@@ -43,6 +43,7 @@ export function getSchemaBlockGroups(blocks: SchemaBlock[] | undefined) {
case 'jgn-program-name-ja-input':
case 'jgn-program-name-en-input':
case 'e-rad-award-funder-input':
+ case 'single-select-pulldown-input':
case 'pulldown-input':
case 'e-rad-award-number-input':
case 'e-rad-award-title-ja-input':
@@ -53,6 +54,7 @@ export function getSchemaBlockGroups(blocks: SchemaBlock[] | undefined) {
case 'e-rad-researcher-name-en-input':
case 'e-rad-bunnya-input':
case 'file-metadata-input':
+ case 'ad-metadata-input':
case 'date-input':
case 'section-heading':
case 'subsection-heading':
diff --git a/app/packages/registration-schema/index.ts b/app/packages/registration-schema/index.ts
index b09e7121f..d8f93c645 100644
--- a/app/packages/registration-schema/index.ts
+++ b/app/packages/registration-schema/index.ts
@@ -2,7 +2,12 @@ export { getPages } from './get-pages';
export { getSchemaBlockGroups } from './get-schema-block-group';
export { SchemaBlock, SchemaBlockType } from './schema-block';
export { SchemaBlockGroup } from './schema-block-group';
-export { buildValidation, buildMetadataValidations, setupEventForSyncValidation } from './validations';
+export {
+ buildValidation,
+ buildMetadataValidations,
+ setupEventForSyncValidation,
+ setupEventForSyncValidation2,
+} from './validations';
export {
FileReference,
RegistrationResponse,
diff --git a/app/packages/registration-schema/page-manager.ts b/app/packages/registration-schema/page-manager.ts
index b801a28ce..a7a099372 100644
--- a/app/packages/registration-schema/page-manager.ts
+++ b/app/packages/registration-schema/page-manager.ts
@@ -10,6 +10,7 @@ import {
SchemaBlock,
SchemaBlockGroup,
setupEventForSyncValidation,
+ setupEventForSyncValidation2,
} from 'ember-osf-web/packages/registration-schema';
import { RegistrationResponse } from 'ember-osf-web/packages/registration-schema/registration-response';
@@ -17,14 +18,14 @@ export class PageManager {
changeset?: ChangesetDef;
schemaBlockGroups?: SchemaBlockGroup[];
pageHeadingText?: string;
- hideProjectmetadata?: boolean;
+ concealmentPageNavigator?: boolean;
isVisited?: boolean;
constructor(pageSchemaBlocks: SchemaBlock[], registrationResponses: RegistrationResponse, node?: NodeModel) {
this.schemaBlockGroups = getSchemaBlockGroups(pageSchemaBlocks);
if (this.schemaBlockGroups) {
this.pageHeadingText = this.schemaBlockGroups[0].labelBlock!.displayText!;
- this.hideProjectmetadata = this.schemaBlockGroups[0].labelBlock!.hideProjectmetadata!;
+ this.concealmentPageNavigator = this.schemaBlockGroups[0].labelBlock!.concealmentPageNavigator!;
this.isVisited = this.schemaBlockGroups.some(
({ registrationResponseKey: key }) => Boolean(key && (key in registrationResponses)),
@@ -36,8 +37,11 @@ export class PageManager {
lookupValidator(validations),
validations,
) as ChangesetDef;
+
setupEventForSyncValidation(this.changeset, this.schemaBlockGroups);
+ setupEventForSyncValidation2(this.changeset, this.schemaBlockGroups);
+
if (this.isVisited) {
this.changeset.validate();
}
diff --git a/app/packages/registration-schema/schema-block.ts b/app/packages/registration-schema/schema-block.ts
index b99203c94..3c7aa46e9 100644
--- a/app/packages/registration-schema/schema-block.ts
+++ b/app/packages/registration-schema/schema-block.ts
@@ -17,6 +17,7 @@ export type SchemaBlockType =
'jgn-program-name-ja-input' |
'jgn-program-name-en-input' |
'e-rad-award-funder-input' |
+ 'single-select-pulldown-input' |
'pulldown-input' |
'e-rad-award-number-input' |
'e-rad-award-title-ja-input' |
@@ -27,6 +28,7 @@ export type SchemaBlockType =
'e-rad-researcher-name-en-input' |
'e-rad-bunnya-input' |
'file-metadata-input' |
+ 'ad-metadata-input' |
'date-input' |
'array-input';
@@ -44,7 +46,12 @@ export interface SchemaBlock {
index?: number;
pattern?: string;
spaceNormalization?: boolean;
- autoDate?: boolean;
- autoTitle?: boolean;
hideProjectmetadata?: boolean;
+ retrievalTitle?: string;
+ retrievalDate?: string;
+ concealmentPageNavigator?: boolean;
+ requiredAllCheck?: string;
+ multiLanguage?: boolean;
+ retrievalVersion?: string;
+ rowAdditionCaption?: string;
}
diff --git a/app/packages/registration-schema/validations.ts b/app/packages/registration-schema/validations.ts
index e844e3476..362a21ca9 100644
--- a/app/packages/registration-schema/validations.ts
+++ b/app/packages/registration-schema/validations.ts
@@ -161,6 +161,47 @@ export function setupEventForSyncValidation(changeset: ChangesetDef, groups: Sch
});
}
+export function setupEventForSyncValidation2(changeset: ChangesetDef, groups: SchemaBlockGroup[]) {
+ const requiredAllCheckGroups = groups
+ // ignore GRDM file specific fields
+ .filter((group: SchemaBlockGroup) => !group.registrationResponseKey
+ || !group.registrationResponseKey.match(/^__responseKey_grdm-file:.+$/))
+ .filter((group: SchemaBlockGroup) => group.inputBlock && group.inputBlock.requiredAllCheck);
+
+ let isProcesing = false;
+
+ changeset.on('afterValidation', () => {
+ if (isProcesing) {
+ return;
+ }
+ isProcesing = true;
+
+ try {
+ const checkboxList = requiredAllCheckGroups.map(group => {
+ const registrationResponseKey: string = group.registrationResponseKey || '';
+ const value = changeset.get(registrationResponseKey);
+ return Array.isArray(value) && value.length === 1;
+ });
+
+ requiredAllCheckGroups
+ .forEach(group => {
+ if (!checkboxList.includes(false)) {
+ const todayDate = `${new Date().getFullYear()}/${
+ String(new Date().getMonth() + 1).padStart(2, '0')
+ }/${
+ String(new Date().getDate()).padStart(2, '0')
+ }`;
+ changeset.set(`__responseKey_${group.inputBlock!.requiredAllCheck}`, todayDate);
+ } else {
+ changeset.set(`__responseKey_${group.inputBlock!.requiredAllCheck}`, '');
+ }
+ });
+ } finally {
+ isProcesing = false;
+ }
+ });
+}
+
export function validateNodeLicense() {
return async (_: unknown, __: unknown, ___: unknown, changes: DraftRegistration, content: DraftRegistration) => {
let validateLicenseTarget = await content.license;
diff --git a/app/router.ts b/app/router.ts
index af203a45c..0b4285cf8 100644
--- a/app/router.ts
+++ b/app/router.ts
@@ -142,6 +142,7 @@ Router.map(function() {
this.mount('analytics-page', { as: 'analytics' });
this.route('forks');
this.route('iqbrims');
+ this.route('workflow');
this.route('binderhub');
this.route('metadata');
this.route('registrations');
diff --git a/app/serializers/workflow-config.ts b/app/serializers/workflow-config.ts
new file mode 100644
index 000000000..41ddd2503
--- /dev/null
+++ b/app/serializers/workflow-config.ts
@@ -0,0 +1,10 @@
+import OsfSerializer from './osf-serializer';
+
+export default class WorkFlowConfigSerializer extends OsfSerializer {
+}
+
+declare module 'ember-data/types/registries/serializer' {
+ export default interface SerializerRegistry {
+ 'workflow-config': WorkFlowConfigSerializer;
+ } // eslint-disable-line semi
+}
diff --git a/lib/osf-components/addon/components/contributor-list/component.ts b/lib/osf-components/addon/components/contributor-list/component.ts
index 595e5bc45..ff6795b13 100644
--- a/lib/osf-components/addon/components/contributor-list/component.ts
+++ b/lib/osf-components/addon/components/contributor-list/component.ts
@@ -60,7 +60,15 @@ export default class ContributorList extends Component {
this.set('totalContributors', nextPage.meta.total);
} else {
this.set('page', 1);
- const firstPage = yield this.node.bibliographicContributors;
+ const firstPage = yield this.node.queryHasMany(
+ 'bibliographicContributors',
+ {
+ fields: {
+ users: 'full_name,given_name,family_name,id,links',
+ },
+ page: { size: 10 },
+ },
+ );
this.setProperties({
displayedContributors: firstPage.toArray(),
totalContributors: firstPage.meta.total,
diff --git a/lib/osf-components/addon/components/form-controls/template.hbs b/lib/osf-components/addon/components/form-controls/template.hbs
index d8cb46a9f..70454fe6e 100644
--- a/lib/osf-components/addon/components/form-controls/template.hbs
+++ b/lib/osf-components/addon/components/form-controls/template.hbs
@@ -14,13 +14,6 @@
shouldShowMessages=this.shouldShowMessages
disabled=this.disabled
)
- autodate=(
- component
- 'validated-input/autodate'
- changeset=@changeset
- shouldShowMessages=this.shouldShowMessages
- disabled=this.disabled
- )
recaptcha=(
component
'validated-input/recaptcha'
diff --git a/lib/osf-components/addon/components/node-navbar/component.ts b/lib/osf-components/addon/components/node-navbar/component.ts
index c7bcf1127..55064a2a6 100644
--- a/lib/osf-components/addon/components/node-navbar/component.ts
+++ b/lib/osf-components/addon/components/node-navbar/component.ts
@@ -106,6 +106,22 @@ export default class NodeNavbar extends Component {
return result;
}
+ @computed('node.addons.[]')
+ get workflowEnabled(): Promise | null {
+ if (!this.node) {
+ return null;
+ }
+ const { node } = this;
+ return (async () => {
+ const addons = await node.addons;
+ if (!addons) {
+ return false;
+ }
+ const workflow = addons.filter(addon => addon.id === 'workflow');
+ return workflow.length > 0;
+ })();
+ }
+
@action
toggleNav() {
this.toggleProperty('collapsedNav');
diff --git a/lib/osf-components/addon/components/node-navbar/template.hbs b/lib/osf-components/addon/components/node-navbar/template.hbs
index a107122f9..59055b937 100644
--- a/lib/osf-components/addon/components/node-navbar/template.hbs
+++ b/lib/osf-components/addon/components/node-navbar/template.hbs
@@ -35,6 +35,9 @@
{{/node-navbar/link}}
{{node-navbar/link node=@node useLinkTo=false destination='files'}}
+ {{#if this.workflowEnabled }}
+ {{node-navbar/link node=@node useLinkTo=false destination='workflow'}}
+ {{/if}}
{{#if this.iqbrimsEnabled }}
{{node-navbar/link node=@node useLinkTo=false destination='iqbrims'}}
{{/if}}
diff --git a/lib/osf-components/addon/components/osf-layout/registries-side-nav/component.ts b/lib/osf-components/addon/components/osf-layout/registries-side-nav/component.ts
index 4aeeb128b..abc2898d6 100644
--- a/lib/osf-components/addon/components/osf-layout/registries-side-nav/component.ts
+++ b/lib/osf-components/addon/components/osf-layout/registries-side-nav/component.ts
@@ -5,7 +5,11 @@ import { and, or } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Media from 'ember-responsive';
+import DS from 'ember-data';
+import Intl from 'ember-intl/services/intl';
import { layout } from 'ember-osf-web/decorators/component';
+import DraftRegistration from 'ember-osf-web/models/draft-registration';
+import Toast from 'ember-toastr/services/toast';
import styles from './styles';
import template from './template';
@@ -13,7 +17,15 @@ import template from './template';
@tagName('')
@layout(template, styles)
export default class RegistriesSideNav extends Component {
+ @service store!: DS.Store;
+ draftRegistrations: DraftRegistration[] = [];
+ // registrationSchema: RegistrationSchema[] = [];
+ @service toast!: Toast;
+ format?: string = '';
+
@service media!: Media;
+ @service intl!: Intl;
+ // changeset!: ChangesetDef;
// Optional params
onLinkClicked?: () => void;
@@ -27,8 +39,84 @@ export default class RegistriesSideNav extends Component {
@and('isCollapseAllowed', 'shouldCollapse')
isCollapsed!: boolean;
+ @service router!: any;
+ disableButtons = false;
+ pageTitle = '';
+ jsonData = '';
+
+ constructor(...args: any[]) {
+ super(...args);
+ this.handleRouteChange();
+ if (this.router && typeof this.router.on === 'function') {
+ this.router.on('routeDidChange', this.handleRouteChange.bind(this));
+ }
+ }
+
@action
toggle() {
this.toggleProperty('shouldCollapse');
}
+
+ @action
+ async handleRouteChange() {
+ const currentUrl = window.location.href;
+ const urlParts = currentUrl.split('/');
+ const lastPartWithQuery = urlParts[urlParts.length - 1];
+ const metadataTitle = lastPartWithQuery.split('?')[0];
+ const cleanedTitle = metadataTitle.replace(/^\d+-/, '');
+ const title = decodeURIComponent(cleanedTitle.replace(/-/g, ' '));
+ this.set('pageTitle', title.trim().toLowerCase());
+ const allSchemas = await this.store.findAll('registration-schema');
+ const matchedSchema = allSchemas.find((schema: any) => schema.schema.pages.some((page: any) => (
+ page.title.trim().toLowerCase() === title.trim().toLowerCase()
+ && page.clipboardCopyPaste === false)));
+
+ if (matchedSchema) {
+ this.set('disableButtons', true);
+ } else {
+ this.set('disableButtons', false);
+ }
+ }
+
+ @action
+ async copyToClipboard() {
+ const currentUrl = window.location.href;
+ const idRegex = /\/drafts\/([a-f0-9]{24})/;
+ const match = currentUrl.match(idRegex);
+ if (match && match[1]) {
+ const draftId = match[1];
+
+ const draftRegistration = await this.store.findRecord('draft-registration', draftId);
+
+ if (draftRegistration) {
+ const registrationMetadata = await draftRegistration.registrationMetadata;
+ const metadata: { [key: string]: any } = {};
+ const registrationSchema = await draftRegistration.registrationSchema;
+ for (const page of registrationSchema.schema.pages) {
+ if (page.title.trim().toLowerCase() !== this.pageTitle && !page.clipboardCopyPaste) {
+ continue;
+ }
+
+ for (const question of page.questions) {
+ Object.keys(registrationMetadata).forEach(key => {
+ if (key === question.qid && !key.startsWith('grdm-')) {
+ if (question.format === 'multiselect' && registrationMetadata[key].value === '') {
+ metadata[key] = [];
+ } else {
+ metadata[key] = registrationMetadata[key].value;
+ }
+ }
+ });
+ }
+ }
+ this.jsonData = JSON.stringify(metadata);
+ if (this.jsonData === '{}' || Object.keys(JSON.parse(this.jsonData)).length === 0) {
+ this.toast.warning(this.intl.t('registries.drafts.draft.form.warning_noautosave'));
+ } else {
+ await navigator.clipboard.writeText(this.jsonData);
+ this.toast.success(this.intl.t('registries.drafts.draft.form.clipboard_copied'));
+ }
+ }
+ }
+ }
}
diff --git a/lib/osf-components/addon/components/osf-layout/registries-side-nav/label/styles.scss b/lib/osf-components/addon/components/osf-layout/registries-side-nav/label/styles.scss
index 7725fff7f..8610e153d 100644
--- a/lib/osf-components/addon/components/osf-layout/registries-side-nav/label/styles.scss
+++ b/lib/osf-components/addon/components/osf-layout/registries-side-nav/label/styles.scss
@@ -1,10 +1,11 @@
.LabelWrapper {
justify-content: space-between;
- display: inline-flex;
+ display: flex;
width: 100%;
padding-left: 10px;
font-weight: 700;
- white-space: inherit;
+ white-space: normal;
+ word-wrap: break-word;
overflow: hidden;
&:hover {
@@ -16,6 +17,8 @@
float: left;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: normal;
+ word-wrap: break-word;
&:hover {
overflow: visible;
@@ -24,4 +27,7 @@
.Count {
float: right;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
diff --git a/lib/osf-components/addon/components/osf-layout/registries-side-nav/template.hbs b/lib/osf-components/addon/components/osf-layout/registries-side-nav/template.hbs
index 3c41565ce..44348815a 100644
--- a/lib/osf-components/addon/components/osf-layout/registries-side-nav/template.hbs
+++ b/lib/osf-components/addon/components/osf-layout/registries-side-nav/template.hbs
@@ -38,4 +38,23 @@
{{/let}}
{{/if}}
+
+ {{!-- model1 :{{@model}}
+ model2 : {{model}}
+ {{#each @model as |item index|}}
+ {{#if (eq index 1)}}
+ test >>{{item}}
+ {{/if}}
+ {{/each}}
+ test --}}
+
+ {{t 'registries.drafts.draft.form.copy_to_clipboard'}}
+
+
diff --git a/lib/osf-components/addon/components/registries/registration-form-navigation-dropdown/template.hbs b/lib/osf-components/addon/components/registries/registration-form-navigation-dropdown/template.hbs
index 2e0df5bd2..576899fe8 100644
--- a/lib/osf-components/addon/components/registries/registration-form-navigation-dropdown/template.hbs
+++ b/lib/osf-components/addon/components/registries/registration-form-navigation-dropdown/template.hbs
@@ -60,16 +60,18 @@
{{/each}}
{{/if}}
{{#each this.localizedBlocksWithAnchor as |block|}}
-
-
- {{block.localizedDisplayText}}
-
-
+ {{#unless block.model.concealmentPageNavigator}}
+
+
+ {{block.localizedDisplayText}}
+
+
+ {{/unless}}
{{/each}}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/files/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/files/template.hbs
index e9f22edca..0a53fb0d4 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/files/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/files/template.hbs
@@ -1,13 +1,15 @@
-
- {{t 'osf-components.registries.schema-block-renderer/editable/files.instructions'
- projectOrComponent=(if @node.isRoot 'project' 'component')
- nodeUrl=this.nodeUrl
- htmlSafe=true
- }}
-
+{{#if (not @schemaBlock.sentence)}}
+
+ {{t 'osf-components.registries.schema-block-renderer/editable/files.instructions'
+ projectOrComponent=(if @node.isRoot 'project' 'component')
+ nodeUrl=this.nodeUrl
+ htmlSafe=true
+ }}
+
+{{/if}}
void;
+
+ @alias('schemaBlock.schema.id')
+ schemaId!: any;
+
+ folderExpands: {[key: string]: boolean} = {};
+
+ item: File[] = [];
+ @service store!: DS.Store;
+ currentFolder!: File;
+
+ fileProvider!: FileProvider;
+ @requiredAction openFile!: (file: File, show: string) => void;
+
+ @task
+ getCurrentFolderItems = task(function *(this: AdMetadataInput, targetFolder: File) {
+ this.set('currentFolder', targetFolder);
+ const folderItems = yield this.currentFolder.files;
+ const itemsArray = folderItems.toArray();
+ this.set('item', this.item.concat(itemsArray));
+
+ const paths = this.get('projectFilePaths');
+ const folderExpands = this.get('folderExpands');
+ if (!Object.values(folderExpands).length) {
+ paths.forEach(path => {
+ if (path.endsWith('/') && path.split('/').length === 2) {
+ folderExpands[path] = true;
+ }
+ });
+ this.set('folderExpands', folderExpands);
+ }
+
+ for (const item of itemsArray) {
+ if (item.materializedPath.match(/.+\/$/)) {
+ yield this.getCurrentFolderItems.perform(item);
+ }
+ }
+ });
+
+ async didInsertElement() {
+ super.didInsertElement();
+ const fileProviders = await this.node.files;
+ const fileProvider = fileProviders.findBy('name', 'osfstorage') as FileProvider;
+ const rootFolder = await fileProvider.rootFolder;
+ this.getCurrentFolderItems.perform(rootFolder);
+ }
+
+ @computed('node')
+ get nodeUrl() {
+ return this.node && pathJoin(baseURL, this.node.id);
+ }
+
+ @computed('changeset', 'valuePath')
+ get adMetadatas(): FileMetadata[] {
+ const value = this.changeset.get(this.valuePath);
+ if (!value) {
+ return [];
+ }
+ const metadatas: FileMetadata[] = JSON.parse(value);
+ metadatas.sort((a, b) => a.path.localeCompare(b.path));
+ return metadatas;
+ }
+
+ @computed('item')
+ get projectFileMetadata(): FileMetadata[] {
+ const res: FileMetadata[] = [];
+ this.get('item').forEach(item => {
+ res.push({
+ path: item.provider + item.materializedPath,
+ urlpath: '',
+ metadata: {},
+ });
+ });
+ return res;
+ }
+
+ @computed('projectFileMetadata')
+ get projectFilePaths(): string[] {
+ const projectFileMetadatas = this.get('projectFileMetadata');
+ const pathSet = new Set();
+ projectFileMetadatas.forEach(projectFileMetadata => {
+ let path = '';
+ const parts = projectFileMetadata.path.split('/');
+ parts.forEach((part, i) => {
+ if (!part.length) {
+ return;
+ }
+ path += part;
+ if (i + 1 < parts.length) {
+ path += '/';
+ }
+ pathSet.add(path);
+ });
+ });
+ return Array.from(pathSet).sort((a, b) => a.localeCompare(b));
+ }
+
+ @computed('adMetadatas', 'projectFileMetadata', 'projectFilePaths', 'folderExpands')
+ get fileEntries(): FileEntry[] {
+ const metadataMap: {[key: string]: FileMetadata} = {};
+ this.get('adMetadatas').forEach(metadata => {
+ metadataMap[metadata.path] = metadata;
+ });
+ const projectFileMetadataMap: {[key: string]: FileMetadata} = {};
+
+ this.get('projectFileMetadata').forEach(projectFileMetadata => {
+ projectFileMetadataMap[projectFileMetadata.path] = projectFileMetadata;
+ });
+
+ const paths = this.get('projectFilePaths');
+ const folderExpands = this.get('folderExpands');
+ const res = paths.map(path => {
+ const metadata = metadataMap[path] || projectFileMetadataMap[path];
+ const parts = path.split('/');
+ if (!parts[parts.length - 1].length) {
+ parts.pop();
+ }
+ const folder = path.match(/.+\/$/) !== null;
+ // 18
+ return {
+ path,
+ parts,
+ lastPart: parts[parts.length - 1],
+ lastPartDepth: parts.length,
+ folder,
+ title: metadata ? this.extractTitleFromMetadata(metadata) : null,
+ manager: metadata ? this.extractManagerFromMetadata(metadata) : null,
+ url: metadata ? this.extractUrlFromMetadata(metadata) : null,
+ fileUrl: metadata && metadata.urlpath ? `${pathJoin(baseURL, metadata.urlpath)}#edit-metadata` : null,
+ metadata,
+ added: metadataMap[path] != null,
+ hasProject: projectFileMetadataMap[path] != null,
+ style: `margin: 0 0 0 ${parts.length * 16 + (folder ? 0 : 18)}px`,
+ visible: [...parts.slice(0, parts.length - 1).keys()]
+ .every(i => folderExpands[`${parts.slice(0, i + 1).join('/')}/`]),
+ folderExpanded: folderExpands[path],
+ } as FileEntry;
+ });
+ return res;
+ }
+
+ didReceiveAttrs() {
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a changeset to render',
+ Boolean(this.changeset),
+ );
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a node to render',
+ Boolean(this.node),
+ );
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a valuePath to render',
+ Boolean(this.valuePath),
+ );
+ }
+
+ saveFileMetadatas(metadatas: FileMetadata[]) {
+ metadatas.sort((a, b) => a.path.localeCompare(b.path));
+ this.changeset.set(
+ this.valuePath,
+ metadatas.length ? JSON.stringify(metadatas) : null,
+ );
+ this.onInput();
+ this.notifyPropertyChange('adMetadatas');
+ }
+
+ @action
+ addFileMetadata(this: AdMetadataInput, entry: FileEntry) {
+ const metadatas = this.get('adMetadatas');
+ if (entry.metadata) {
+ metadatas.push(entry.metadata);
+ }
+ this.saveFileMetadatas(metadatas);
+ }
+
+ @action
+ removeFileMetadata(this: AdMetadataInput, entry: FileEntry) {
+ const metadatas = this.get('adMetadatas');
+ const metadata = metadatas.find(m => m.path === entry.path);
+ if (metadata) {
+ metadatas.splice(metadatas.indexOf(metadata), 1);
+ }
+ this.saveFileMetadatas(metadatas);
+ }
+
+ extractTitleFromMetadata(metadata: FileMetadata): string | null {
+ const titleJa = metadata.metadata['grdm-file:title-ja'];
+ const titleEn = metadata.metadata['grdm-file:title-en'];
+ if (!titleJa && !titleEn) {
+ return null;
+ }
+ if (titleJa && !titleEn) {
+ return `${titleJa.value}`;
+ }
+ if (!titleJa && titleEn) {
+ return `${titleEn.value}`;
+ }
+ if (!titleJa.value && !titleEn.value) {
+ return null;
+ }
+ if (this.intl.locale.includes('ja')) {
+ return `${titleJa.value}`;
+ }
+ return `${titleEn.value}`;
+ }
+
+ extractManagerFromMetadata(metadata: FileMetadata): string | null {
+ const managerJa = metadata.metadata['grdm-file:data-man-name-ja'];
+ const managerEn = metadata.metadata['grdm-file:data-man-name-en'];
+ if (!managerJa && !managerEn) {
+ return null;
+ }
+ let value;
+ if (managerJa && !managerEn) {
+ value = managerJa.value;
+ } else if (!managerJa && managerEn) {
+ value = managerEn.value;
+ } else if (this.intl.locale.includes('ja')) {
+ value = managerJa.value;
+ } else {
+ value = managerEn.value;
+ }
+ if (value && typeof value === 'object') {
+ return (
+ this.intl.locale.includes('ja')
+ ? [value.last, value.middle, value.first]
+ : [value.first, value.middle, value.last]
+ ).filter(v => v).join(' ');
+ }
+ return value;
+ }
+
+ extractUrlFromMetadata(metadata: FileMetadata): string | null {
+ const url = metadata.metadata['grdm-file:repo-url-doi-link'];
+ if (!url) {
+ return null;
+ }
+ return `${url.value}`;
+ }
+
+ @action
+ expandFolder(this: AdMetadataInput, entry: FileEntry, expand: boolean) {
+ const folderExpands = this.get('folderExpands');
+ folderExpands[entry.path] = expand;
+ this.set('folderExpands', folderExpands);
+ this.notifyPropertyChange('folderExpands');
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/styles.scss
new file mode 100644
index 000000000..f29e4bdb2
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/styles.scss
@@ -0,0 +1,49 @@
+.file-metadata-input-container {
+ width: 100%;
+ border: 1px solid #eee;
+ padding: 0.5em;
+}
+
+.file-metadata-input-files {
+ border: 1px solid #eee;
+}
+
+.file-metadata-input-files th {
+ border: 1px solid #eee;
+ background: #f5f5f5;
+ height: 35px;
+}
+
+.file-metadata-input-files td {
+ border-top: 1px solid #eee;
+ height: 35px;
+}
+
+.file-metadata-input-files .file-metadata-path {
+ max-width: 250px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-files .file-metadata-title {
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-files .file-metadata-manager {
+ max-width: 50px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-edit-button {
+ padding: 0 !important;
+}
+
+.file-metadata-input-buttons {
+ margin-bottom: 4px;
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/template.hbs
new file mode 100644
index 000000000..0551275fe
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/template.hbs
@@ -0,0 +1,75 @@
+
+ {{#if this.fileEntries}}
+
+
+
+ |
+ |
+
+ {{t 'metadata.file-metadata-input.columns.path'}}
+ |
+
+
+
+ {{#each this.fileEntries as |fileEntry|}}
+ {{#if fileEntry.visible}}
+
+ |
+ {{#if fileEntry.added}}
+
+ {{else if fileEntry.hasProject}}
+
+ {{/if}}
+ |
+
+
+ {{#if fileEntry.folder}}
+ {{#if fileEntry.folderExpanded}}
+
+ {{else}}
+
+ {{/if}}
+
+ {{else}}
+
+ {{/if}}
+ {{fileEntry.lastPart}}
+
+ |
+
+ {{/if}}
+ {{/each}}
+
+
+ {{else}}
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/component.ts
index 4b2a45a66..e151ca58b 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/component.ts
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/component.ts
@@ -4,13 +4,17 @@ import Component from '@ember/component';
import { assert } from '@ember/debug';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+
import Changeset from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
import { ChangesetDef } from 'ember-changeset/types';
+import Intl from 'ember-intl/services/intl';
import { layout } from 'ember-osf-web/decorators/component';
import NodeModel from 'ember-osf-web/models/node';
-import { buildValidation, SchemaBlockGroup } from 'ember-osf-web/packages/registration-schema';
+import { buildValidation, SchemaBlock, SchemaBlockGroup } from 'ember-osf-web/packages/registration-schema';
import DraftRegistrationManager from 'registries/drafts/draft/draft-registration-manager';
+
import styles from './styles';
import template from './template';
@@ -21,17 +25,23 @@ export default class ArrayInput extends Component {
changeset!: ChangesetDef;
metadataChangeset!: ChangesetDef;
draftManager!: DraftRegistrationManager;
+ @service intl!: Intl;
@alias('schemaBlock.registrationResponseKey')
valuePath!: string;
onInput!: () => void;
onMetadataInput!: () => void;
schemaBlockGroup!: SchemaBlockGroup;
+ schemaBlock!: SchemaBlock;
node!: NodeModel;
subChangesets: ChangesetDef[] = [];
didReceiveAttrs() {
+ const rowAdditionCaption = this.schemaBlock.rowAdditionCaption || '';
+
+ this.schemaBlock.rowAdditionCaption = this.getLocalizedText(rowAdditionCaption);
+
const raw = this.changeset.get(this.valuePath);
if (raw) {
const prefix = `${this.valuePath}|`;
@@ -98,4 +108,15 @@ export default class ArrayInput extends Component {
this.set('subChangesets', newSubChangesets);
this.save(newSubChangesets);
}
+
+ getLocalizedText(text: string) {
+ if (!text.includes('|')) {
+ return text;
+ }
+ const texts = text.split('|');
+ if (this.intl.locale.includes('ja')) {
+ return texts[0];
+ }
+ return texts[1];
+ }
}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/template.hbs
index 3bf4592c3..6c41fd193 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/array-input/template.hbs
@@ -23,7 +23,7 @@
- {{t 'metadata.array-input.remove-item'}}
+ {{this.schemaBlock.rowAdditionCaption}}{{t 'metadata.array-input.remove-item'}}
{{/each}}
@@ -32,7 +32,7 @@
@type='primary'
@onClick={{action this.onAdd}}
>
- {{t 'metadata.array-input.add-item'}}
+ {{this.schemaBlock.rowAdditionCaption}}{{t 'metadata.array-input.add-item'}}
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-number-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-number-input/component.ts
index 8756ce0e8..440b07045 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-number-input/component.ts
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-number-input/component.ts
@@ -54,6 +54,7 @@ export default class ERadAwardNumberInput extends Component {
if (eradRecord) {
kadaiId = eradRecord.kadai_id;
this.updateCode('e-rad-award-funder-input', eradRecord.haibunkikan_cd);
+ this.updateCode('single-select-pulldown-input', eradRecord.haibunkikan_cd);
this.updateCode('e-rad-award-field-input', eradRecord.bunya_cd);
this.changeset.set(
this.draftManager.getResponseKeyByBlockType('e-rad-award-title-ja-input'),
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-en-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-en-input/template.hbs
index 23e459a95..25af56ad4 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-en-input/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-en-input/template.hbs
@@ -10,7 +10,6 @@
local-class='schema-block-input'
@uniqueID={{@uniqueID}}
@valuePath={{@schemaBlock.registrationResponseKey}}
- @onChange={{action this.onInput2}}
@onKeyUp={{action this.onInput2}}
@placeholder=' '
/>
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-ja-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-ja-input/template.hbs
index 55c15c85c..283351ef3 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-ja-input/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/e-rad-award-title-ja-input/template.hbs
@@ -10,7 +10,6 @@
local-class='schema-block-input'
@uniqueID={{@uniqueID}}
@valuePath={{@schemaBlock.registrationResponseKey}}
- @onChange={{action this.onInput2}}
@onKeyUp={{action this.onInput2}}
@placeholder=' '
/>
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/japan-grant-number-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/japan-grant-number-input/component.ts
index eb931644c..5bc8f34db 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/japan-grant-number-input/component.ts
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/japan-grant-number-input/component.ts
@@ -54,7 +54,7 @@ export default class JapanGrantNumberInput extends Component {
if (eradRecord) {
kadaiId = eradRecord.japan_grant_number;
this.updateCode('e-rad-award-funder-input', eradRecord.haibunkikan_cd);
- this.updateCode('pulldown-input', eradRecord.haibunkikan_cd);
+ this.updateCode('single-select-pulldown-input', eradRecord.haibunkikan_cd);
this.updateCode('e-rad-award-field-input', eradRecord.bunya_cd);
if (eradRecord.funding_stream_code) {
const key = this.getResponseKeyByBlockType('funding-stream-code-input');
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.ts
new file mode 100644
index 000000000..6562416a5
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.ts
@@ -0,0 +1,85 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { assert } from '@ember/debug';
+import { action, computed } from '@ember/object';
+import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+
+import { ChangesetDef } from 'ember-changeset/types';
+import Intl from 'ember-intl/services/intl';
+
+import { layout } from 'ember-osf-web/decorators/component';
+import { SchemaBlock } from 'ember-osf-web/packages/registration-schema';
+
+import template from './template';
+
+@layout(template)
+@tagName('')
+export default class SingleSelectPulldownInput extends Component {
+ @service intl!: Intl;
+ // Required param
+ optionBlocks!: SchemaBlock[];
+ changeset!: ChangesetDef;
+
+ @alias('schemaBlock.registrationResponseKey')
+ valuePath!: string;
+ onInput!: () => void;
+ onMetadataInput!: () => void;
+
+ anotherOption?: string;
+
+ didReceiveAttrs() {
+ assert(
+ 'SchemaBlockRenderer::Editable::SingleSelectPulldownInput requires optionBlocks to render',
+ Boolean(this.optionBlocks),
+ );
+ }
+
+ @computed('optionBlocks.[]', 'anotherOption')
+ get optionBlockValues() {
+ const options = this.optionBlocks
+ .map(item => this.getLocalizedItemText(item));
+ if (this.anotherOption) {
+ options.push(this.anotherOption);
+ }
+ return options;
+ }
+
+ @action
+ onChange(option: string) {
+ const code = (option || '').trim();
+ const item = this.optionBlocks.find(b => code === b.displayText);
+ const result = item ? item.displayText : option;
+ this.changeset.set(this.valuePath, result);
+ this.onMetadataInput();
+ this.onInput();
+ this.set('anotherOption', null);
+ }
+
+ @action
+ onInputSearch(text: string) {
+ if (!this.optionBlocks.find(item => item.displayText === text || this.getLocalizedItemText(item) === text)) {
+ this.set('anotherOption', text);
+ }
+ return true;
+ }
+
+ getLocalizedItemText(item: SchemaBlock) {
+ const text = item.helpText || item.displayText;
+ if (text === undefined) {
+ return item.displayText;
+ }
+ return `${item.displayText}`;
+ }
+
+ getLocalizedText(text: string) {
+ if (!text.includes('|')) {
+ return text;
+ }
+ const texts = text.split('|');
+ if (this.intl.locale.includes('ja')) {
+ return texts[0];
+ }
+ return texts[1];
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/styles.scss
new file mode 100644
index 000000000..462216f6a
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/styles.scss
@@ -0,0 +1,9 @@
+.schema-block-input {
+ :global(input) {
+ height: 40px;
+ background-color: $color-bg-gray-blue-light;
+ color: $color-text-gray-blue;
+ border-radius: 2px;
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/template.hbs
new file mode 100644
index 000000000..4fef23b8f
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/template.hbs
@@ -0,0 +1,26 @@
+
+
+
+ {{option}}
+
+
+
+
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.ts
new file mode 100644
index 000000000..6562416a5
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.ts
@@ -0,0 +1,85 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { assert } from '@ember/debug';
+import { action, computed } from '@ember/object';
+import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+
+import { ChangesetDef } from 'ember-changeset/types';
+import Intl from 'ember-intl/services/intl';
+
+import { layout } from 'ember-osf-web/decorators/component';
+import { SchemaBlock } from 'ember-osf-web/packages/registration-schema';
+
+import template from './template';
+
+@layout(template)
+@tagName('')
+export default class SingleSelectPulldownInput extends Component {
+ @service intl!: Intl;
+ // Required param
+ optionBlocks!: SchemaBlock[];
+ changeset!: ChangesetDef;
+
+ @alias('schemaBlock.registrationResponseKey')
+ valuePath!: string;
+ onInput!: () => void;
+ onMetadataInput!: () => void;
+
+ anotherOption?: string;
+
+ didReceiveAttrs() {
+ assert(
+ 'SchemaBlockRenderer::Editable::SingleSelectPulldownInput requires optionBlocks to render',
+ Boolean(this.optionBlocks),
+ );
+ }
+
+ @computed('optionBlocks.[]', 'anotherOption')
+ get optionBlockValues() {
+ const options = this.optionBlocks
+ .map(item => this.getLocalizedItemText(item));
+ if (this.anotherOption) {
+ options.push(this.anotherOption);
+ }
+ return options;
+ }
+
+ @action
+ onChange(option: string) {
+ const code = (option || '').trim();
+ const item = this.optionBlocks.find(b => code === b.displayText);
+ const result = item ? item.displayText : option;
+ this.changeset.set(this.valuePath, result);
+ this.onMetadataInput();
+ this.onInput();
+ this.set('anotherOption', null);
+ }
+
+ @action
+ onInputSearch(text: string) {
+ if (!this.optionBlocks.find(item => item.displayText === text || this.getLocalizedItemText(item) === text)) {
+ this.set('anotherOption', text);
+ }
+ return true;
+ }
+
+ getLocalizedItemText(item: SchemaBlock) {
+ const text = item.helpText || item.displayText;
+ if (text === undefined) {
+ return item.displayText;
+ }
+ return `${item.displayText}`;
+ }
+
+ getLocalizedText(text: string) {
+ if (!text.includes('|')) {
+ return text;
+ }
+ const texts = text.split('|');
+ if (this.intl.locale.includes('ja')) {
+ return texts[0];
+ }
+ return texts[1];
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/styles.scss
new file mode 100644
index 000000000..462216f6a
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/styles.scss
@@ -0,0 +1,9 @@
+.schema-block-input {
+ :global(input) {
+ height: 40px;
+ background-color: $color-bg-gray-blue-light;
+ color: $color-text-gray-blue;
+ border-radius: 2px;
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/template.hbs
new file mode 100644
index 000000000..4fef23b8f
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/template.hbs
@@ -0,0 +1,26 @@
+
+
+
+ {{option}}
+
+
+
+
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/text/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/text/template.hbs
index 9322a3674..1718d0582 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/editable/text/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/editable/text/template.hbs
@@ -11,9 +11,14 @@
@uniqueID={{@uniqueID}}
@valuePath={{@schemaBlock.registrationResponseKey}}
@onKeyUp={{@onInput}}
- @autoTitle={{@schemaBlock.autoTitle}}
- @autoDate={{@schemaBlock.autoDate}}
+ @retrievalTitle={{@schemaBlock.retrievalTitle}}
+ @retrievalDate={{@schemaBlock.retrievalDate}}
+ @retrievalVersion={{@schemaBlock.retrievalVersion}}
+ @readonly={{@schemaBlock.readonly}}
@title={{@draftManager.node.title}}
+ @nodeId={{@node.id}}
+ @datetimeInitiated={{@draftManager.draftRegistration.datetimeInitiated}}
+ @datetimeUpdated={{@draftManager.draftRegistration.datetimeUpdated}}
@placeholder=' '
/>
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/component.ts
index 42b7e43f0..6ae178992 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/component.ts
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/component.ts
@@ -49,6 +49,25 @@ export default class LabelContent extends Component {
return this.getLocalizedText(text);
}
+ @computed('localizedHelpText')
+ get localizedHelpTextLines() {
+ const text = this.localizedHelpText;
+ if (!text) {
+ return [];
+ }
+
+ return text.split('\n').map(line => {
+ const urlRegex = /(https?:\/\/[^\s]+)/g;
+
+ const parts = line.split(urlRegex).map(part => ({
+ content: part,
+ isLink: part.startsWith('https://'),
+ }));
+
+ return parts;
+ });
+ }
+
getLocalizedText(text: string) {
if (!text.includes('|')) {
return text;
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/styles.scss
index 873f7ddf6..30c9d8563 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/styles.scss
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/styles.scss
@@ -1,7 +1,6 @@
.DisplayText {
white-space: pre-wrap;
display: inline-block;
- margin: 0;
}
.Required {
@@ -13,8 +12,8 @@
.HelpText {
composes: Element from '../../styles';
- white-space: pre-wrap;
font-weight: 400;
+ margin-top: 0;
}
.ExampleButton {
@@ -37,3 +36,9 @@
white-space: pre-wrap;
font-weight: 400;
}
+
+
+.HelpTextLine {
+ font-weight: normal !important;
+ margin-bottom: 0;
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/template.hbs
index 3901cadcb..0af64917a 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/label/label-content/template.hbs
@@ -8,9 +8,26 @@
{{~/if~}}
{{#if @isEditableForm}}
-
- {{~this.localizedHelpText~}}
-
+
+ {{#each this.localizedHelpTextLines as |lineParts|}}
+
+ {{#each lineParts as |part|}}
+ {{#if part.isLink}}
+
+ {{part.content}}
+
+ {{else}}
+ {{part.content}}
+ {{/if}}
+ {{/each}}
+
+ {{/each}}
+
+
{{#if @schemaBlock.exampleText}}
{
+ const parts = line.split(urlPattern).map(part => (
+ urlPattern.test(part)
+ ? { type: 'link', content: part.trim() }
+ : { type: 'text', content: part.trim() }
+ ));
+ return parts;
+ });
+
+ return lines;
}
getLocalizedText(text: string) {
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/styles.scss
index cff4b6753..907b577b0 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/styles.scss
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/styles.scss
@@ -5,5 +5,5 @@
}
.PageHeading_helper {
- margin-top: 10px;
+ margin-bottom: 0;
}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/template.hbs
index 4e43c5f7f..814ec79eb 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/page-heading/template.hbs
@@ -1,4 +1,4 @@
-{{#unless @schemaBlock.hideProjectmetadata}}
+{{#if (or (not @schemaBlock.concealmentPageNavigator) (eq @schemaBlock.concealmentPageNavigator undefined)) }}
{{this.localizedDisplayText}}
-{{/unless}}
+{{/if}}
{{#if this.isEditableForm}}
- {{this.localizedHelpText}}
+
+ {{#each this.localizedHelpText as |line|}}
+
+ {{#each line as |part|}}
+ {{#if (eq part.type 'link')}}
+
+ {{part.content}}
+
+ {{else}}
+ {{part.content}}
+ {{/if}}
+ {{/each}}
+
+ {{/each}}
+
{{/if}}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/mapper/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/mapper/template.hbs
index 9e9e2e9cd..3c47bbd32 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/mapper/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/mapper/template.hbs
@@ -129,6 +129,13 @@
changeset=@changeset
node=@node
)
+ ad-metadata-input=(
+ component
+ 'registries/schema-block-renderer/read-only/rdm/ad-metadata-input'
+ registrationResponses=@registrationResponses
+ changeset=@changeset
+ node=@node
+ )
array-input=(
component
'registries/schema-block-renderer/read-only/rdm/array-input'
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.ts b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.ts
new file mode 100644
index 000000000..8e3980ab5
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.ts
@@ -0,0 +1,79 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { assert } from '@ember/debug';
+
+import { computed } from '@ember/object';
+import { alias } from '@ember/object/computed';
+import { inject as service } from '@ember/service';
+import { ChangesetDef } from 'ember-changeset/types';
+import Intl from 'ember-intl/services/intl';
+import { layout } from 'ember-osf-web/decorators/component';
+import NodeModel from 'ember-osf-web/models/node';
+import styles from './styles';
+import template from './template';
+
+interface FileMetadataEntity {
+ comments?: any[];
+ extra?: any[];
+ value: any;
+}
+
+interface FileMetadata {
+ path: string;
+ urlpath: string | null;
+ metadata: {
+ [key: string]: FileMetadataEntity,
+ };
+}
+
+interface FileEntry {
+ path: string;
+}
+
+@layout(template, styles)
+@tagName('')
+export default class AdMetadataInput extends Component {
+ @service intl!: Intl;
+
+ // Required param
+ changeset!: ChangesetDef;
+ node!: NodeModel;
+
+ @alias('schemaBlock.registrationResponseKey')
+ valuePath!: string;
+ onInput!: () => void;
+
+ didReceiveAttrs() {
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a changeset to render',
+ Boolean(this.changeset),
+ );
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a node to render',
+ Boolean(this.node),
+ );
+ assert(
+ 'Registries::SchemaBlockRenderer::Editable::Rdm::AdMetadataInput requires a valuePath to render',
+ Boolean(this.valuePath),
+ );
+ }
+
+ @computed('changeset', 'valuePath')
+ get adMetadatas(): FileMetadata[] {
+ if (!this.changeset) {
+ return [];
+ }
+ const value = this.changeset.get(this.valuePath);
+ if (!value) {
+ return [];
+ }
+ return JSON.parse(value) as FileMetadata[];
+ }
+
+ @computed('adMetadatas')
+ get fileEntries(): FileEntry[] {
+ return this.get('adMetadatas').map(metadata => ({
+ path: metadata.path,
+ }) as FileEntry);
+ }
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/styles.scss
new file mode 100644
index 000000000..f29e4bdb2
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/styles.scss
@@ -0,0 +1,49 @@
+.file-metadata-input-container {
+ width: 100%;
+ border: 1px solid #eee;
+ padding: 0.5em;
+}
+
+.file-metadata-input-files {
+ border: 1px solid #eee;
+}
+
+.file-metadata-input-files th {
+ border: 1px solid #eee;
+ background: #f5f5f5;
+ height: 35px;
+}
+
+.file-metadata-input-files td {
+ border-top: 1px solid #eee;
+ height: 35px;
+}
+
+.file-metadata-input-files .file-metadata-path {
+ max-width: 250px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-files .file-metadata-title {
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-files .file-metadata-manager {
+ max-width: 50px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.file-metadata-input-edit-button {
+ padding: 0 !important;
+}
+
+.file-metadata-input-buttons {
+ margin-bottom: 4px;
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/template.hbs
new file mode 100644
index 000000000..0bdf64fdc
--- /dev/null
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/template.hbs
@@ -0,0 +1,24 @@
+
+ {{#if this.fileEntries }}
+
+
+
+ |
+ {{t 'metadata.file-metadata-input.columns.path'}}
+ |
+
+
+
+ {{#each this.fileEntries as |fileEntry|}}
+
+ |
+ {{fileEntry.path}}
+ |
+
+ {{/each}}
+
+
+ {{else}}
+
+ {{/if}}
+
\ No newline at end of file
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/styles.scss b/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/styles.scss
index 24b104d74..fa7090685 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/styles.scss
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/styles.scss
@@ -3,3 +3,19 @@
color: $color-text-gray-blue;
}
+
+.HelpText {
+ composes: Element from '../styles';
+
+ // white-space: pre-wrap;
+ font-weight: 400;
+ margin-top: 0;
+}
+
+
+.HelpTextLine {
+ font-weight: normal !important;
+ margin-bottom: 0;
+ font-size: 14px;
+ margin-top: 17px;
+}
diff --git a/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/template.hbs b/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/template.hbs
index 94bc882bb..58636f61b 100644
--- a/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/template.hbs
+++ b/lib/osf-components/addon/components/registries/schema-block-renderer/section-heading/template.hbs
@@ -4,6 +4,9 @@
...attributes
>
{{this.localizedDisplayText}}
+
+
{{this.schemaBlock.helpText}}
+
{{#if this.isEditableForm}}
diff --git a/lib/osf-components/addon/components/validated-input/text/component.ts b/lib/osf-components/addon/components/validated-input/text/component.ts
index 13cf6c92f..0f0338ec0 100644
--- a/lib/osf-components/addon/components/validated-input/text/component.ts
+++ b/lib/osf-components/addon/components/validated-input/text/component.ts
@@ -1,9 +1,8 @@
+import { action } from '@ember/object';
+import { inject as service } from '@ember/service';
import DS, { AttributesFor } from 'ember-data';
-
import { layout } from 'ember-osf-web/decorators/component';
import defaultTo from 'ember-osf-web/utils/default-to';
-
-import { action } from '@ember/object';
import BaseValidatedComponent from '../base-component';
import template from './template';
@@ -11,12 +10,54 @@ import template from './template';
export default class ValidatedText extends BaseValidatedComponent {
valuePath!: AttributesFor;
+ @service store!: DS.Store;
+
// Additional arguments
password: boolean = defaultTo(this.password, false);
onKeyUp?: () => void; // Action
- onChange?: () => void; // Action
title?: string = this.title;
+ retrievalTitle: string = defaultTo(this.retrievalTitle, '');
+ retrievalDate: string = defaultTo(this.retrievalDate, '');
+ retrievalVersion: string = defaultTo(this.retrievalVersion, '');
+
+ datetimeInitiated: Date = defaultTo(this.datetimeInitiated, new Date());
+ datetimeUpdated: Date = defaultTo(this.datetimeUpdated, new Date());
+
+ readonly: boolean = defaultTo(this.readonly, false);
+
+ didInsertElement() {
+ if (this.datetimeUpdated !== undefined || this.datetimeInitiated !== undefined) {
+ const diffInMs = this.datetimeUpdated.getTime() - this.datetimeInitiated.getTime();
+ const diffInMinutes = diffInMs / 1000;
+ if (
+ (this.retrievalTitle === 'auto_retrieval' || this.retrievalTitle === 'dual_retrieval')
+ && diffInMinutes <= 1
+ ) {
+ this.set('value', this.title);
+ }
+
+ if (
+ (this.retrievalDate === 'auto_retrieval' || this.retrievalDate === 'dual_retrieval')
+ && diffInMinutes <= 1
+ ) {
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = String(now.getMonth() + 1).padStart(2, '0');
+ const date = String(now.getDate()).padStart(2, '0');
+
+ this.set('value', `${year}-${month}-${date}`);
+ }
+ } else {
+ // Invalid date values for datetimeInitiated or datetimeUpdated.
+ }
+
+ this.set('readonly', this.readonly === true);
+
+ if (this.retrievalVersion !== '') {
+ this.set('value', this.retrievalVersion);
+ }
+ }
@action
getTitle() {
@@ -25,10 +66,17 @@ export default class ValidatedText extends BaseValidatedComp
@action
getDate() {
- this.set(
- 'value',
- `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}-${String(new
- Date().getDate()).padStart(2, '0')}`,
- );
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = String(now.getMonth() + 1).padStart(2, '0');
+ const date = String(now.getDate()).padStart(2, '0');
+
+ this.set('value', `${year}-${month}-${date}`);
+ }
+
+ @action
+ onChange(event: Event) {
+ const target = event.target as HTMLInputElement;
+ this.set('value', target.value);
}
}
diff --git a/lib/osf-components/addon/components/validated-input/text/template.hbs b/lib/osf-components/addon/components/validated-input/text/template.hbs
index a5f3c442c..d150262ac 100644
--- a/lib/osf-components/addon/components/validated-input/text/template.hbs
+++ b/lib/osf-components/addon/components/validated-input/text/template.hbs
@@ -2,6 +2,9 @@
model=this.model
changeset=this.changeset
title=this.title
+ datetimeInitiated=this.datetimeInitiated
+ datetimeUpdated=this.datetimeUpdated
+ nodeId=this.nodeId
errors=this.errors
label=this.label
valuePath=this.valuePath
@@ -17,25 +20,25 @@
@class='form-control'
@name={{@valuePath}}
@keyUp={{@onKeyUp}}
- @change={{@onChange}}
- @disabled={{this.disabled}}
+ @change={{action this.onChange}}
+ @disabled={{this.getEdit}}
/>
- {{#if @autoTitle}}
+ {{#if (or (eq @retrievalTitle 'button_retrieval') (eq @retrievalTitle 'dual_retrieval'))}}
- {{t 'registries.drafts.draft.form.auto_button_label'}}
+ {{t 'registries.drafts.draft.form.get_retrieval_label'}}
{{/if}}
- {{#if @autoDate}}
+ {{#if (or (eq @retrievalDate 'button_retrieval') (eq @retrievalDate 'dual_retrieval'))}}
- {{t 'registries.drafts.draft.form.auto_button_label'}}
+ {{t 'registries.drafts.draft.form.get_retrieval_label'}}
{{/if}}
{{/validated-input/x-input-wrapper}}
diff --git a/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/component.js b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/component.js
new file mode 100644
index 000000000..804d31e86
--- /dev/null
+++ b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/component.js
@@ -0,0 +1,2 @@
+export { default } from
+ 'osf-components/components/registries/schema-block-renderer/editable/rdm/ad-metadata-input/component';
diff --git a/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.js b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.js
new file mode 100644
index 000000000..91d59780a
--- /dev/null
+++ b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component.js
@@ -0,0 +1,2 @@
+export { default } from
+ 'osf-components/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component';
diff --git a/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.js b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.js
new file mode 100644
index 000000000..354b25160
--- /dev/null
+++ b/lib/osf-components/app/components/registries/schema-block-renderer/editable/rdm/singleselect-pulldown-input/component.js
@@ -0,0 +1,2 @@
+export { default } from
+ 'osf-components/components/registries/schema-block-renderer/editable/rdm/single-select-pulldown-input/component';
diff --git a/lib/osf-components/app/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.js b/lib/osf-components/app/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.js
new file mode 100644
index 000000000..4bd11e191
--- /dev/null
+++ b/lib/osf-components/app/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component.js
@@ -0,0 +1,2 @@
+export { default } from
+ 'osf-components/components/registries/schema-block-renderer/read-only/rdm/ad-metadata-input/component';
diff --git a/lib/registries/addon/drafts/draft/-components/right-nav/component.ts b/lib/registries/addon/drafts/draft/-components/right-nav/component.ts
new file mode 100644
index 000000000..6c2693bce
--- /dev/null
+++ b/lib/registries/addon/drafts/draft/-components/right-nav/component.ts
@@ -0,0 +1,97 @@
+import { tagName } from '@ember-decorators/component';
+import Component from '@ember/component';
+import { action } from '@ember/object';
+import { inject as service } from '@ember/service';
+import DS from 'ember-data';
+import Intl from 'ember-intl/services/intl';
+import { layout } from 'ember-osf-web/decorators/component';
+import DraftRegistration from 'ember-osf-web/models/draft-registration';
+import Toast from 'ember-toastr/services/toast';
+import template from './template';
+
+@tagName('')
+@layout(template)
+export default class RightNav extends Component {
+ @service store!: DS.Store;
+ draftRegistrations: DraftRegistration[] = [];
+ @service toast!: Toast;
+
+ @service router!: any;
+ disableButtons: boolean = false;
+ @service intl!: Intl;
+
+ constructor(...args: any[]) {
+ super(...args);
+ this.handleRouteChange();
+ this.router.on('routeDidChange', this.handleRouteChange);
+ }
+
+ willDestroy() {
+ super.willDestroy();
+ this.router.off('routeDidChange', this.handleRouteChange);
+ }
+
+ @action
+ async handleRouteChange() {
+ const currentUrl = window.location.href;
+ const urlParts = currentUrl.split('/');
+ const lastPartWithQuery = urlParts[urlParts.length - 1];
+ const metadataTitle = lastPartWithQuery.split('?')[0];
+ const cleanedTitle = metadataTitle.replace(/^\d+-/, '');
+ const title = decodeURIComponent(cleanedTitle.replace(/-/g, ' '));
+ const allSchemas = await this.store.findAll('registration-schema');
+
+ const matchedSchema = allSchemas.find(
+ (schema: any) => schema.schema.pages.some(
+ (page: any) => page.title.trim().toLowerCase() === title.trim().toLowerCase()
+ && page.clipboardCopyPaste === false,
+ ),
+ );
+
+ if (!this.isDestroyed && !this.isDestroying) {
+ if (matchedSchema) {
+ this.set('disableButtons', true);
+ } else {
+ this.set('disableButtons', false);
+ }
+ }
+ }
+
+ @action
+ async pasteFromClipboard() {
+ try {
+ const clipboardText = await navigator.clipboard.readText();
+ try {
+ const parsedJson = JSON.parse(clipboardText);
+ const structuredJson: {[key: string]: any } = {};
+ Object.entries(parsedJson).forEach(([key, value]) => {
+ structuredJson[key] = {
+ extra: [],
+ value,
+ comments: [],
+ };
+ });
+
+ const currentUrl = window.location.href;
+ const idRegex = /\/drafts\/([a-f0-9]{24})/;
+ const match = currentUrl.match(idRegex);
+ if (match && match[1]) {
+ try {
+ const draftId = match[1];
+ const draftRegistration = await this.store.findRecord('draft-registration', draftId);
+ draftRegistration.set('registrationMetadata', structuredJson);
+ await draftRegistration.save();
+ window.location.reload();
+ this.toast.success(this.intl.t('registries.drafts.draft.form.clipboard_pasted'));
+ } catch (error) {
+ this.toast.success(this.intl.t('registries.drafts.draft.form.json_invalid'), error);
+ }
+ }
+ } catch (error) {
+ this.toast.success(this.intl.t('registries.drafts.draft.form.clipboard_unread'), error);
+ }
+ } catch (error) {
+ this.toast.error('Failed to read from clipboard: ', error);
+ }
+ }
+}
diff --git a/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss b/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss
index e69de29bb..a9a447dbc 100644
--- a/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss
+++ b/lib/registries/addon/drafts/draft/-components/right-nav/styles.scss
@@ -0,0 +1,11 @@
+.Label {
+ float: left;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: normal;
+ word-wrap: break-word;
+
+ &:hover {
+ overflow: visible;
+ }
+}
diff --git a/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs b/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs
index 0fc70271c..4a126874e 100644
--- a/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs
+++ b/lib/registries/addon/drafts/draft/-components/right-nav/template.hbs
@@ -60,4 +60,15 @@
-
\ No newline at end of file
+
+
+
+ {{t 'registries.drafts.draft.form.from_clipboard_paste'}}
+
\ No newline at end of file
diff --git a/mirage/config.ts b/mirage/config.ts
index 562608270..ef1fee050 100644
--- a/mirage/config.ts
+++ b/mirage/config.ts
@@ -22,6 +22,7 @@ import { summaryMetrics } from './views/institution';
import { iqbrimsStatus } from './views/iqbrims-status';
import { metadataNodeErad } from './views/metadata-node-erad';
import { metadataNodeProject } from './views/metadata-node-project';
+// import { workflowConfig } from './views/workflow-config';
import { createNode } from './views/node';
import { osfNestedResource, osfResource, osfToManyRelationship } from './views/osf-resource';
import { getProviderSubjects } from './views/provider-subjects';
@@ -263,6 +264,7 @@ export default function(this: Server) {
this.del('/project/:pid/binderhub/server_annotation/:aid', serverAnnotation.del);
this.get('/project/:id/metadata/erad/candidates', metadataNodeErad);
this.get('/project/:id/metadata/project', metadataNodeProject);
+ // this.get('/project/:id/workflow/config', workflowConfig);
this.urlPrefix = apiUrl;
this.namespace = apiNamespace;
diff --git a/mirage/factories/workflow-config.ts b/mirage/factories/workflow-config.ts
new file mode 100644
index 000000000..40a8fba22
--- /dev/null
+++ b/mirage/factories/workflow-config.ts
@@ -0,0 +1,12 @@
+import { Factory } from 'ember-cli-mirage';
+
+import WorkFlowConfigModel from 'ember-osf-web/models/iqbrims-status';
+
+export default Factory.extend({
+});
+
+declare module 'ember-cli-mirage/types/registries/schema' {
+ export default interface MirageSchemaRegistry {
+ workflowConfigs: WorkFlowConfigModel;
+ } // eslint-disable-line semi
+}
diff --git a/mirage/fixture-data/registration-schemas/prereg-challenge.ts b/mirage/fixture-data/registration-schemas/prereg-challenge.ts
index d42e7e2b6..a77ba2026 100644
--- a/mirage/fixture-data/registration-schemas/prereg-challenge.ts
+++ b/mirage/fixture-data/registration-schemas/prereg-challenge.ts
@@ -428,6 +428,7 @@ export default {
{
type: 'object',
id: 'page7',
+ clipboardCopyPaste: false,
questions: [
{
help: '',
diff --git a/public/workflow_connection.json b/public/workflow_connection.json
new file mode 100644
index 000000000..7fe59d415
--- /dev/null
+++ b/public/workflow_connection.json
@@ -0,0 +1,3 @@
+{
+ "one": "abc"
+}
\ No newline at end of file
diff --git a/tests/integration/components/node-navbar/component-test.ts b/tests/integration/components/node-navbar/component-test.ts
index 0bdeb3271..93c703c29 100644
--- a/tests/integration/components/node-navbar/component-test.ts
+++ b/tests/integration/components/node-navbar/component-test.ts
@@ -9,6 +9,7 @@ import { OsfLinkRouterStub } from '../../helpers/osf-link-router-stub';
enum NavCondition {
HasParent,
IQBRIMSEnabled,
+ WorkFlowEnabled,
BinderHubEnabled,
IsRegistration = 'isRegistration',
IsPublic = 'public',
@@ -23,6 +24,7 @@ enum NavLink {
ThisNode,
Files = 'files',
IQBRIMS = 'iqbrims',
+ WorkFlow = 'workflow',
BinderHub = 'binderhub',
Wiki = 'wiki',
Analytics = 'analytics',
@@ -51,12 +53,14 @@ export class FakeNode {
html: 'http://localhost:4200/fak3d',
};
+ [key: string]: any;
+
constructor(conditions: NavCondition[] = []) {
for (const condition of conditions) {
if (condition === NavCondition.HasParent) {
this.parentId = faker.random.uuid();
} else if (condition !== NavCondition.IQBRIMSEnabled
- && condition !== NavCondition.BinderHubEnabled) {
+ && condition !== NavCondition.BinderHubEnabled && condition !== NavCondition.WorkFlowEnabled) {
this[condition] = true;
}
}
@@ -278,6 +282,16 @@ module('Integration | Component | node-navbar', () => {
NavLink.BinderHub,
],
},
+ {
+ conditions: [
+ NavCondition.WorkFlowEnabled,
+ ],
+ links: [
+ NavLink.ThisNode,
+ NavLink.Files,
+ NavLink.WorkFlow,
+ ],
+ },
];
testCases.forEach((testCase, i) => {
@@ -290,9 +304,12 @@ module('Integration | Component | node-navbar', () => {
this.set('iqbrimsEnabled', iqbrimsEnabled.length > 0);
const binderhubEnabled = testCase.conditions.filter(c => c === NavCondition.BinderHubEnabled);
this.set('binderhubEnabled', binderhubEnabled.length > 0);
+ const workflowEnabled = testCase.conditions.filter(c => c === NavCondition.WorkFlowEnabled);
+ this.set('workflowEnabled', workflowEnabled.length > 0);
await render(
hbs`{{node-navbar node=this.node iqbrimsEnabled=this.iqbrimsEnabled
+ workflowEnabled=this.workflowEnabled
binderhubEnabled=this.binderhubEnabled renderInPlace=true}}`,
);
diff --git a/tests/integration/components/registries-side-nav/component-test.ts b/tests/integration/components/registries-side-nav/component-test.ts
index 2a59dbd68..88f5cb89a 100644
--- a/tests/integration/components/registries-side-nav/component-test.ts
+++ b/tests/integration/components/registries-side-nav/component-test.ts
@@ -21,6 +21,9 @@ class RouterStub extends Service {
}
class CurrentUserStub extends Service {
+ ajaxHeaders() {
+ return {};
+ }
}
/* tslint:disable:only-arrow-functions */
diff --git a/tests/integration/components/registries/schema-block-group-renderer/component-test.ts b/tests/integration/components/registries/schema-block-group-renderer/component-test.ts
index 3c655f981..ff29b2523 100644
--- a/tests/integration/components/registries/schema-block-group-renderer/component-test.ts
+++ b/tests/integration/components/registries/schema-block-group-renderer/component-test.ts
@@ -172,6 +172,8 @@ module('Integration | Component | schema-block-group-renderer', hooks => {
'page-one_single-select-two': '',
'page-one_multi-select': [],
'page-one_file-input': [testFile],
+ datetimeInitiated: new Date(),
+ datetimeUpdated: new Date(),
};
const registrationResponseChangeset = new Changeset(registrationResponse);
const node = await this.store.findRecord('node', mirageNode.id, {
diff --git a/translations/en-us.yml b/translations/en-us.yml
index 19d25ad2a..c37de3466 100644
--- a/translations/en-us.yml
+++ b/translations/en-us.yml
@@ -546,6 +546,7 @@ node_navbar:
toggle: 'Toggle navigation'
project_nav: 'Project Navigation'
iqbrims: 'IQB-RIMS'
+ workflow: 'Work Flow'
wiki: Wiki
analytics: Statistics
registrations: Registrations
@@ -1025,7 +1026,15 @@ registries:
last_saved: 'Auto-saved: '
save_failed: 'Save failed. Unsaved changes present.'
failed_auto_save: 'Failed to auto-save draft registration form'
- auto_button_label: 'Fill'
+ get_retrieval_label: 'Fill'
+ copy_to_clipboard: 'Copy To Clipboard'
+ from_clipboard_paste: 'From Clipboard Paste'
+ clipboard_copied: 'Clipboard copied!'
+ warning_noautosave: 'Not autosaved. Please try again later!'
+ clipboard_pasted: 'Clipboard pasted!'
+ clipboard_fail: 'Failed to save from clipboard: '
+ json_invalid: 'Clipboard does not contain valid JSON: '
+ clipboard_unread: 'Failed to read from clipboard: '
review:
title: 'Review registration before submitting'
page_label: Review
@@ -1768,6 +1777,57 @@ iqbrims:
has_checklist: 'Checklist'
files_comment: 'Comment'
uploader_comment: 'Comment'
+
+workflow:
+ page_title: '{nodeTitle} Workscreen'
+ header: 'Workscreen'
+ loading: 'Loading Workflow config...'
+ tab1: 'task'
+ tab2: 'process'
+ tab3: 'Workflow Management'
+ tab1_content: 'Running Tasks'
+ tab2_content: 'Running Process'
+ tab3_content: 'Workflow Management'
+ start_button: 'Start Workflow'
+ Available_Workflows: 'Available Workflows'
+ register_button: 'Register Workflow'
+ activate: 'valid'
+ deactivate: 'invalid'
+ edit: 'edit'
+ remove: 'remove'
+ workflow_registration: 'Workflow Registration'
+ workflow_edit: 'Worlflow Edit'
+ workflow_engine: 'Workflow Engine'
+ workflow_name: 'Workflow Name'
+ workflow_id: 'Workflow ID'
+ creatorToken: 'Settings for worklaw creator token disbursement'
+ adminToken: 'Settings for dispensing tokens for project managers'
+ executorToken: 'Settings for dispensing tokens for the workflow executor'
+ cancel_button: 'Cancel'
+ register_button2: 'Registration'
+ update_button: 'Update'
+ available workflow processes: 'Available Workflow Processes'
+ workflow2_name: 'Name'
+ workflow_processid: 'Workflow_ProcessID'
+ initiator: 'Initiator'
+ starting time: 'Starting Time'
+ completion time: 'Completion Time'
+ status of processing: 'Status of Processing'
+ execution state: 'Execution State'
+ operation: 'Operation'
+ workflowprocessinformation: 'Workflow Process Information'
+ completed tasks: 'Completed Task'
+ workflowprocess: 'Workflow Process: '
+ delprocess: 'Del Process'
+ running tasks: 'Running Tasks'
+ related files: 'Related Files'
+ stop_button: 'Delete_Process'
+ update_button2: 'Reload'
+ workflow_task_list: 'Workflow Task List'
+ request_targer: 'Request Target'
+ status: 'Status'
+ processid: 'ProcessID'
+
binderhub:
page_title: '{nodeTitle} BinderHub'
host_info:
@@ -1887,4 +1947,4 @@ metadata:
description: 'Select a destination.'
array-input:
add-item: 'Add item'
- remove-item: 'Remove item'
+ remove-item: 'Remove item'
\ No newline at end of file
diff --git a/translations/ja.yml b/translations/ja.yml
index 9a98c01ab..6d3bfb740 100644
--- a/translations/ja.yml
+++ b/translations/ja.yml
@@ -546,6 +546,7 @@ node_navbar:
toggle: ナビゲーションを切り替える
project_nav: プロジェクトナビゲーション
iqbrims: IQB-RIMS
+ workflow: ワークフロー
wiki: Wiki
analytics: 統計
registrations: 登録
@@ -641,7 +642,7 @@ node:
metadata:
new_report_modal:
title: メタデータ様式を選択
- info: 'デフォルトは「公的資金による研究データのメタデータ登録」です。- 「ムーンショット目標2未病データベース-メタデータ」は該当する人のみ選択してください
新規に作成したいプロジェクトメタデータの様式を以下から選択してください。- メタデータ作成では、様式で定義された各項目を入力することができます。
- メタデータ作成では、このプロジェクトに含まれるファイルのメタデータを登録することができます。
- メタデータ作成から報告書様式に準拠したファイルをダウンロードし、報告書等の提出に利用することができます。
'
+ info: 'デフォルトは「公的資金による研究データのメタデータ登録」です。
査読付き論文著者最終稿の登録も可能です。- 「ムーンショット目標2未病データベース-メタデータ」は該当する人のみ選択してください
新規に作成したいプロジェクトメタデータの様式を以下から選択してください。- メタデータ作成では、様式で定義された各項目を入力することができます。
- メタデータ作成では、このプロジェクトに含まれるファイルのメタデータを登録することができます。
- メタデータ作成から報告書様式に準拠したファイルをダウンロードし、報告書等の提出に利用することができます。
'
note: *今後、対応する事業や機関の増加に合わせて、メタデータの様式は随時追加されていきます。
create: メタデータを作成
page_title: '{nodeTitle} メタデータ'
@@ -1025,7 +1026,15 @@ registries:
last_saved: '自動保存済み: '
save_failed: '保存に失敗しました。保存できていない変更があります。'
failed_auto_save: '下書きの自動保存に失敗しました。'
- auto_button_label: '再取得'
+ get_retrieval_label: '自動取得'
+ copy_to_clipboard: 'クリップボードにコピー'
+ from_clipboard_paste: 'クリップボードから貼り付け'
+ clipboard_copied: 'クリップボードコピーされました!'
+ warning_noautosave: '自動保存されていません。少し時間をたってから、もう一度お試しください!'
+ clipboard_pasted: 'クリップボード貼り付けされました!'
+ clipboard_fail: 'クリップボードからの保存に失敗しました: '
+ json_invalid: 'クリップボードには有効な JSON が含まれていません: '
+ clipboard_unread: 'クリップボードからの読み取りに失敗しました: '
review:
title: 送信前に登録を確認する
page_label: 内容確認
@@ -1888,3 +1897,53 @@ metadata:
array-input:
add-item: 'アイテム追加'
remove-item: 'アイテム削除'
+
+workflow:
+ page_title: '{nodeTitle} ワークフロー'
+ header: 'ワークフロー'
+ loading: 'ロード中...'
+ tab1: 'タスク'
+ tab2: 'プロセス'
+ tab3: 'ワークフロー管理'
+ tab1_content: 'タスク一覧'
+ tab2_content: 'プロセス一覧'
+ tab3_content: 'ワークフロー管理'
+ start_button: 'ワークフローの開始'
+ Available_Workflows: '利用可能なワークフロー一覧'
+ register_button: 'ワークフローの登録'
+ activate: '有効'
+ deactivate: '無効'
+ edit: '情報の編集'
+ remove: '登録解除'
+ workflow_registration: 'ワークフローの登録'
+ workflow_edit: 'ワークフロー情報の編集'
+ workflow_engine: 'ワークフローエンジン'
+ workflow_name: 'ワークフロー名'
+ workflow_id: 'ワークフローID'
+ creatorToken: 'ワークフロー作成者のトークンの払い出しに関する設定'
+ adminToken: 'プロジェクト管理者のトークンの払い出しに関する設定'
+ executorToken: 'ワークフロー実行者のトークンの払い出しに関する設定'
+ cancel_button: 'キャンセル'
+ register_button2: '登録'
+ update_button: '更新'
+ available workflow processes: 'ワークフロープロセスの一覧'
+ workflow2_name: '名前'
+ workflow_processid: 'ワークフロープロセスID'
+ initiator: '開始者'
+ starting time: '開始時刻'
+ completion time: '完了時刻'
+ status of processing: '処理状況'
+ execution state: '実行状況'
+ operation: '操作'
+ workflowprocessinformation: 'ワークフロープロセスの情報'
+ related files: '関連ファイル'
+ completed tasks: '完了済タスク'
+ update_button2: '再読み込み'
+ workflow_task_list: 'ワークフロータスクの一覧'
+ request_targer: '依頼対象'
+ status: '状態'
+ processid: 'プロセスID'
+ stop_button: 'プロセス削除'
+ workflowprocess: 'ワークフロープロセス'
+ delprocess: 'プロセス削除'
+ running tasks: '実行中タスク'