<s> import os <EOL> import os . path as op <EOL> import platform <EOL> import re <EOL> import shutil <EOL> from datetime import datetime <EOL> from operator import itemgetter <EOL> from werkzeug import secure_filename <EOL> from flask import flash , redirect , abort , request , send_file <EOL> from wtforms import fields , validators <EOL> from flask . ext . admin import form , helpers <EOL> from flask . ext . admin . _compat import urljoin , as_unicode <EOL> from flask . ext . admin . base import BaseView , expose <EOL> from flask . ext . admin . actions import action , ActionsMixin <EOL> from flask . ext . admin . babel import gettext , lazy_gettext <EOL> class FileAdmin ( BaseView , ActionsMixin ) : <EOL> """<STR_LIT>""" <EOL> can_upload = True <EOL> """<STR_LIT>""" <EOL> can_download = True <EOL> """<STR_LIT>""" <EOL> can_delete = True <EOL> """<STR_LIT>""" <EOL> can_delete_dirs = True <EOL> """<STR_LIT>""" <EOL> can_mkdir = True <EOL> """<STR_LIT>""" <EOL> can_rename = True <EOL> """<STR_LIT>""" <EOL> allowed_extensions = None <EOL> """<STR_LIT>""" <EOL> editable_extensions = tuple ( ) <EOL> """<STR_LIT>""" <EOL> list_template = '<STR_LIT>' <EOL> """<STR_LIT>""" <EOL> upload_template = '<STR_LIT>' <EOL> """<STR_LIT>""" <EOL> mkdir_template = '<STR_LIT>' <EOL> """<STR_LIT>""" <EOL> rename_template = '<STR_LIT>' <EOL> """<STR_LIT>""" <EOL> edit_template = '<STR_LIT>' <EOL> """<STR_LIT>""" <EOL> form_base_class = form . BaseForm <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , base_path , base_url = None , <EOL> name = None , category = None , endpoint = None , url = None , <EOL> verify_path = True ) : <EOL> """<STR_LIT>""" <EOL> self . base_path = as_unicode ( base_path ) <EOL> self . base_url = base_url <EOL> self . init_actions ( ) <EOL> self . _on_windows = platform . system ( ) == '<STR_LIT>' <EOL> if ( self . allowed_extensions and <EOL> not isinstance ( self . allowed_extensions , set ) ) : <EOL> self . allowed_extensions = set ( self . allowed_extensions ) <EOL> if ( self . editable_extensions and <EOL> not isinstance ( self . editable_extensions , set ) ) : <EOL> self . editable_extensions = set ( self . editable_extensions ) <EOL> if not op . exists ( base_path ) : <EOL> raise IOError ( '<STR_LIT>' % base_path ) <EOL> super ( FileAdmin , self ) . __init__ ( name , category , endpoint , url ) <EOL> def is_accessible_path ( self , path ) : <EOL> """<STR_LIT>""" <EOL> return True <EOL> def get_base_path ( self ) : <EOL> """<STR_LIT>""" <EOL> return op . normpath ( self . base_path ) <EOL> def get_base_url ( self ) : <EOL> """<STR_LIT>""" <EOL> return self . base_url <EOL> def get_upload_form ( self ) : <EOL> """<STR_LIT>""" <EOL> class UploadForm ( self . form_base_class ) : <EOL> """<STR_LIT>""" <EOL> upload = fields . FileField ( lazy_gettext ( '<STR_LIT>' ) ) <EOL> def __init__ ( self , * args , ** kwargs ) : <EOL> super ( UploadForm , self ) . __init__ ( * args , ** kwargs ) <EOL> self . admin = kwargs [ '<STR_LIT>' ] <EOL> def validate_upload ( self , field ) : <EOL> if not self . upload . data : <EOL> raise validators . ValidationError ( gettext ( '<STR_LIT>' ) ) <EOL> filename = self . upload . data . filename <EOL> if not self . admin . is_file_allowed ( filename ) : <EOL> raise validators . ValidationError ( gettext ( '<STR_LIT>' ) ) <EOL> return UploadForm <EOL> def get_edit_form ( self ) : <EOL> """<STR_LIT>""" <EOL> class EditForm ( self . form_base_class ) : <EOL> content = fields . TextAreaField ( lazy_gettext ( '<STR_LIT>' ) , <EOL> ( validators . required ( ) , ) ) <EOL> return EditForm <EOL> def get_name_form ( self ) : <EOL> """<STR_LIT>""" <EOL> def validate_name ( self , field ) : <EOL> regexp = re . compile ( r'<STR_LIT>' ) <EOL> if not regexp . match ( field . data ) : <EOL> raise validators . ValidationError ( gettext ( '<STR_LIT>' ) ) <EOL> class NameForm ( self . form_base_class ) : <EOL> """<STR_LIT>""" <EOL> name = fields . StringField ( lazy_gettext ( '<STR_LIT:Name>' ) , <EOL> validators = [ validators . Required ( ) , <EOL> validate_name ] ) <EOL> path = fields . HiddenField ( ) <EOL> return NameForm <EOL> def get_delete_form ( self ) : <EOL> """<STR_LIT>""" <EOL> class DeleteForm ( self . form_base_class ) : <EOL> path = fields . HiddenField ( validators = [ validators . Required ( ) ] ) <EOL> return DeleteForm <EOL> def upload_form ( self ) : <EOL> """<STR_LIT>""" <EOL> upload_form_class = self . get_upload_form ( ) <EOL> if request . form : <EOL> formdata = request . form . copy ( ) <EOL> formdata . update ( request . files ) <EOL> return upload_form_class ( formdata , admin = self ) <EOL> elif request . files : <EOL> return upload_form_class ( request . files , admin = self ) <EOL> else : <EOL> return upload_form_class ( admin = self ) <EOL> def name_form ( self ) : <EOL> """<STR_LIT>""" <EOL> name_form_class = self . get_name_form ( ) <EOL> if request . form : <EOL> return name_form_class ( request . form ) <EOL> elif request . args : <EOL> return name_form_class ( request . args ) <EOL> else : <EOL> return name_form_class ( ) <EOL> def edit_form ( self ) : <EOL> """<STR_LIT>""" <EOL> edit_form_class = self . get_edit_form ( ) <EOL> if request . form : <EOL> return edit_form_class ( request . form ) <EOL> else : <EOL> return edit_form_class ( ) <EOL> def delete_form ( self ) : <EOL> """<STR_LIT>""" <EOL> delete_form_class = self . get_delete_form ( ) <EOL> if request . form : <EOL> return delete_form_class ( request . form ) <EOL> else : <EOL> return delete_form_class ( ) <EOL> def is_file_allowed ( self , filename ) : <EOL> """<STR_LIT>""" <EOL> ext = op . splitext ( filename ) [ <NUM_LIT:1> ] . lower ( ) <EOL> if ext . startswith ( '<STR_LIT:.>' ) : <EOL> ext = ext [ <NUM_LIT:1> : ] <EOL> if self . allowed_extensions and ext not in self . allowed_extensions : <EOL> return False <EOL> return True <EOL> def is_file_editable ( self , filename ) : <EOL> """<STR_LIT>""" <EOL> ext = op . splitext ( filename ) [ <NUM_LIT:1> ] . lower ( ) <EOL> if ext . startswith ( '<STR_LIT:.>' ) : <EOL> ext = ext [ <NUM_LIT:1> : ] <EOL> if not self . editable_extensions or ext not in self . editable_extensions : <EOL> return False <EOL> return True <EOL> def is_in_folder ( self , base_path , directory ) : <EOL> """<STR_LIT>""" <EOL> return op . normpath ( directory ) . startswith ( base_path ) <EOL> def save_file ( self , path , file_data ) : <EOL> """<STR_LIT>""" <EOL> file_data . save ( path ) <EOL> def validate_form ( self , form ) : <EOL> """<STR_LIT>""" <EOL> return helpers . validate_form_on_submit ( form ) <EOL> def _get_dir_url ( self , endpoint , path = None , ** kwargs ) : <EOL> """<STR_LIT>""" <EOL> if not path : <EOL> return self . get_url ( endpoint ) <EOL> else : <EOL> if self . _on_windows : <EOL> path = path . replace ( '<STR_LIT:\\>' , '<STR_LIT:/>' ) <EOL> kwargs [ '<STR_LIT:path>' ] = path <EOL> return self . get_url ( endpoint , ** kwargs ) <EOL> def _get_file_url ( self , path ) : <EOL> """<STR_LIT>""" <EOL> if self . is_file_editable ( path ) : <EOL> route = '<STR_LIT>' <EOL> else : <EOL> route = '<STR_LIT>' <EOL> return self . get_url ( route , path = path ) <EOL> def _normalize_path ( self , path ) : <EOL> """<STR_LIT>""" <EOL> base_path = self . get_base_path ( ) <EOL> if path is None : <EOL> directory = base_path <EOL> path = '<STR_LIT>' <EOL> else : <EOL> path = op . normpath ( path ) <EOL> directory = op . normpath ( op . join ( base_path , path ) ) <EOL> if not self . is_in_folder ( base_path , directory ) : <EOL> abort ( <NUM_LIT> ) <EOL> if not op . exists ( directory ) : <EOL> abort ( <NUM_LIT> ) <EOL> return base_path , directory , path <EOL> def is_action_allowed ( self , name ) : <EOL> if name == '<STR_LIT>' and not self . can_delete : <EOL> return False <EOL> elif name == '<STR_LIT>' and len ( self . editable_extensions ) == <NUM_LIT:0> : <EOL> return False <EOL> return True <EOL> def on_rename ( self , full_path , dir_base , filename ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def on_edit_file ( self , full_path , path ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def on_file_upload ( self , directory , path , filename ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def on_mkdir ( self , parent_dir , dir_name ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def on_directory_delete ( self , full_path , dir_name ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def on_file_delete ( self , full_path , filename ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> def _save_form_files ( self , directory , path , form ) : <EOL> filename = op . join ( directory , <EOL> secure_filename ( form . upload . data . filename ) ) <EOL> if op . exists ( filename ) : <EOL> flash ( gettext ( '<STR_LIT>' , name = filename ) , <EOL> '<STR_LIT:error>' ) <EOL> else : <EOL> self . save_file ( filename , form . upload . data ) <EOL> self . on_file_upload ( directory , path , filename ) <EOL> @ expose ( '<STR_LIT:/>' ) <EOL> @ expose ( '<STR_LIT>' ) <EOL> def index ( self , path = None ) : <EOL> """<STR_LIT>""" <EOL> if self . can_delete : <EOL> delete_form = self . delete_form ( ) <EOL> else : <EOL> delete_form = None <EOL> base_path , directory , path = self . _normalize_path ( path ) <EOL> if not self . is_accessible_path ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> items = [ ] <EOL> if directory != base_path : <EOL> parent_path = op . normpath ( op . join ( path , '<STR_LIT:..>' ) ) <EOL> if parent_path == '<STR_LIT:.>' : <EOL> parent_path = None <EOL> items . append ( ( '<STR_LIT:..>' , parent_path , True , <NUM_LIT:0> , <NUM_LIT:0> ) ) <EOL> for f in os . listdir ( directory ) : <EOL> fp = op . join ( directory , f ) <EOL> rel_path = op . join ( path , f ) <EOL> if self . is_accessible_path ( rel_path ) : <EOL> items . append ( ( f , rel_path , op . isdir ( fp ) , op . getsize ( fp ) , op . getmtime ( fp ) ) ) <EOL> items . sort ( key = itemgetter ( <NUM_LIT:0> ) ) <EOL> items . sort ( key = itemgetter ( <NUM_LIT:2> ) , reverse = True ) <EOL> items . sort ( key = lambda values : ( values [ <NUM_LIT:0> ] , values [ <NUM_LIT:1> ] , values [ <NUM_LIT:2> ] , values [ <NUM_LIT:3> ] , datetime . fromtimestamp ( values [ <NUM_LIT:4> ] ) ) , reverse = True ) <EOL> accumulator = [ ] <EOL> breadcrumbs = [ ] <EOL> for n in path . split ( os . sep ) : <EOL> accumulator . append ( n ) <EOL> breadcrumbs . append ( ( n , op . join ( * accumulator ) ) ) <EOL> actions , actions_confirmation = self . get_actions_list ( ) <EOL> return self . render ( self . list_template , <EOL> dir_path = path , <EOL> breadcrumbs = breadcrumbs , <EOL> get_dir_url = self . _get_dir_url , <EOL> get_file_url = self . _get_file_url , <EOL> items = items , <EOL> actions = actions , <EOL> actions_confirmation = actions_confirmation , <EOL> delete_form = delete_form ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> def upload ( self , path = None ) : <EOL> """<STR_LIT>""" <EOL> base_path , directory , path = self . _normalize_path ( path ) <EOL> if not self . can_upload : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' , path ) ) <EOL> if not self . is_accessible_path ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> form = self . upload_form ( ) <EOL> if self . validate_form ( form ) : <EOL> try : <EOL> self . _save_form_files ( directory , path , form ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' , path ) ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , error = ex ) , '<STR_LIT:error>' ) <EOL> return self . render ( self . upload_template , form = form ) <EOL> @ expose ( '<STR_LIT>' ) <EOL> def download ( self , path = None ) : <EOL> """<STR_LIT>""" <EOL> if not self . can_download : <EOL> abort ( <NUM_LIT> ) <EOL> base_path , directory , path = self . _normalize_path ( path ) <EOL> base_url = self . get_base_url ( ) <EOL> if base_url : <EOL> base_url = urljoin ( self . get_url ( '<STR_LIT>' ) , base_url ) <EOL> return redirect ( urljoin ( base_url , path ) ) <EOL> return send_file ( directory ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> def mkdir ( self , path = None ) : <EOL> """<STR_LIT>""" <EOL> base_path , directory , path = self . _normalize_path ( path ) <EOL> dir_url = self . _get_dir_url ( '<STR_LIT>' , path ) <EOL> if not self . can_mkdir : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( dir_url ) <EOL> if not self . is_accessible_path ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> form = self . name_form ( ) <EOL> if self . validate_form ( form ) : <EOL> try : <EOL> os . mkdir ( op . join ( directory , form . name . data ) ) <EOL> self . on_mkdir ( directory , form . name . data ) <EOL> return redirect ( dir_url ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , error = ex ) , '<STR_LIT:error>' ) <EOL> else : <EOL> helpers . flash_errors ( form , message = '<STR_LIT>' ) <EOL> return self . render ( self . mkdir_template , <EOL> form = form , <EOL> dir_url = dir_url ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:POST>' , ) ) <EOL> def delete ( self ) : <EOL> """<STR_LIT>""" <EOL> form = self . delete_form ( ) <EOL> path = form . path . data <EOL> if path : <EOL> return_url = self . _get_dir_url ( '<STR_LIT>' , op . dirname ( path ) ) <EOL> else : <EOL> return_url = self . get_url ( '<STR_LIT>' ) <EOL> if self . validate_form ( form ) : <EOL> base_path , full_path , path = self . _normalize_path ( path ) <EOL> if not self . can_delete : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( return_url ) <EOL> if not self . is_accessible_path ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> if op . isdir ( full_path ) : <EOL> if not self . can_delete_dirs : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( return_url ) <EOL> try : <EOL> shutil . rmtree ( full_path ) <EOL> self . on_directory_delete ( full_path , path ) <EOL> flash ( gettext ( '<STR_LIT>' , path = path ) ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , error = ex ) , '<STR_LIT:error>' ) <EOL> else : <EOL> try : <EOL> os . remove ( full_path ) <EOL> self . on_file_delete ( full_path , path ) <EOL> flash ( gettext ( '<STR_LIT>' , name = path ) ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , name = ex ) , '<STR_LIT:error>' ) <EOL> else : <EOL> helpers . flash_errors ( form , message = '<STR_LIT>' ) <EOL> return redirect ( return_url ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> def rename ( self ) : <EOL> """<STR_LIT>""" <EOL> form = self . name_form ( ) <EOL> path = form . path . data <EOL> if path : <EOL> base_path , full_path , path = self . _normalize_path ( path ) <EOL> return_url = self . _get_dir_url ( '<STR_LIT>' , op . dirname ( path ) ) <EOL> else : <EOL> return redirect ( self . get_url ( '<STR_LIT>' ) ) <EOL> if not self . can_rename : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( return_url ) <EOL> if not self . is_accessible_path ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> if not op . exists ( full_path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( return_url ) <EOL> if self . validate_form ( form ) : <EOL> try : <EOL> dir_base = op . dirname ( full_path ) <EOL> filename = secure_filename ( form . name . data ) <EOL> os . rename ( full_path , op . join ( dir_base , filename ) ) <EOL> self . on_rename ( full_path , dir_base , filename ) <EOL> flash ( gettext ( '<STR_LIT>' , <EOL> src = op . basename ( path ) , <EOL> dst = filename ) ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , error = ex ) , '<STR_LIT:error>' ) <EOL> return redirect ( return_url ) <EOL> else : <EOL> helpers . flash_errors ( form , message = '<STR_LIT>' ) <EOL> return self . render ( self . rename_template , <EOL> form = form , <EOL> path = op . dirname ( path ) , <EOL> name = op . basename ( path ) , <EOL> dir_url = return_url ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:GET>' , '<STR_LIT:POST>' ) ) <EOL> def edit ( self ) : <EOL> """<STR_LIT>""" <EOL> next_url = None <EOL> path = request . args . getlist ( '<STR_LIT:path>' ) <EOL> if not path : <EOL> return redirect ( self . get_url ( '<STR_LIT>' ) ) <EOL> if len ( path ) > <NUM_LIT:1> : <EOL> next_url = self . get_url ( '<STR_LIT>' , path = path [ <NUM_LIT:1> : ] ) <EOL> path = path [ <NUM_LIT:0> ] <EOL> base_path , full_path , path = self . _normalize_path ( path ) <EOL> if not self . is_accessible_path ( path ) or not self . is_file_editable ( path ) : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return redirect ( self . _get_dir_url ( '<STR_LIT>' ) ) <EOL> dir_url = self . _get_dir_url ( '<STR_LIT>' , os . path . dirname ( path ) ) <EOL> next_url = next_url or dir_url <EOL> form = self . edit_form ( ) <EOL> error = False <EOL> if self . validate_form ( form ) : <EOL> form . process ( request . form , content = '<STR_LIT>' ) <EOL> if form . validate ( ) : <EOL> try : <EOL> with open ( full_path , '<STR_LIT:w>' ) as f : <EOL> f . write ( request . form [ '<STR_LIT:content>' ] ) <EOL> except IOError : <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) , '<STR_LIT:error>' ) <EOL> error = True <EOL> else : <EOL> self . on_edit_file ( full_path , path ) <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) ) <EOL> return redirect ( next_url ) <EOL> else : <EOL> helpers . flash_errors ( form , message = '<STR_LIT>' ) <EOL> try : <EOL> with open ( full_path , '<STR_LIT:rb>' ) as f : <EOL> content = f . read ( ) <EOL> except IOError : <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) , '<STR_LIT:error>' ) <EOL> error = True <EOL> except : <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) , '<STR_LIT:error>' ) <EOL> error = True <EOL> else : <EOL> try : <EOL> content = content . decode ( '<STR_LIT:utf8>' ) <EOL> except UnicodeDecodeError : <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) , '<STR_LIT:error>' ) <EOL> error = True <EOL> except : <EOL> flash ( gettext ( "<STR_LIT>" , name = path ) , '<STR_LIT:error>' ) <EOL> error = True <EOL> else : <EOL> form . content . data = content <EOL> return self . render ( self . edit_template , dir_url = dir_url , path = path , <EOL> form = form , error = error ) <EOL> @ expose ( '<STR_LIT>' , methods = ( '<STR_LIT:POST>' , ) ) <EOL> def action_view ( self ) : <EOL> return self . handle_action ( ) <EOL> @ action ( '<STR_LIT>' , <EOL> lazy_gettext ( '<STR_LIT>' ) , <EOL> lazy_gettext ( '<STR_LIT>' ) ) <EOL> def action_delete ( self , items ) : <EOL> if not self . can_delete : <EOL> flash ( gettext ( '<STR_LIT>' ) , '<STR_LIT:error>' ) <EOL> return <EOL> for path in items : <EOL> base_path , full_path , path = self . _normalize_path ( path ) <EOL> if self . is_accessible_path ( path ) : <EOL> try : <EOL> os . remove ( full_path ) <EOL> flash ( gettext ( '<STR_LIT>' , name = path ) ) <EOL> except Exception as ex : <EOL> flash ( gettext ( '<STR_LIT>' , name = ex ) , '<STR_LIT:error>' ) <EOL> @ action ( '<STR_LIT>' , lazy_gettext ( '<STR_LIT>' ) ) <EOL> def action_edit ( self , items ) : <EOL> return redirect ( self . get_url ( '<STR_LIT>' , path = items ) ) </s>
