Dokumentový sklad v.1 Dokumentový sklad Petr Lampa První verze (2002): Ukládání diplomových prací a ročníkových projektů v PDF a PostScriptu Požadavky: Archivace dokumentů Malá četnost prohlížení Žádná indexace Řešení: Fixní systém práv (student pouze uložit/číst svou práci), ostatní všechno číst Pevná struktura pojmenování (/báze/rok/bp/id.pdf) Dokumentový sklad v.2 Druhá verze (2003): Cíle: Strukturovaný dokumentový sklad pro ukládání dokumentů fakulty Organizace ve formě stromu Ukládání metadat k dokumentům Přístupová a majitelství adresářů/souborů Možnost ukládat verze dokumentů Jednoduchá správa Web prohlížečem (použití sekretariáty) Koncepce: Stejné rozhraní by se hodilo pro prohlížení DP a do budoucna také pro odevzdávání projektů v předmětech Koncepce skladu v.2 Co je společné? Uživatelské rozhraní Zpracování úplného jména dokumentu, validace a kontroly Některé operace (download, upload, rm) Co je různé? Systém pojmenování dokumentů/adresářů Co je povoleno (přístupová práva) Vazba na tabulky v informačním systému 1
Koncepce skladu v.2 Řešení: Ukládání do systému souborů (důležitý návrh pojmenování) velké BLOBy v databází silně komplikují archivaci/restore na funkčnosti skladu bezprostředně nezávisí funkčnost IS fulltext v databázi sice funguje, ale: problém stemizace velká zátěž databáze při indexaci problém konverze formátů (PDF) Koncepce skladu v.2 Použít objektově orientovaný návrh Společné vlastnosti implementovat jako bázovou třídu Specifické mutace funkčnosti různých částí skladu implementovat odvozenými třídami Integrace různých typů skladu do jednoho stromu Omezení: typ je dán nejvyšším adresářem Pojmenování /rp/<rok>/<typ>/<id>.pdf (ps) /dr/{<adresář>/<soubor> /ci/<id_předmětu>/<id_termínu>/{<id_varian ty>/<login>/<soubor> /course/<zkr>/<typ>/{<adresář>/<soubor> První komponenta určuje typ skladu, čili třídu generovaného objektu Implementace class doc_file { function doc_create($name, $path) { // factory method $classname = "doc_folder_${name ; if (!class_exists($classname)) { $src = "$docs_handlers/docs_${name.php"; if (file_exists($src)) include_once($src); return new $classname($path); function doc_file($path) { // constructor 2
Metody doc_title(), doc_access($mode), doc_realpath(), doc_path(), doc_remove(), doc_upload() doc_attrs() seznam atributů, které podporuje daný typ skladu: array("type" => array("title"=>"typ", "sort"=>true, "show"=>true, "order"=>"10", "type"=>"icon"), "title" => array("title"=>"název", "sort"=>true, "show"=>true, "order"=>"20", "type"=>"link", "filter"=>32),... ); Metody doc_getdir() vygeneruje pole obsahující obsah adresáře, ke každému souboru všechny atributy doc_oper() zpracování požadavku z uživatelského rozhraní (download, upload, mkdir) Uživatelské rozhraní docs_show($dir, $attrs) Univerzální metoda, nezávislá na typu skladu. Styl zobrazení, zobrazené prvky, povolené operace dány polem parametrů. Zobrazí ovládací prvky (HTML form, vstupní pole), detekuje jejich použití, kontroluje validitu a volá patřičné metody. Podle definovaných atributů generuje patřičné filtry sloupců, odkazy pro řazení, titulky, obsahy sloupců, atd. Hlavní problémy Koncepce jedné třídy reprezentující dokument se ukázala jako příliš omezující: Při zpracování operací je třeba pracovat s komponentami v cestě diskrétně (kontrola práv, nadřazený adresář). Parsování a validaci cesty musela být implementována v každé odvozené třídě znovu. Pro nové typy skladů bylo třeba zavést přechody mezi různými typy skladů (obdoba mount v Unixu). Uživatelské rozhraní musí u některých operací pracovat současně s dvěma soubory. 3
Dokumentový sklad v.3 Cíle: Odstranit omezení rozšiřovatelnosti. Oddělit třídu zabalující uživatelské rozhraní od třídy popisující dokument. Implementovat do bázové třídy dokumentu více funkčnosti tak, aby byl návrh odvozených tříd jednodušší (a bezpečnější). Při implementaci využít principy používané v systémech souborů (místo kompletního parsování jména použít zpracování po komponentách jména). Nová struktura tříd class doc_folder komponenta v cestě class doc_file dokument (obsahuje pole objektů doc_folder) class doc_repos zabalení uživatelského rozhraní a společných dat doc_file function doc_parse($path) { $cur =& new doc_folder(&$this->repos, null, '/'); $this->folders[0] = &$cur; $this->dirs = explode('/', $path); for ($i = 1; $i < count($this->dirs); $i++) { if (!$cur->doc_access(auth_walk)) *ERR if (!$cur->doc_valid_name($this->dirs[$i], false)) *ERR if (!$cur->doc_lookup(&$this->repos, $this->dirs[$i], $i, &$this->folders[$i])) *ERR $cur = &$this->folders[$i]; doc_folder function doc_lookup($name, $level, &$cur) { if ($level!= 1!ctype_alpha($name)) *ERR $classname = "doc_folder_${name"; if (!class_exists($classname)) { $src = "$docs_handlers/docs_${name.php"; if (file_exists($src)) include_once($src); if (!class_exists($classname)) *ERR $cur = new $classname(&$this, $name); ctype_digit() PHP 4.3.x! 4
Výsledek Prodloužení kódu, nicméně logičtější struktura a snadnější zápis. Složitější na pochopení v některých metodách reprezentuje $this aktuální komponentu, jinde nadřazenou komponentu. Ukázalo se jako nezbytné v některých operacích reprezentovat dokument instancí a v jiných reprezentovat instancí nadřazeného adresáře a holým jménem (neregulérní struktura). Výsledek Lze přecházet z jednoho typu skladu do druhého libovolně: /pubs zpracováno bázovou třídou, vznikne instance třídy doc_pub /132 zpracováno doc_pub, vznikne instance třídy doc_dr /adresář /soubor zpracováno doc_dr Aplikovaná třída zajišťuje správnou kontrolu validity jména, testování přístupových práv a také povolený repertoár operací. Implementované typy ci soubory k termínům (jedna úroveň) course soubory k předmětům (seznam předmětů z IS, seznam virtuálních podadresářů, dále dr) dr strukturovaný adresář s právy a metadaty ftp FTP klient product, proj, pub soubory k VaV (báze z IS, dále z dr) rp diplomky Použití Použití částí skladu je zabudováno do různých částí IS FIT, s omezením na podstrom Prohlížeč celého skladu: $docs_params = array( ); $repos = new doc_repos($pathinfo, $self, $path_type, $docs_params); $ok = $repos->doc_oper($vars, &$file); is_header(); if ($ok) $repos->docs_show(&$file, $vars); else $repos->docs_print_error(); is_footer(); 5
Fulltext Prohlížeč skladu generuje regulární HTML dokument s odkazy na soubory/podadresáře. Sklad lze indexovat libovolným nástrojem indexace Web prostoru. Problémy: omezení na část prostoru, aplikace přístupových práv, prohledávání podle typu. Mnogosearch open source engine, index v MySQL jako blob, zvládá vícejazyčné mutace, tvary slov, tagování prostoru podle kritérií. By-product - FTP klient Vznikl jako ověření koncepce v.3 za jedno odpoledne. Kompletně včetně implementace protokolu FTP v PHP. Skutečně se uplatnila nová hierarchie tříd. Jediná komplikace bylo doplnění operace rename, bylo třeba reimplementovat protokol FTP tak, aby byl lokální na úrovni instance, ne na úrovni doc_repos (je třeba otevřít dvě nezávislé spojení pro zpracování zdroje a cíle). Kupodivu se stal nejpoužívanějším typem skladu. Sklad v.3.1 + AJAX Operace copy/rename vyžadují jako parametr cílový adresář nebo jméno. Web je na aplikaci rozhraní typu NC příliš těžkopádný odeslání odpovědi na formulář, vygenerování a přenos celého HTML dokumentu trvá příliš dlouho. Je nutno doplnit jednoduchý průchod adresářovou strukturou pro výběr cílového adresáře. Ideální na aplikaci AJAXu (Asynchronous JavaScript and XML) Složitost modifikace Doplnit XML výstup pro doc_getdir() triviální, bude se hodit i pro jiné aplikace. Dynamicky naplněný HTML <SELECT> podle obsahu poslední komponenty cesty zadané v textovém poli: Dovoluje zadat rychle část nebo celou cestu v textovém poli a pak teprve vygenerovat obsah. Zároveň dovoluje průchod stromem bez zadávání, jen myší. 6
Princip Událost onchange seznamu volá JavaScript funkci xml_getdir(): xmlreq = new XMLHttpRequest(); xmlreq.open('get', URL, true); xmlreq.onreadystatechange = xml_finish; Stránka zůstává v prohlížeči zobrazena, požadavek GET se uloží jako samostatný strom a pak xml_finish(): dir = xmlreq.responsexml.getelementsbytagname("dir"); for (var i = 0; i < dir.length; i++) { el = document.createelement("option"); el.value = dir[i].getattribute("name"); el.appendchild(document.createtextnode( dir[i].getattribute('name'))); idselect.appendchild(el); PHP jako objektový jazyk PHP4 lze použít, ale: PHP nemá ukazatel, vše je implicitně předáváno hodnotou, čili vzniká kopie instance objektu (výjimka je $this). Což nevadí, dokud není třeba něco vracet z metody nebo sdílet jednu instanci pro společná data. Pak je třeba použít referenci - & Katastrofa je třeba bedlivě hlídat, kde referenci napsat a kde ne. Nestačí jen v deklaraci funkce, ale je třeba také při přiřazení a volání. Raději PHP5 Implicitně se předávají a přiřazují objekty referencí (pro kopii clone) Není kompatibilní s PHP4 přebytečné reference hlásí jako chybu Omezení viditelnosti (public, private) Konstruktor, destruktor Statické metody a atributy Abstraktní třídy a interface Deklarace tříd parametrů (typová kontrola) Final note Jak správně zacházet s URL (STD 66): vygenerovat správně <a href="něco"> htmlspecialchars(něco) nestačí! prefixuje pouze <, >, &,, ale URL jen A-Z,0-9,-,_,.,~ rawurlencode() nebo urlencode()? ani jedno určeno pro zakódování pouze částí! <scheme>://[<user>[:<pass>]@]<host><location>?[<name> =<value>]{&<name>=<value>[#<fragment>] urlencode() je na <name>, <value>, nahrazuje za + rawurlencode() je určeno na <location>, nahrazuje za %20, ale taky / za %2F, ~ za %7E (je třeba obnovit) 7
Final note Generované URL je třeba obecně rozparsovat na jednotlivé části, parametry CGI na jednotlivé dvojice (jméno, hodnota) a na každou část aplikovat jiný způsob zakódování. Problém URL oddělovače &: v URL je naprosto legální není legální v HTML! (uvozuje entity) nahrazovat v URL vždy & 8