Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import Path from 'path';

import React, { useState, Fragment, useEffect, useContext } from 'react';
import React, { useState, Fragment, useEffect, useContext, useRef } from 'react';
import formatMessage from 'format-message';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
Expand All @@ -26,6 +26,7 @@ interface FormData {

interface FormDataError {
name?: string;
location?: string;
}

interface Files {
Expand All @@ -36,20 +37,21 @@ interface Files {
interface DefineConversationProps {
onSubmit: (formData: FormData) => void;
onDismiss: () => void;
onCurrentPathUpdate?: (newPath?: string, storageId?: string) => void;
onCurrentPathUpdate: (newPath?: string, storageId?: string) => void;
onGetErrorMessage?: (text: string) => void;
focusedStorageFolder?: StorageFolder;
currentPath?: string;
focusedStorageFolder: StorageFolder;
files?: Files[];
}

const initialFormDataError: FormDataError = {};

export const DefineConversation: React.FC<DefineConversationProps> = props => {
const { onSubmit, onDismiss, onCurrentPathUpdate, focusedStorageFolder, currentPath, files } = props;
const { onSubmit, onDismiss, onCurrentPathUpdate, focusedStorageFolder, files } = props;
const { state } = useContext(StoreContext);
const { templateId } = state;

const { templateId, storages } = state;
const currentPath = Path.join(focusedStorageFolder.parent, focusedStorageFolder.name);
const currentStorageIndex = useRef(0);
const platform = storages[currentStorageIndex.current].platform;
const getDefaultName = () => {
let i = -1;
const bot = templateId;
Expand Down Expand Up @@ -81,8 +83,10 @@ export const DefineConversation: React.FC<DefineConversationProps> = props => {
const nameRegex = /^[a-zA-Z0-9-_.]+$/;
const validateForm = (data: FormData) => {
const errors: FormDataError = {};
const { name } = data;

const { name, location } = data;
if (location === '/' && platform === 'win32') {
errors.location = formatMessage('Cannot save bot here');
}
if (!name || !nameRegex.test(name)) {
errors.name = formatMessage(
'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
Expand Down Expand Up @@ -157,13 +161,7 @@ export const DefineConversation: React.FC<DefineConversationProps> = props => {
/>
</StackItem>
</Stack>
{focusedStorageFolder && onCurrentPathUpdate && currentPath && (
<LocationSelectContent
onCurrentPathUpdate={onCurrentPathUpdate}
focusedStorageFolder={focusedStorageFolder}
currentPath={currentPath}
/>
)}
<LocationSelectContent onCurrentPathUpdate={onCurrentPathUpdate} focusedStorageFolder={focusedStorageFolder} />

<DialogFooter>
<DefaultButton onClick={onDismiss} text={formatMessage('Cancel')} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,24 @@ import { dropdown, loading, detailListContainer, detailListClass, fileSelectorCo

interface FileSelectorProps {
focusedStorageFolder: StorageFolder;
currentPath: string;
onCurrentPathUpdate: (newPath?: string, storageId?: string) => void;
onSelectionChanged: (file: any) => void;
checkShowItem: (file: File) => boolean;
storageFileLoadingStatus: string;
platform: string;
}

export const FileSelector: React.FC<FileSelectorProps> = props => {
const {
onSelectionChanged,
focusedStorageFolder,
checkShowItem,
currentPath,
onCurrentPathUpdate,
storageFileLoadingStatus,
platform,
} = props;
// for detail file list in open panel
const currentPath = path.join(focusedStorageFolder.parent, focusedStorageFolder.name);
const tableColums = [
{
key: 'column1',
Expand Down Expand Up @@ -127,7 +128,7 @@ export const FileSelector: React.FC<FileSelectorProps> = props => {
isPadded: true,
},
];

const diskRootPattern = /[a-zA-Z]:\/$/;
const storageFiles = useMemo(() => {
if (!focusedStorageFolder.children) return [];
const files = focusedStorageFolder.children.reduce((result, file) => {
Expand All @@ -146,12 +147,13 @@ export const FileSelector: React.FC<FileSelectorProps> = props => {
return result;
}, [] as any[]);
// add parent folder
const p = path.join(focusedStorageFolder.parent, focusedStorageFolder.name);
files.unshift({
name: '..',
value: '..',
fileType: 'folder',
iconName: 'folder',
filePath: focusedStorageFolder.parent,
filePath: diskRootPattern.test(p) || p === '/' ? '/' : focusedStorageFolder.parent,
});
return files;
}, [focusedStorageFolder]);
Expand Down Expand Up @@ -185,24 +187,29 @@ export const FileSelector: React.FC<FileSelectorProps> = props => {

const separator = path.sep;
const pathItems = currentPath.split(separator).filter(p => p !== '');
const breadcrumbItems = pathItems.map((item, index) => {
let itemPath = getNavItemPath(pathItems, separator, 0, index);
// put a leading / back on the path if it started as a unix style path
itemPath = currentPath.startsWith('/') ? `/${itemPath}` : itemPath;
// add a trailing / if the last path is something like c:
itemPath = itemPath[itemPath.length - 1] === ':' ? `${itemPath}/` : itemPath;
const displayText = itemPath.startsWith('/') ? itemPath : `/${itemPath}`;
return {
text: displayText, // displayed text
key: itemPath, // value returned
title: item, // title shown on hover
};
});

const breadcrumbItems = pathItems
.map((item, index) => {
let itemPath = getNavItemPath(pathItems, separator, 0, index);

// put a leading / back on the path if it started as a unix style path
itemPath = currentPath.startsWith('/') ? `/${itemPath}` : itemPath;
// add a trailing / if the last path is something like c:
itemPath = itemPath[itemPath.length - 1] === ':' ? `${itemPath}/` : itemPath;

return {
text: itemPath, // displayed text
key: itemPath, // value returned
title: item, // title shown on hover
};
})
.reverse();
if (platform === 'win32') {
breadcrumbItems.splice(0, 0, {
text: '/', // displayed text
key: '/', // value returned
title: '/', // title shown on hover
});
}

breadcrumbItems.reverse();
const updateLocation = (e, item?: IDropdownOption) => {
onCurrentPathUpdate(item ? (item.key as string) : '');
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

/** @jsx jsx */

import { jsx } from '@emotion/core';
import { Fragment, useContext, useRef } from 'react';

Expand All @@ -15,16 +16,13 @@ interface LocationSelectContentProps {
onOpen?: (path: string, storage: string) => void;
focusedStorageFolder: StorageFolder;
onCurrentPathUpdate: (newPath?: string, storageId?: string) => void;
currentPath: string;
}

export const LocationSelectContent: React.FC<LocationSelectContentProps> = props => {
const { onOpen, focusedStorageFolder, onCurrentPathUpdate, currentPath } = props;
const { onOpen, focusedStorageFolder, onCurrentPathUpdate } = props;
const { state } = useContext(StoreContext);
const { storages, storageFileLoadingStatus, creationFlowStatus } = state;

const currentStorageIndex = useRef(0);

const onSelectionChanged = item => {
if (item) {
const type = item.fileType;
Expand All @@ -50,10 +48,10 @@ export const LocationSelectContent: React.FC<LocationSelectContentProps> = props
<FileSelector
storageFileLoadingStatus={storageFileLoadingStatus}
checkShowItem={checkShowItem}
currentPath={currentPath}
focusedStorageFolder={focusedStorageFolder}
onCurrentPathUpdate={onCurrentPathUpdate}
onSelectionChanged={onSelectionChanged}
platform={storages[currentStorageIndex.current].platform}
/>
</Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,17 @@ interface OpenProjectProps {
onOpen: (path: string, storage: string) => void;
focusedStorageFolder: StorageFolder;
onCurrentPathUpdate: (newPath?: string, storageId?: string) => void;
currentPath: string;
onDismiss: () => void;
}

export const OpenProject: React.FC<OpenProjectProps> = props => {
const { onOpen, onDismiss, focusedStorageFolder, currentPath, onCurrentPathUpdate } = props;
const { onOpen, onDismiss, focusedStorageFolder, onCurrentPathUpdate } = props;

return (
<div data-testid="SelectLocation">
<LocationSelectContent
onOpen={onOpen}
focusedStorageFolder={focusedStorageFolder}
currentPath={currentPath}
onCurrentPathUpdate={onCurrentPathUpdate}
/>
<DialogFooter>
Expand Down
6 changes: 0 additions & 6 deletions Composer/packages/client/src/CreationFlow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export function CreationFlow(props) {
const currentStorageIndex = useRef(0);
const storage = storages[currentStorageIndex.current];
const currentStorageId = storage ? storage.id : 'default';
const [currentPath, setCurrentPath] = useState('');
useEffect(() => {
if (storages && storages.length) {
const storageId = storage.id;
Expand All @@ -48,9 +47,6 @@ export function CreationFlow(props) {
const allFilesInFolder = get(focusedStorageFolder, 'children', []);

setFiles(allFilesInFolder);
if (Object.keys(focusedStorageFolder).length) {
setCurrentPath(Path.join(focusedStorageFolder.parent, focusedStorageFolder.name));
}
}, [focusedStorageFolder]);

useEffect(() => {
Expand Down Expand Up @@ -147,7 +143,6 @@ export function CreationFlow(props) {
onOpen={openBot}
onDismiss={handleDismiss}
focusedStorageFolder={focusedStorageFolder}
currentPath={currentPath}
onCurrentPathUpdate={updateCurrentPath}
/>
),
Expand All @@ -160,7 +155,6 @@ export function CreationFlow(props) {
onDismiss={handleDismiss}
onCurrentPathUpdate={updateCurrentPath}
focusedStorageFolder={focusedStorageFolder}
currentPath={currentPath}
files={files}
/>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ interface CreateDialogModalProps {
onDismiss: () => void;
onCurrentPathUpdate?: (newPath?: string, storageId?: string) => void;
focusedStorageFolder?: StorageFolder;
currentPath?: string;
isOpen: boolean;
}

Expand Down
14 changes: 14 additions & 0 deletions Composer/packages/server/src/services/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Path } from '../utility/path';
import { StorageConnection, IFileStorage } from '../models/storage/interface';
import { StorageFactory } from '../models/storage/storageFactory';
import { Store } from '../store/store';
import settings from '../settings';

import { UserIdentity } from './pluginLoader';

Expand Down Expand Up @@ -70,6 +71,19 @@ class StorageService {
// return connent for file
// return children for dir
public getBlob = async (storageId: string, filePath: string, user?: UserIdentity) => {
if (filePath === '/' && settings.platform === 'win32') {
return {
name: '',
parent: '/',
children: settings.diskNames.map(d => {
return {
name: d,
type: 'folder',
path: d,
};
}),
};
}
const storageClient = this.getStorageClient(storageId, user);
const stat = await storageClient.stat(filePath);
if (stat.isFile) {
Expand Down
20 changes: 20 additions & 0 deletions Composer/packages/server/src/settings/env.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import os from 'os';
import childProcess from 'child_process';

import { Path } from '../utility/path';

Expand All @@ -13,6 +15,24 @@ if (folder && folder.endsWith(':')) {
folder = folder + '/';
}

let names: string[] = [];
const getDiskNames = text => {
names = text
.split('\r\r\n')
.filter(token => token.indexOf(':') > -1)
.map(token => token.trim().replace(/\\/g, '/'));
return names;
};
if (os.platform() === 'win32') {
try {
const stdout = childProcess.execSync(`wmic volume get name`).toString();
names = getDiskNames(stdout);
} catch (err) {
console.log(err);
}
}
export const diskNames = names;
export const platform = os.platform();
export const environment = process.env.NODE_ENV || 'development';
export const botsFolder = folder;
export const botEndpoint = process.env.BOT_ENDPOINT || 'http://localhost:3979';
Expand Down
15 changes: 14 additions & 1 deletion Composer/packages/server/src/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ import merge from 'lodash/merge';
import log from '../logger';
import { Path } from '../utility/path';

import { botsFolder, botEndpoint, appDataPath, environment, runtimeFolder, runtimeFrameworkVersion } from './env';
import {
botsFolder,
botEndpoint,
appDataPath,
environment,
runtimeFolder,
runtimeFrameworkVersion,
platform,
diskNames,
} from './env';

interface Settings {
botAdminEndpoint: string;
Expand All @@ -18,6 +27,8 @@ interface Settings {
runtimeFrameworkVersion: string;
botsFolder: string;
appDataPath: string;
platform: string;
diskNames: string[];
}

const envSettings: { [env: string]: Settings } = {
Expand All @@ -29,6 +40,8 @@ const envSettings: { [env: string]: Settings } = {
runtimeFolder,
runtimeFrameworkVersion,
appDataPath,
platform,
diskNames,
},
};

Expand Down
5 changes: 5 additions & 0 deletions Composer/packages/server/src/store/migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const migrations: Migration[] = [
condition: data => !data.version || data.version != initData.version,
run: data => initData,
},
{
name: 'Platform update',
condition: data => get(data, 'storageConnections.0.platform') !== settings.platform,
run: data => set(data, 'storageConnections[0].platform', settings.platform),
},
];

export function runMigrations(initialData: any): any {
Expand Down