diff --git a/cs3api4lab/api/cs3_file_api.py b/cs3api4lab/api/cs3_file_api.py index 34081a02..fc5f4cde 100644 --- a/cs3api4lab/api/cs3_file_api.py +++ b/cs3api4lab/api/cs3_file_api.py @@ -16,7 +16,7 @@ import cs3.storage.provider.v1beta1.provider_api_pb2 as cs3sp from google.protobuf.json_format import MessageToDict -from cs3api4lab.exception.exceptions import ResourceNotFoundError, FileLockedError +from cs3api4lab.exception.exceptions import ResourceNotFoundError from cs3api4lab.utils.file_utils import FileUtils from cs3api4lab.api.storage_api import StorageApi @@ -268,3 +268,4 @@ def _handle_error(self, response): self.log.error(response) raise Exception("Incorrect server response: " + response.status.message) + diff --git a/cs3api4lab/api/cs3apismanager.py b/cs3api4lab/api/cs3apismanager.py index b3c32f24..07f5c87c 100644 --- a/cs3api4lab/api/cs3apismanager.py +++ b/cs3api4lab/api/cs3apismanager.py @@ -126,7 +126,7 @@ def get(self, path, content=True, type=None, format=None): elif type == 'notebook' or (type is None and path.endswith('.ipynb')): try: #this needs to be fixed/refactored in a separate issue model = self._notebook_model(path, content=content) - except Exception: + except Exception as e: self.log.info("Notebook does not exist %s", path) else: if path.endswith('.ipynb'): diff --git a/cs3api4lab/api/lock_manager.py b/cs3api4lab/api/lock_manager.py deleted file mode 100644 index e69de29b..00000000 diff --git a/cs3api4lab/locks/base.py b/cs3api4lab/locks/base.py index 0ada2739..d9b1377e 100644 --- a/cs3api4lab/locks/base.py +++ b/cs3api4lab/locks/base.py @@ -38,12 +38,9 @@ def get_current_user(self): metadata=[('x-access-token', self.auth.authenticate())]) return self.user.user - def resolve_file_path(self, stat): - if self.is_valid_external_lock(stat): - file_name = stat['filepath'].split('/')[-1] - file_dir = '/'.join(stat['filepath'].split('/')[0:-1]) - return self._resolve_directory(file_dir, self.config.endpoint) + self._get_conflict_filename(file_name) - return stat['filepath'] + def resolve_file_path(self, path): + file_name = path.split('/')[-1] + return self._get_conflict_filename(file_name) @abstractmethod def is_valid_external_lock(self, stat): diff --git a/cs3api4lab/tests/test_cs3apismanager.py b/cs3api4lab/tests/test_cs3apismanager.py index 57481102..49de4f01 100644 --- a/cs3api4lab/tests/test_cs3apismanager.py +++ b/cs3api4lab/tests/test_cs3apismanager.py @@ -225,42 +225,8 @@ def test_is_editor(self): finally: try: self.contents_manager.delete_file(file_path) - except Exception: - pass - - def test_is_editor_shared_as_viewer(self): - self.content = "Lorem ipsum dolor sit amet..." - file_path = '/home/test_is_editor_file.txt' - remote_path = '/reva/richard/test_is_editor_file.txt' - einstein_id = '4c510ada-c86b-4815-8820-42cdf82c3d51' - einstein_idp = 'cernbox.cern.ch' - try: - richards_share = self.create_share('richard', einstein_id, einstein_idp, file_path, 'viewer') - self.share_id = richards_share['opaque_id'] - stat = self.file_api.stat_info(remote_path, self.config.endpoint) - result = self.contents_manager._is_editor(stat) - self.assertEqual(result, False, 'Is editor should be false') - finally: - try: - self.remove_test_share('richard', self.share_id) - self.remove_test_file('richard', file_path) - except Exception: - pass #we don't need any actions here - - - def test_is_editor(self): - file_path = '/home/test_is_editor_file.txt' - message = "Lorem ipsum dolor sit amet..." - try: - self.file_api.write_file(file_path, message, self.endpoint) - stat = self.file_api.stat_info(file_path, self.config.endpoint) - result = self.contents_manager._is_editor(stat) - self.assertEqual(result, True, 'Incorrect check if file is editor') - finally: - try: - self.contents_manager.delete_file(file_path) - except Exception: - pass + except Exception as e: + self.log.warn("Cannot remove %s:%s" % (file_path, e)) def test_is_editor_shared_as_viewer(self): self.content = "Lorem ipsum dolor sit amet..." @@ -281,7 +247,6 @@ def test_is_editor_shared_as_viewer(self): except Exception: pass #we don't need any actions here - def test_delete_non_exits_file(self): file_path = "/test_delete_non_exits_file.txt" with self.assertRaises(web.HTTPError): diff --git a/package.json b/package.json index 3b535208..3eaaf37d 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "typescript": "~4.1.6" }, "sideEffects": [ - "style/*.css", + "style/**/*.css", "style/index.js" ], "jupyterlab": { diff --git a/src/cs3panel.tsx b/src/cs3panel.tsx index 79fcb47f..d250e231 100644 --- a/src/cs3panel.tsx +++ b/src/cs3panel.tsx @@ -134,15 +134,17 @@ export class Cs3HeaderWidget extends ReactWidget { } export const Bottom = (props: BottomProps): JSX.Element => { - const [text, setText] = useState(''); + // const [text, setText] = useState(''); + const [hiddenFiles, setHiddenFiles] = useState(0); + const [action, setAction] = useState('show'); const setLabel = async (): Promise => { const showHidden: boolean = (await props.db.fetch('showHidden')) as boolean; - const hiddenFilesNo: number = (await props.db.fetch( - 'hiddenFilesNo' - )) as number; + const hiddenFilesNo: number = + ((await props.db.fetch('hiddenFilesNo')) as number) || 0; const action = showHidden === undefined || !showHidden ? 'show' : 'hide'; - setText(`${hiddenFilesNo} hidden files (${action})`); + setHiddenFiles(hiddenFilesNo); + setAction(action); }; props.browser.model.pathChanged.connect(async () => { @@ -153,7 +155,13 @@ export const Bottom = (props: BottomProps): JSX.Element => { await setLabel(); }); - return
{text}
; + return ( +
+
+ {hiddenFiles} hidden files ({action}) +
+
+ ); }; export class Cs3BottomWidget extends ReactWidget { @@ -170,7 +178,8 @@ export class Cs3BottomWidget extends ReactWidget { browser: FileBrowser ) { super(options); - this.addClass('c3-bottom-widget'); + // this.addClass('c3-bottom-widget'); + // this.addClass('p-Widget'); this.id = id; this.title.closable = false; this.bottomProps = { db: stateDB, browser: browser }; @@ -189,13 +198,17 @@ export class Cs3BottomWidget extends ReactWidget { } export class Cs3TabWidget extends ReactWidget { - constructor(title: string, icon: LabIcon, options: Widget.IOptions = {}) { + constructor(title: string, icon?: LabIcon, options: Widget.IOptions = {}) { super(options); this.addClass('c3-tab-widget'); this.title.label = title; this.title.caption = title; - this.title.icon = icon; + this.title.className = 'jp-main-tab'; + this.title.iconClass = 'jp-main-tab-icon'; + if (icon) { + this.title.icon = icon; + } } protected render(): JSX.Element { diff --git a/src/icons.ts b/src/icons.ts index 0be23ef6..d867d3b4 100644 --- a/src/icons.ts +++ b/src/icons.ts @@ -1,5 +1,7 @@ import accept from '../style/icons/accept.svg'; import decline from '../style/icons/decline.svg'; +import share from '../style/icons/share.svg'; +import info from '../style/icons/info.svg'; import { LabIcon } from '@jupyterlab/ui-components'; export const acceptIcon = new LabIcon({ @@ -11,3 +13,13 @@ export const declineIcon = new LabIcon({ name: 'cs3api4lab:decline', svgstr: decline }); + +export const shareIcon = new LabIcon({ + name: 'cs3api4lab:share', + svgstr: share +}); + +export const infoIcon = new LabIcon({ + name: 'cs3api4lab:info', + svgstr: info +}); diff --git a/src/index.ts b/src/index.ts index b31713e6..7de75d21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,19 +37,13 @@ import { import { PendingSharesListWrapper } from './pendingShares'; import { addHomeDirButton, addLaunchersButton } from './utils'; import { AccordionPanel } from '@lumino/widgets'; -import { - kernelIcon, - caseSensitiveIcon, - inspectorIcon, - newFolderIcon, - circleIcon -} from '@jupyterlab/ui-components'; +import { kernelIcon } from '@jupyterlab/ui-components'; import { Contents } from '@jupyterlab/services'; import { addCommands, createLauncher, restoreBrowser } from './browserCommands'; import { IMainMenu } from '@jupyterlab/mainmenu'; import { ITranslator } from '@jupyterlab/translation'; import { requestAPI } from './services'; -// import { cs3AccordionChild } from './cs3Accordion'; +import { infoIcon, shareIcon } from './icons'; /** * The command IDs used by the react-widget plugin. @@ -179,7 +173,8 @@ const cs3share: JupyterFrontEndPlugin = { dialogTracker.add(dialog); } }, - iconClass: () => 'jp-MaterialIcon jp-FileUploadIcon', + // iconClass: () => 'jp-MaterialIcon jp-FileUploadIcon', + icon: infoIcon, label: () => { return 'File info'; } @@ -205,7 +200,8 @@ const cs3share: JupyterFrontEndPlugin = { }); } }, - iconClass: () => 'jp-MaterialIcon jp-FileUploadIcon', + // iconClass: () => 'jp-MaterialIcon jp-FileUploadIcon', + icon: shareIcon, label: () => { return 'Share file'; } @@ -276,12 +272,14 @@ const cs3browser: JupyterFrontEndPlugin = { // const fileBrowser = factory.defaultBrowser factory.defaultBrowser.title.label = 'My Files'; factory.defaultBrowser.title.caption = 'My Files'; - factory.defaultBrowser.title.icon = caseSensitiveIcon; + factory.defaultBrowser.title.className = 'jp-main-tab'; + factory.defaultBrowser.title.iconClass = 'jp-main-tab-icon'; + // factory.defaultBrowser.title.icon = caseSensitiveIcon; factory.defaultBrowser.toolbar.addItem( 'Share files', new ToolbarButton({ - icon: circleIcon, + icon: shareIcon, tooltip: 'Share files', onClick: () => { const selectedFileList: Contents.IModel[] = toArray( @@ -312,7 +310,9 @@ const cs3browser: JupyterFrontEndPlugin = { const cs3BottomWidget: ReactWidget = new Cs3BottomWidget( 'cs3Api Bottom', 'cs3-bottom-widget', - {}, + { + // he + }, stateDB, factory.defaultBrowser ); @@ -363,8 +363,8 @@ const cs3browser: JupyterFrontEndPlugin = { // Projects tab // const cs3TabWidget3: ReactWidget = new Cs3TabWidget( - 'Projects', - newFolderIcon + 'Projects' + // newFolderIcon ); /** @@ -448,7 +448,8 @@ const cs3browser: JupyterFrontEndPlugin = { cs3Accordion.id = 'sharesPanel'; cs3Accordion.title.caption = 'Shares'; cs3Accordion.title.label = 'Shares'; - cs3Accordion.title.icon = inspectorIcon; + cs3Accordion.title.iconClass = 'jp-main-tab-icon'; + cs3Accordion.title.className = 'jp-main-tab'; cs3Accordion.hide(); const pendingShares = new PendingSharesListWrapper(); diff --git a/style/base.css b/style/base.css index 05c274b0..7e1a1d28 100644 --- a/style/base.css +++ b/style/base.css @@ -2,11 +2,18 @@ { display: flex; } +jp-bottom-hidden-files { + display: flex; +} .jp-bottom-div { + display: block; text-align: right; - margin-bottom: 5px; + width: auto; + height: 100%; + margin-right: 10px; + margin-top: 5px; } .jp-file-info @@ -200,17 +207,30 @@ .c3-panel-header { - height: 75px !important; + /*height: 75px !important;*/ +} + +#cs3-bottom-widget { + box-sizing: border-box; } .c3-panel-bottom { - height: 25px !important; + display: flex; + align-content: center; + text-align: right; + background-color: var(--jp-layout-color2); + border-top: 1px solid var(--jp-border-color1); } .c3-panel-bottom > div { - padding-left: 10px; + font-family: var(--jp-ui-font-family); + color: var(--jp-ui-font-color2); +} + +#cs3-header-widget { + background-color: var(--jp-layout-color0); } #cs3-dock-panel-header h1 @@ -310,4 +330,47 @@ content: var(--jp-icon-caret-up); font-weight: bold; color: var(--jp-ui-font-color0); +} + +#cs3-panel .m-TabBar { + border-bottom: 1px solid var(--jp-layout-color3) !important; +} + +.jp-main-tab { + /*background-color: var(--jp-layout-color3);*/ + border: none !important; + + border-bottom: 1px solid var(--jp-layout-color3) !important; + background-color: var(--jp-layout-color0) !important; +} +#cs3-dock-panel-main .lm-TabBar-tab.lm-mod-current { + + transform: none !important; + /*border-bottom: 1px solid var(--jp-layout-color3) !important;*/ +} + +#cs3-dock-panel-main .lm-TabBar { + border: none !important; + /*border-bottom: 1px solid var(--jp-layout-color3) !important;*/ +} + +.jp-main-tab.lm-mod-current > div { + background-color: var(--jp-layout-color3) !important; + color: var(--jp-ui-font-color0) !important; + /*border-bottom: 1px solid var(--jp-layout-color3) !important;*/ +} + +.jp-main-tab > div { + background-color: var(--jp-layout-color0) !important; + color: var(--jp-ui-font-color0) !important; + text-align: center; + text-transform: capitalize; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} +.jp-main-tab-icon { + display: none; +} +#cs3-panel { + background-color: var(--jp-layout-color0); } \ No newline at end of file diff --git a/style/icons/info.svg b/style/icons/info.svg new file mode 100644 index 00000000..7e3566fd --- /dev/null +++ b/style/icons/info.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/style/icons/share.svg b/style/icons/share.svg new file mode 100644 index 00000000..be712bb4 --- /dev/null +++ b/style/icons/share.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file