From afea59d3195ea1b1fbf0c64f7f35a3467626edf6 Mon Sep 17 00:00:00 2001 From: Devrim Date: Tue, 7 Feb 2023 17:41:55 +0300 Subject: [PATCH] fix: jans-cli-tui working branch 6 (#3794) * feat:jans-cli-tui path finder widget * fix: jans-cli-tui SSA org_id, status and software_roles (#3765) * fix: jans-cli-tui cli post params, mime-type and data types * fix: jans-cli-tui fix saving scripts (ref: #3793) * fix: jans-cli-tui adding scope to new client * fix: jans-client-tui client deletion (ref: #3796) * fix: jans-client-tui uncomment * fix: jans-cli-tui display SSA base64 token (ref: #3797) * fix: jans-cli-tui rename path finder for future use * fix: jans-cli-tui display file save successfull message --------- Co-authored-by: AbdelwahabAdam --- jans-cli-tui/cli_tui/cli/config_cli.py | 129 +++-- jans-cli-tui/cli_tui/jans_cli_tui.py | 107 ++++- .../010_auth_server/.#edit_client_dialog.py | 1 - .../010_auth_server/edit_client_dialog.py | 6 +- .../cli_tui/plugins/010_auth_server/ssa.py | 10 +- .../plugins/060_scripts/edit_script_dialog.py | 22 +- .../cli_tui/wui_components/jans_cli_dialog.py | 2 +- .../wui_components/jans_path_finder.py.txt | 451 ++++++++++++++++++ 8 files changed, 654 insertions(+), 74 deletions(-) delete mode 120000 jans-cli-tui/cli_tui/plugins/010_auth_server/.#edit_client_dialog.py create mode 100644 jans-cli-tui/cli_tui/wui_components/jans_path_finder.py.txt diff --git a/jans-cli-tui/cli_tui/cli/config_cli.py b/jans-cli-tui/cli_tui/cli/config_cli.py index f2f8a3bc544..7137aa997f8 100755 --- a/jans-cli-tui/cli_tui/cli/config_cli.py +++ b/jans-cli-tui/cli_tui/cli/config_cli.py @@ -1060,27 +1060,31 @@ def get_scope_for_endpoint(self, endpoint): return ' '.join(scope) - def get_requests(self, endpoint, params={}): + def get_requests(self, endpoint, params=None): if not self.wrapped: sys.stderr.write("Please wait while retrieving data ...\n") security = self.get_scope_for_endpoint(endpoint) self.get_access_token(security) + headers=self.get_request_header({'Accept': 'application/json'}) url_param_name = self.get_url_param(endpoint.path) - url = 'https://{}{}'.format(self.host, endpoint.path) + if params and url_param_name in params: url = url.format(**{url_param_name: params.pop(url_param_name)}) - response = requests.get( - url = url, - headers=self.get_request_header({'Accept': 'application/json'}), - params=params, - verify=self.verify_ssl, - cert=self.mtls_client_cert - ) + get_params = { + 'url': url, + 'headers': headers, + 'verify': self.verify_ssl, + 'cert': self.mtls_client_cert, + } + + if params: + get_params['params'] = params + response = requests.get(**get_params) self.log_response(response) if self.wrapped: @@ -1108,22 +1112,29 @@ def get_mime_for_endpoint(self, endpoint, req='requestBody'): return key - def post_requests(self, endpoint, data): + def post_requests(self, endpoint, data, params=None): url = 'https://{}{}'.format(self.host, endpoint.path) security = self.get_scope_for_endpoint(endpoint) self.get_access_token(security) mime_type = self.get_mime_for_endpoint(endpoint) - headers = self.get_request_header({'Accept': 'application/json', 'Content-Type': mime_type}) - if mime_type: - headers['Content-Type'] = mime_type - response = requests.post(url, - headers=headers, - json=data, - verify=self.verify_ssl, - cert=self.mtls_client_cert - ) + post_params = { + 'url': url, + 'headers': headers, + 'verify': self.verify_ssl, + 'cert': self.mtls_client_cert, + } + + if params: + post_params['params'] = params + + if mime_type.endswith(('json', 'text')): + post_params['json'] = data + else: + post_params['data'] = data + + response = requests.post(**post_params) self.log_response(response) @@ -1144,7 +1155,8 @@ def delete_requests(self, endpoint, url_param_dict): if url_params: url_path = endpoint.path.format(**url_param_dict) for param in url_params: - del url_param_dict[param] + if param in url_param_dict: + del url_param_dict[param] else: url_path = endpoint.path @@ -1170,28 +1182,34 @@ def patch_requests(self, endpoint, url_param_dict, data): url = 'https://{}{}'.format(self.host, endpoint.path.format(**url_param_dict)) security = self.get_scope_for_endpoint(endpoint) self.get_access_token(security) + mime_type = self.get_mime_for_endpoint(endpoint) + headers = self.get_request_header({'Accept': 'application/json', 'Content-Type': mime_type}) - content_key = 'application/json-patch+json' - for content_key in endpoint.info.get('requestBody', {}).get('content', {}): - break + patch_params = { + 'url': url, + 'headers': headers, + 'verify': self.verify_ssl, + 'cert': self.mtls_client_cert, + } - headers = self.get_request_header({'Accept': 'application/json', 'Content-Type': content_key}) - data = data - response = requests.patch( - url=url, - headers=headers, - json=data, - verify=self.verify_ssl, - cert=self.mtls_client_cert - ) + if url_param_dict: + patch_params['params'] = patch_params + + if mime_type.endswith(('json', 'text')): + patch_params['json'] = data + else: + patch_params['data'] = data + + response = requests.patch(**patch_params) self.log_response(response) + try: return response.json() except: self.print_exception(response.text) - def put_requests(self, endpoint, data, params={}): + def put_requests(self, endpoint, data, params=None): security = self.get_scope_for_endpoint(endpoint) self.get_access_token(security) @@ -1204,13 +1222,25 @@ def put_requests(self, endpoint, data, params={}): if params and url_param_name in params: url = url.format(**{url_param_name: params.pop(url_param_name)}) - response = requests.put( - url=url, - headers=self.get_request_header({'Accept': mime_type}), - json=data, - verify=self.verify_ssl, - cert=self.mtls_client_cert - ) + headers = self.get_request_header({'Accept': 'application/json', 'Content-Type': mime_type}) + + put_params = { + 'url': url, + 'headers': headers, + 'verify': self.verify_ssl, + 'cert': self.mtls_client_cert, + } + + + if mime_type.endswith(('json', 'text')): + put_params['json'] = data + else: + put_params['data'] = data + + if params: + put_params['params'] = params + + response = requests.put(**put_params) self.log_response(response) @@ -1371,11 +1401,21 @@ def print_response(self, response): sys.stderr.write("Server Response:\n") self.pretty_print(response) + + def read_binary_file(self, fn): + with open(fn, 'rb') as f: + return f.read() + + def process_command_post(self, path, suffix_param, endpoint_params, data_fn, data): # TODO: suffix_param, endpoint_params endpoint = self.get_fake_endpoint(path) + mime_type = self.get_mime_for_endpoint(endpoint) + params = {} + params.update(suffix_param) + params.update(endpoint_params) if not data and data_fn: @@ -1385,15 +1425,16 @@ def process_command_post(self, path, suffix_param, endpoint_params, data_fn, dat options={"verify_signature": False, "verify_exp": False, "verify_aud": False}) else: try: - data = self.get_json_from_file(data_fn) + if mime_type.endswith(('json', 'text')): + data = self.get_json_from_file(data_fn) + else: + data = self.read_binary_file(data_fn) except ValueError as ve: self.exit_with_error(str(ve)) if path['__method__'] == 'post': - response = self.post_requests(endpoint, data) + response = self.post_requests(endpoint, data, params) elif path['__method__'] == 'put': - params = endpoint_params.copy() - params.update(suffix_param) response = self.put_requests(endpoint, data, params) if self.wrapped: diff --git a/jans-cli-tui/cli_tui/jans_cli_tui.py b/jans-cli-tui/cli_tui/jans_cli_tui.py index 4f779459ef7..ea6c652191f 100755 --- a/jans-cli-tui/cli_tui/jans_cli_tui.py +++ b/jans-cli-tui/cli_tui/jans_cli_tui.py @@ -11,6 +11,7 @@ import concurrent.futures from enum import Enum +from functools import partial from pathlib import Path from itertools import cycle from requests.models import Response @@ -41,6 +42,46 @@ from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous from prompt_toolkit.layout.containers import Float, HSplit, VSplit from prompt_toolkit.formatted_text import HTML, merge_formatted_text +from prompt_toolkit.completion import PathCompleter + + +class TextInputDialog: + def __init__(self, title="", label_text="", completer=None): + self.future = Future() + + def accept_text(buf): + get_app().layout.focus(ok_button) + buf.complete_state = None + return True + + def accept(): + self.future.set_result(self.text_area.text) + + def cancel(): + self.future.set_result(None) + + self.text_area = TextArea( + completer=completer, + multiline=False, + width=D(preferred=40), + accept_handler=accept_text, + ) + + ok_button = Button(text="OK", handler=accept) + cancel_button = Button(text="Cancel", handler=cancel) + + self.dialog = Dialog( + title=title, + body=HSplit([Label(text=label_text), self.text_area]), + buttons=[ok_button, cancel_button], + width=D(preferred=80), + modal=True, + ) + + def __pt_container__(self): + return self.dialog + + from prompt_toolkit.layout.containers import ( Float, @@ -50,7 +91,8 @@ DynamicContainer, FloatContainer, Window, - FormattedTextControl + FormattedTextControl, + AnyContainer ) from prompt_toolkit.layout.dimension import D from prompt_toolkit.layout.layout import Layout @@ -67,24 +109,20 @@ from collections import OrderedDict from typing import Any, Optional, Sequence, Union from prompt_toolkit.key_binding.key_processor import KeyPressEvent -from prompt_toolkit.layout.containers import ( - AnyContainer, -) from prompt_toolkit.layout.dimension import AnyDimension from prompt_toolkit.formatted_text import AnyFormattedText from typing import TypeVar, Callable from prompt_toolkit.widgets import Button, Dialog, Label +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.keys import Keys +from cli_style import style +from utils.multi_lang import _ +from utils.static import cli_style from utils.validators import IntegerValidator from wui_components.jans_cli_dialog import JansGDialog from wui_components.jans_nav_bar import JansNavBar from wui_components.jans_message_dialog import JansMessageDialog -from cli_style import style -import cli_style -from utils.multi_lang import _ -from prompt_toolkit.mouse_events import MouseEvent, MouseEventType -from prompt_toolkit.keys import Keys - home_dir = Path.home() config_dir = home_dir.joinpath('.config') @@ -711,7 +749,7 @@ def custom_handler(): if on_selection_changed: rl._handle_enter = custom_handler - v = VSplit([Window(FormattedTextControl(title), width=len(title)+1, style=style,), rl]) + v = VSplit([Window(FormattedTextControl(title), width=len(title)+1, style=style,), rl], height=len(values)) v.me = rl @@ -772,12 +810,16 @@ def main_nav_selection_changed(self, selection: str) -> None: plugin.on_page_enter() plugin.set_center_frame() - async def show_dialog_as_float(self, dialog:Dialog) -> None: + async def show_dialog_as_float(self, dialog:Dialog, focus=None) -> None: 'Coroutine.' float_ = Float(content=dialog) self.root_layout.floats.append(float_) self.layout.focus(dialog) + if focus: + self.layout.focus(focus) + self.invalidate() + result = await dialog.future if float_ in self.root_layout.floats: @@ -790,11 +832,11 @@ async def show_dialog_as_float(self, dialog:Dialog) -> None: return result - def show_jans_dialog(self, dialog:Dialog) -> None: + def show_jans_dialog(self, dialog:Dialog, focus=None) -> None: async def coroutine(): focused_before = self.layout.current_window - result = await self.show_dialog_as_float(dialog) + result = await self.show_dialog_as_float(dialog, focus) try: self.layout.focus(focused_before) except Exception: @@ -806,8 +848,7 @@ async def coroutine(): def data_display_dialog(self, **params: Any) -> None: - body = HSplit([ - TextArea( + text_area = TextArea( lexer=DynamicLexer(lambda: PygmentsLexer.from_filename('.json', sync_from_start=True)), scrollbar=True, line_numbers=True, @@ -816,9 +857,37 @@ def data_display_dialog(self, **params: Any) -> None: text=str(json.dumps(params['data'], indent=2)), style='class:jans-main-datadisplay.text' ) - ],style='class:jans-main-datadisplay') - dialog = JansGDialog(self, title=params['selected'][0], body=body) + data_display_widgets = [text_area] + if 'message' in params: + data_display_widgets.insert(0, Label(params['message'], style="blink")) + + body = HSplit(data_display_widgets, style='class:jans-main-datadisplay') + title = params.get('title') or params['selected'][0] + + def do_save(dialog): + try: + with open(dialog.body.text, 'w') as w: + w.write(text_area.text) + self.pbar_text = _("File {} was saved".format(dialog.body.text)) + self.show_message(_("Info"), _("File {} was successfully saved").format(dialog.body.text), tobefocused=self.center_container) + except Exception as e: + self.show_message(_("Error!"), _("An error ocurred while saving") + ":\n{}".format(str(e)), tobefocused=self.center_container) + + def save(dialog): + dialog.future.set_result('Save') + path_textarea = TextArea(style=cli_style.edit_text) + do_save_button = Button(_("OK"), handler=do_save) + buttons = [Button('Cancel'), do_save_button] + save_dialog = JansGDialog(self, title=_("Enter path of file to save"), body=path_textarea, buttons=buttons) + self.show_jans_dialog(save_dialog) + + + save_button = Button(_("Save"), handler=save) + + + buttons = [Button('Close'), save_button] + dialog = JansGDialog(self, title=title, body=body, buttons=buttons) self.show_jans_dialog(dialog) @@ -859,7 +928,7 @@ def show_message( if not tobefocused: focused_before = self.root_layout.floats[-1].content if self.root_layout.floats else self.layout.current_window #show_message else : - focused_before = self.root_layout.floats[-1].content if self.root_layout.floats else tobefocused + focused_before = tobefocused float_ = Float(content=dialog) self.root_layout.floats.append(float_) dialog.me = float_ diff --git a/jans-cli-tui/cli_tui/plugins/010_auth_server/.#edit_client_dialog.py b/jans-cli-tui/cli_tui/plugins/010_auth_server/.#edit_client_dialog.py deleted file mode 120000 index b7534fa5351..00000000000 --- a/jans-cli-tui/cli_tui/plugins/010_auth_server/.#edit_client_dialog.py +++ /dev/null @@ -1 +0,0 @@ -mbaser@ubuntu.7412 \ No newline at end of file diff --git a/jans-cli-tui/cli_tui/plugins/010_auth_server/edit_client_dialog.py b/jans-cli-tui/cli_tui/plugins/010_auth_server/edit_client_dialog.py index b7498d9d6da..c15c9d9f2dd 100755 --- a/jans-cli-tui/cli_tui/plugins/010_auth_server/edit_client_dialog.py +++ b/jans-cli-tui/cli_tui/plugins/010_auth_server/edit_client_dialog.py @@ -204,6 +204,9 @@ def fill_client_scopes(self): label = scope.get('displayName') or scope.get('inum') or scope_dn if [scope_dn, label] not in self.client_scopes_entries: self.client_scopes_entries.append([scope_dn, label]) + if hasattr(self, 'client_scopes'): + if not [scope_dn, label] in self.client_scopes.entries: + self.client_scopes.add_label(scope_dn, label) def prepare_tabs(self) -> None: """Prepare the tabs for Edil Client Dialogs @@ -331,7 +334,6 @@ def prepare_tabs(self) -> None: jans_help=_("Add Scopes"), handler=self.add_scopes) ]) - self.client_scopes = JansLabelContainer( title=_('Scopes'), @@ -342,8 +344,6 @@ def prepare_tabs(self) -> None: entries = self.client_scopes_entries, ) - - basic_tab_widgets.append(self.client_scopes) diff --git a/jans-cli-tui/cli_tui/plugins/010_auth_server/ssa.py b/jans-cli-tui/cli_tui/plugins/010_auth_server/ssa.py index bb3ec6703e8..48ddd9a8511 100644 --- a/jans-cli-tui/cli_tui/plugins/010_auth_server/ssa.py +++ b/jans-cli-tui/cli_tui/plugins/010_auth_server/ssa.py @@ -122,7 +122,10 @@ def save_ssa(self, dialog): # set expiration to 50 years new_data['expiration'] = int(datetime.now().timestamp()) + 1576800000 else: - new_data['expiration'] = int(datetime.fromisoformat(self.expire_widget.value).timestamp()) + if self.expire_widget.value: + new_data['expiration'] = int(datetime.fromisoformat(self.expire_widget.value).timestamp()) + + new_data['software_roles'] = new_data['software_roles'].splitlines() new_data['software_roles'] = new_data['software_roles'].splitlines() @@ -132,8 +135,11 @@ async def coroutine(): operation_id = 'post-register-ssa' cli_args = {'operation_id': operation_id, 'cli_object': self.cli_object, 'data': new_data} self.app.start_progressing(_("Saving ssa...")) - await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args) + result = await get_event_loop().run_in_executor(self.app.executor, self.app.cli_requests, cli_args) self.app.stop_progressing() + ssa = result.json() + self.app.data_display_dialog(data=ssa, title=_("SSA Token"), message=_("Save and store it securely. This is the only time you see this token.")) + self.get_ssa() dialog.future.set_result(True) diff --git a/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py b/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py index 0387279e6f1..64d0f80478f 100755 --- a/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py +++ b/jans-cli-tui/cli_tui/plugins/060_scripts/edit_script_dialog.py @@ -12,6 +12,7 @@ RadioList, Button, Dialog, + Frame ) import asyncio from prompt_toolkit.lexers import PygmentsLexer @@ -96,16 +97,26 @@ def save(self) -> None: data['script'] = self.script if data['locationType'] != 'file': - data.pop('locationPath', None) + data['locationType'] = 'db' + + if not 'moduleProperties' in data: + data['moduleProperties'] = [] + + for prop in data['moduleProperties'][:]: + if prop['value1'] == 'location_type': + data['moduleProperties'].remove(prop) + + data['moduleProperties'].append({'value1': 'location_type', 'value2': data['locationType']}) if self.data.get('baseDn'): data['baseDn'] = self.data['baseDn'] self.new_data = data - close_me = True + if self.save_handler: close_me = self.save_handler(self) + if close_me: self.future.set_result(DialogResult.ACCEPT) @@ -187,6 +198,8 @@ def create_window(self) -> None: module_properties_data = [] for prop in self.data.get('moduleProperties', []): + if prop['value1'] == 'location_type': + continue module_properties_data.append([prop['value1'], prop.get('value2', '')]) self.module_properties_container = JansVerticalNav( @@ -228,11 +241,12 @@ def create_window(self) -> None: self.myparent.getTitledText(_("Name"), name='name', value=self.data.get('name',''), style='class:script-titledtext', jans_help=self.myparent.get_help_from_schema(schema, 'name')), self.myparent.getTitledText(_("Description"), name='description', value=self.data.get('description',''), style='class:script-titledtext', jans_help=self.myparent.get_help_from_schema(schema, 'description')), + self.myparent.getTitledRadioButton( _("Location"), name='locationType', - values=[('ldap', _("Database")), ('file', _("File System"))], - current_value= 'file' if self.data.get('locationType') == 'file' else 'ldap', + values=[('db', _("Database")), ('file', _("File System"))], + current_value= 'file' if self.data.get('locationType') == 'file' else 'db', jans_help=_("Where to save script"), style='class:outh-client-radiobutton', on_selection_changed=self.script_location_changed, diff --git a/jans-cli-tui/cli_tui/wui_components/jans_cli_dialog.py b/jans-cli-tui/cli_tui/wui_components/jans_cli_dialog.py index 530d51fe635..fa4cd5f9a5a 100755 --- a/jans-cli-tui/cli_tui/wui_components/jans_cli_dialog.py +++ b/jans-cli-tui/cli_tui/wui_components/jans_cli_dialog.py @@ -46,7 +46,7 @@ def do_handler(button_text, handler, keep_dialog): if handler: handler(self) - if not keep_dialog: + if not (keep_dialog or self.future.done()): self.future.set_result(button_text) for button in buttons: diff --git a/jans-cli-tui/cli_tui/wui_components/jans_path_finder.py.txt b/jans-cli-tui/cli_tui/wui_components/jans_path_finder.py.txt new file mode 100644 index 00000000000..4c9d85a8b8b --- /dev/null +++ b/jans-cli-tui/cli_tui/wui_components/jans_path_finder.py.txt @@ -0,0 +1,451 @@ +#!/usr/bin/env python + +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.layout import ( + Float, + FloatContainer, + HSplit, + Layout, +) +from prompt_toolkit.application.current import get_app +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.layout.containers import Float, HSplit, Window +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.formatted_text import HTML, merge_formatted_text +from prompt_toolkit.layout.margins import ScrollbarMargin +from prompt_toolkit.key_binding.bindings.focus import focus_next +from prompt_toolkit.layout.dimension import D +from prompt_toolkit.layout.containers import ( + Float, + HSplit, + FloatContainer, + Window, + FormattedTextControl +) +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.data_structures import Point + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.formatted_text import HTML, merge_formatted_text +from prompt_toolkit.layout.margins import ScrollbarMargin +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.layout.dimension import D +from prompt_toolkit.widgets import Button, Label +from prompt_toolkit.layout.containers import ( + Float, + HSplit, + VSplit, + DynamicContainer, + Window +) +from typing import Optional +from prompt_toolkit.formatted_text import AnyFormattedText +from prompt_toolkit.widgets import Button, Dialog +from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyBindingsBase + +import glob + +#-------------------------------------------------------------------------------# +#-------------------------------------------------------------------------------# +#-------------------------------------------------------------------------------# + +class JansPathFinder: + """_summary_ + """ + + def __init__( + self, + )-> HSplit: + + + self.cord_y = 0 + self.cord_x = 0 + self.old_cord_x = 0 + self.selected_cord = (0, 0) + + self.start_dir = 0 + self.start_file = 0 + self.flag_y =0 + self.list_dir = [] + self.select_flag = True + + self.current_dir = '/' + self.current_file = '' + self.list_dir.append(self.current_dir) + # -----------------------------------------------------------------------------------------------# + # -----------------------------------------------------------------------------------------------# + # -----------------------------------------------------------------------------------------------# + + self.current_dir_label = Label(text=self.current_dir,width=len(self.current_dir)) + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + + self.dire_container = Window( + content=FormattedTextControl( + text=self._get_directories, + ), + height=5, + cursorline=False, + style="fg:#ff0000 bg:#ffffff", ### days window style + right_margins=[ScrollbarMargin(display_arrows=True),], + wrap_lines=True, + ) + + self.file_container = Window( + content=FormattedTextControl( + text=self._get_files, + focusable=True, + ), + height=5, + cursorline=False, + style="fg:#0000ff bg:#ffffff", ### time window style + right_margins=[ScrollbarMargin(display_arrows=True),], + wrap_lines=True + ) + + self.current_directories = self.get_direct() + + self.current_files = self.get_files() + + self.container =HSplit(children=[ + VSplit([ + Label(text="Dir: ",width=len("Dir: ")), + Window(width=2,char=' '), + DynamicContainer(lambda: self.current_dir_label), + ],style="fg:#000000 bg:#ffffff",padding=1), ### Month and year window style + #DynamicContainer(lambda: self.depug), + + + # -----------------------------------------------------------------------------------------------# + # ----------------------------------------- Directories -----------------------------------------# + # -----------------------------------------------------------------------------------------------# + DynamicContainer(lambda: self.dire_container), + + # -----------------------------------------------------------------------------------------------# + # ----------------------------------------- Files -----------------------------------------------# + # -----------------------------------------------------------------------------------------------# + VSplit([ + Label(text="Files: ",width=len("Files: ")), + Window(width=2,char=' '), + DynamicContainer(lambda: self.current_file_label), + ],style="fg:#000000 bg:#ffffff",padding=1), ### Month and year window style + DynamicContainer(lambda: self.file_container), + + ]) + + def get_direct (self)-> list: + dir_list = [] + + self.len_directories = len(glob.glob("/{}/*/".format(self.current_dir))) + + for dir in (glob.glob("/{}/*/".format(self.current_dir))): + + dir_list.append('{}'.format(dir.replace('/{}/'.format(self.current_dir),'')).replace('//','/')) + + return dir_list + + + def get_files(self)-> list: + files_list = [] + + self.len_files = len(glob.glob("/{}/*.*".format(self.current_dir))) + + for dir in (glob.glob("/{}/*.*".format(self.current_dir))): + + files_list.append('{}'.format(dir.replace('/{}/'.format(self.current_dir),'')).replace('//','/')) + + return files_list + + + def _get_files(self)-> AnyFormattedText: + result = [] + files_list = [] + + for i, dir in enumerate(self.current_files[0+self.start_file:5+self.start_file]): + if i == self.cord_y and self.select_flag==True: + files_list.append(HTML(''.format(dir.replace('/{}/'.format(self.current_dir),'')))) + else: + files_list.append(HTML('{}'.format(dir.replace('/{}/'.format(self.current_dir),'')))) + + result= (files_list) + + result.append("\n") + + return merge_formatted_text(result) + + def _get_directories(self)-> AnyFormattedText: + result = [] + dir_list = [] + + for i, dir in enumerate(self.current_directories[0+self.start_dir:5+self.start_dir]): + if i == self.cord_y and self.select_flag == False: + dir_list.append(HTML(''.format(dir.replace('/{}/'.format(self.current_dir),'')))) + else: + dir_list.append(HTML('{}'.format(dir.replace('/{}/'.format(self.current_dir),'')))) + + result= (dir_list) + + result.append("\n") + + return merge_formatted_text(result) + + + def up(self)-> None: + + if self.select_flag == False: ## Dir + + if self.cord_y <= 0 and self.flag_y > 0 : + self.flag_y -=1 + self.start_dir -=1 + + elif self.cord_y -1 >=0: + self.cord_y -=1 + self.flag_y -=1 + self.current_dir = self.current_directories[0+self.start_dir:5+self.start_dir][self.cord_y] + + else: ## file + if self.cord_y <= 0 and self.flag_y > 0 : + self.flag_y -=1 + self.start_file -=1 + + elif self.cord_y -1 >=0: + self.cord_y -=1 + self.flag_y -=1 + + self.current_files = self.current_files[0+self.start_file:5+self.start_file] + + def down(self)-> None: + if self.select_flag == False : + if self.cord_y +1 < 5 : + self.cord_y +=1 + self.flag_y +=1 + + elif self.flag_y < len(self.current_directories) -1: + + self.flag_y +=1 + self.start_dir +=1 + + self.current_dir = self.current_directories[0+self.start_dir:5+self.start_dir][self.cord_y] + else: + if self.cord_y +1 < 5 : + self.cord_y +=1 + self.flag_y +=1 + + elif self.flag_y < len(self.current_files) -1: + + self.flag_y +=1 + self.start_file +=1 + + self.current_files = self.current_files[0+self.start_file:5+self.start_file] + + def enter(self)-> None: + # self.current_dir = self.current_directories[0+self.start_dir:5+self.start_dir][self.cord_y] + # self.current_dir_label =Label(text=self.current_dir,width=len(self.current_dir)) + + ### current files + if self.current_files: + self.current_file = self.current_files[0+self.start_file:5+self.start_file][self.cord_y] + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + else: + self.current_file = 'No Files' + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + + + self.start_dir = 0 + self.cord_y = 0 + + + def right(self)-> None: + self.current_dir = self.current_directories[0+self.start_dir:5+self.start_dir][self.cord_y] + self.current_dir_label =Label(text=self.current_dir,width=len(self.current_dir)) + + ### current files + if self.current_files: + self.current_file = self.current_files[0+self.start_file:5+self.start_file][self.cord_y] + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + else: + self.current_file = 'No Files' + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + + + self.start_dir = 0 + self.cord_y = 0 + self.flag_y = 0 + self.current_files = self.get_files() + + + if len(glob.glob("/{}/*/".format(self.current_dir))) >= 1: + self.list_dir.append(self.current_dir) + self.current_directories = self.get_direct() + + else: + pass + + + def left(self)-> None: + # self.current_dir2 = self.current_directories[0+self.start_dir:5+self.start_dir][self.cord_y] + + if len(self.list_dir) != 1: + self.start_dir = 0 + self.cord_y = 0 + self.flag_y = 0 + + + self.list_dir.remove(self.list_dir[-1]) + self.current_dir = self.list_dir[-1] + self.current_directories = self.get_direct() + self.current_files = self.get_files() + + + self.current_file = 'No Files' + self.current_file_label = Label(text=self.current_file,width=len(self.current_file)) + + self.current_dir_label =Label(text=self.current_dir,width=len(self.current_dir)) + + + def next(self)-> None: + self.select_flag = not self.select_flag + self.cord_y = 0 + self.flag_y = 0 + + + + def __pt_container__(self)-> Dialog: + return self.container + + +class PathFinderWidget: + """This is a Dape Picker widget to select exact time and date + """ + def __init__( + self, + value:str, + ) -> Window: + + if value: + self.text = value + else: + self.text = "Enter to Browse Path" + + + self.value = str(value) + + self.dropdown = True + self.window = Window( + content=FormattedTextControl( + text=self._get_text, + focusable=True, + key_bindings=self._get_key_bindings(), + ), height= 5) #D()) #5 ## large sized enties get >> (window too small) + + self.select_box = JansPathFinder() + self.select_box_float = Float(content=self.select_box, xcursor=True, ycursor=True) + + @property + def value(self): + """Getter for the value property + + Returns: + str: The selected value + """ + if self.text != "Enter to Select": + return self.text + + @value.setter + def value( + self, + value:str, + )-> None: + self._value = self.value + + + def _get_text(self)-> AnyFormattedText: + """To get The selected value + + Returns: + str: The selected value + """ + + if get_app().layout.current_window is self.window: + return HTML('> <'.format('#00FF00', self.text)) + return '> {} <'.format(self.text) + + def _get_key_bindings(self)-> KeyBindingsBase: + """All key binding for the Dialog with Navigation bar + + Returns: + KeyBindings: The method according to the binding key + """ + + kb = KeyBindings() + + def _focus_next(event): + focus_next(event) + + def _focus_pre(event): + focus_previous(event) + + @kb.add("enter") + def _enter(event) -> None: + + if self.select_box_float not in get_app().layout.container.floats: + get_app().layout.container.floats.append(self.select_box_float) + self.select_box.enter() + else: + + self.text= self.select_box.current_file + + + get_app().layout.container.floats.remove(self.select_box_float) + + @kb.add("up") + def _up(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.up() + + @kb.add("down") + def _down(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.down() + + @kb.add("right") + def _right(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.right() + + @kb.add("left") + def _left(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.left() + + @kb.add("tab") + def _tab(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.next() + else : + _focus_next(event) + + @kb.add("s-tab") + def _tab(event): + if self.select_box_float in get_app().layout.container.floats: + self.select_box.next() + else : + _focus_pre(event) + + + @kb.add("escape") + def _escape(event): + if self.select_box_float in get_app().layout.container.floats: + app = get_app() + app.layout.container.floats.remove(self.select_box_float) + return kb + + def __pt_container__(self)-> Window: + return self.window + + +