Fotoalbum v NETTE (flickr style)

Již delší dobu plánuji, že přepíšu dávno nevyhovující fotoalbum oddílu BVÚ.

Nevýhody současného alba:
Viditelné:

  • malé náhledy (generuje je aplikace)
  • v případě aktualizace fotek, není dostupné
  • závislost na datumu souboru (pokud se nepovedlo nahrání, bylo tam nesmyslné datum)

Neviditelné:

  • složité nahrávání fotek (přes již prastarou aplikaci Olympus Camedia Photoalbum)
  • proprietální databáze Olympus Camedia Photoalbum postavená na MS ACCESS 1997, bez rozumné možnosti rozšíření
  • nesnadná správa stránek v prostředí MS – ASP (VB SCRIPT)
  • závislost na datumu souboru – musel se dodržovat speciální postup jinak se o datum přišlo, případně se musel synchronizovat s EXIF informacemi

Co čekám od nového alba:
Vyditelné:

  • rozumné rozhraní odpovídající době
  • možnost větší interakce se stránkami – TOPování fotek, komentáře, TAGy, …
  • větší propojení stránek oddílu (www, stream, fotoalbum)

Neviditelné:

  • sjednocení platforem webů
  • moderní programátorské prostředí (NETTE, jQuery)
  • lepší správa, menší závislost na proprietálním prostředí

Požadavky na nové fotoalbum:
Vyditelné:

  • rychlost
  • přístupnost
  • maximální orientace na prohlížení fotek – je to fotoalbum že!

Neviditelné:

  • minimální závislost na databázi
  • maximální jednoduchost nahrávání fotek

Pro představu uvedu postupy nahrávání fotek:
Postaru:

  • stáhnout fotky v něčem co nemění datumy souborů
  • protřídit
  • zmenšit v něčem co nemění datumy souborů
  • zabalit do ZIP archívu – aby při nahrávání na FTP se nezměnily datumy souborů
  • nahrát archiv na FTP
  • přes vzdálenou plochu na serveru v aplikaci Olympus Camedia Photoalbum vytvořit složku – od otevření aplikace je fotoalbum na webu nedostupné, jelikož si aplikace drží výhradní přístup
  • najít na disku vytvořenou složku (stromová struktura má formát: /ALBUM/001/001/038/019/)
  • nakopírovat fotky z archivu
  • otevřít vytvořené album ve Olympus Camedia Photoalbum – dojde k pomalému vytvoření náhledu a indexaci fotek
  • dříve se ještě v rámci speciální aplikace řešily publikované fotky, to již před časem odpadlo…
  • velké originály popsat a zazálohovat
  • Nově:

    • stáhnout fotky
    • protřídit
    • nahrát na FTP do pojmenované složky (tady ještě uvažuji o použití SAMBA protokolu a ukládání fotek na Windows share, který je pro běžné uživatele dostupnější)

    Úspora kroků je zřejmá!

    Na co se můžete těšit v dalších kapitolách?

    • zpracování fotek – od nahrání až po prezentaci
    • prezentace fotek na celou obrazovku prohlížeče – kdo zná flickr ví o čem píšu

    Migrace aplikace z NETTE 0.9x na NETTE 2.0x

    Potřeboval jsem v jedné starší aplikaci použít dynamické snippety. Po tom co jsem delší dobu hledal jak toto vyřešit ve starém NETTE 0.96, tak jsem na tuto myšlenku nakonec rezignoval a rozhodl jsem se přejít na aktuální verzi NETTE 2.0.6. I když ze začátku jsem se toho bál, tak nakonec to nebylo tak hrozné…

    Co jsem tedy měnil…

    bootstrap.php – ten doznal asi největších změn, ale jedná se spíše jen o přepis než o změnu logiky
    OLD:

    < ?php
    require LIBS_DIR . '/Nette/loader.php';
    require LIBS_DIR . '/dibi/dibi.php';
    require LIBS_DIR . '/currency.php';
    require APP_DIR  . '/models/MyAuthenticator.php';
    require APP_DIR  . '/models/FrontAuthenticator.php';
     
    date_default_timezone_set('Europe/Prague');
     
    //$mode = Debug::DEVELOPMENT;
    $mode = Debug::PRODUCTION;
    Debug::enable($mode, NULL, 'mail@na-me.cz');
    Debug::$strictMode = TRUE;
     
    $loader = new RobotLoader();
    $loader->addDirectory(APP_DIR);
    $loader->register();
     
    // nastavime dibi
    dibi::connect(array(
    	'driver'   => 'mysql',
    	'host'     => 'localhost',
    	'username' => 'user',
    	'password' => 'pass',
    	'database' => 'db',
    	'charset'  => 'utf8',
     ));
     
    $application = Environment::getApplication();
    $application->errorPresenter = 'Error';
    $application->catchExceptions = TRUE;
     
    $router = $application->getRouter();
     
    $router[] = new Route('index.php', array(
    	'presenter' => 'Default',
    	'action' => 'default',
    ), Route::ONE_WAY);
     
    $router[] = new Route('/', array(
        'presenter' => 'Default',
        'action' => 'default',
    ));
     
    $router[] = new Route('admin/<action>/', array(
        'presenter' => 'Admin',
        'action' => 'objednavky',
    ));
     
    // další routy
     
    FormContainer::extensionMethod('FormContainer::addCheckboxList', array('CheckboxList', 'addCheckboxList'));
     
    $application->run();

    NEW:

    < ?php
    require LIBS_DIR . '/Nette/loader.php';
    require LIBS_DIR . '/dibi/dibi.php';
    require LIBS_DIR . '/currency.php';
    require APP_DIR  . '/models/MyAuthenticator.php';
    require APP_DIR  . '/models/FrontAuthenticator.php';
     
    date_default_timezone_set('Europe/Prague');
     
    $configurator = new Configurator;
     
    $configurator->setDebugMode(TRUE);
    $configurator->enableDebugger(dirname(__FILE__) . '/log');
     
    // Enable RobotLoader - this will load all classes automatically
    $configurator->setTempDirectory(dirname(__FILE__) . '/temp');
    $configurator->createRobotLoader()
    	->addDirectory(APP_DIR)
    	->addDirectory(LIBS_DIR)
    	->register();
     
    // Create Dependency Injection container from config.neon file
    //$configurator->addConfig(dirname(__FILE__) . '/config/config.neon');
     
    $container = $configurator->createContainer();
     
    // Setup router
    $container->router[] = new Route('index.php', array(
    	'presenter' => 'Default',
    	'action' => 'default',
    ), Route::ONE_WAY);
     
    $container->router[] = new Route('/', array(
        'presenter' => 'Default',
        'action' => 'default',
    ));
     
    $container->router[] = new Route('admin/<action>/', array(
        'presenter' => 'Admin',
        'action' => 'objednavky',
    ));
     
    // další routy
     
    // nastavime dibi
    dibi::connect(array(
    	'driver'   => 'mysql',
    	'host'     => 'localhost',
    	'username' => 'user',
    	'password' => 'pass',
    	'database' => 'db',
    	'charset'  => 'utf8',
    	'profiler' => TRUE
     ));
     
    FormContainer::extensionMethod('FormContainer::addCheckboxList', array('CheckboxList', 'addCheckboxList'));
     
    $container->application->run();

    Největším oříškem pro mě bylo rozchodit LATTE šablony. Ne že by to bylo v novém NETTE složité ale …
    BasePresenter.php
    OLD

    	// v 0.96 si musíme použití šablony zajistit registrací tohoto filtru
    	public function templatePrepareFilters($template) {
    		$template->registerFilter(new CurlyBracketsFilter);
    	}

    NEW

    	// v novém NETTE jsem myslel, že bude stačit
    	// zakomentovat jeden řádek ($template->register...),
    	// ale to nefungovalo a několik minut jsem nemohl přijít na to proč.
    	// Nakonec pomohlo zakomentovat celou tuto funkci.
    	/*public function templatePrepareFilters($template) {
    		$template->registerFilter(new CurlyBracketsFilter);
    	}*/

    Další změny jsou spíše drobnosti a řešil jsem je jak jsem na ně přicházel:
    $form->addFile() -> $form->addUpload
    User::INACTIVITY -> IUserStorage::INACTIVITY
    $user->setAuthenticationHandler(new MyAuthenticator); -> $user->setAuthenticator(new MyAuthenticator);
    $form->onSubmit[] -> $form->onSuccess[]
    skipFirst() -> setPrompt()
    třída String -> Strings (například Strings::webalize())

    Takže nakonec žádné terno… :)

    Porovnání spořících účtů – ZUNO, AXA, EQUA, AIR BANK

    Již několik let používám spořící účet od AXA BANK. Jelikož se jedná o spoření, kde nepotřebuji dnes a denně bojovat s internetovým bankovnictvím (IB), tak se nedá říct, že ta spousta „maličkostí“, které mě na jejím IB štvou by mě vyváděla z míry…

    Ale…

    Ale najednou se tu začaly jak houby po dešti množit nové internetové banky.

    AIR/BANK
    AXA
    EQUA BANK
    ZUNO

    mBanku záměrhně nezmiňuji, jelikož ji vůbec neřadím jako konkurenci pro její nesmyslné zesložiťování podmínek pro výhodné spoření.

    Tak jsem se začal pídit po tom, co mi která banka nabídne. A jelikož jsem nikde nenašel rozumnou kalkulačku, tak jsem si takovou sestavil.

    Kalkulačka výhodnosti spořících účtů.

    Jak NETTE šetří práci

    Občas potřebuji udělat jen drbku na webu, který na NETTE neběží a pak nastane otázka, zda to dělat ještě v pure PHP a nebo nějak do toho komponovat NETTE. U mě je odpověď jasná. NETTE!

    Zadání:
    Přidat na stávající web formulář pro odeslání mailů.

    Řešení:
    Kdekoliv je libo (někdo plácá HTML s PHP, někdo typy obsahu dělí mezi šablonu a další soubory, …) si vygenerujeme formulář s plnou podporou NETTE.

    include_once $_SERVER['DOCUMENT_ROOT'] . '/_libs/nette.min.php';
     
    use	Nette\Mail\Message,
    	Nette\Forms\Form;
     
    // vytvoření formuláře		
    $form = new Form;
    $form->addText('jmeno', 'Jméno a příjmení *:')
    	->setRequired('Zadejte prosím jméno');
    $form->addText('mail', 'E-mail:');
    $form->addText('tel', 'Telefon:')
    	->addConditionOn($form['mail'], Form::EQUAL, '')
    		->addRule(Form::FILLED, 'Zadejte prosím alespoň jeden z kontaktů');
     
    $form['mail']->addConditionOn($form['tel'], Form::EQUAL, '')
    	->addRule(Form::FILLED, 'Zadejte prosím alespoň jeden z kontaktů')
    	->addRule(Form::EMAIL, 'E-mail nemá správný formát');
     
    $form->addSelect('zajem', 'Mám zájem o:', array('sádrokartony, rekonstrukce' => 'sádrokartony, rekonstrukce', 'malby, nátěry' => 'malby, nátěry', 'ostatní' => 'ostatní'));
    $form->addTextArea('pozn', 'Poznámka:', 40, 4);
     
    // antispam
    // podrobněji toto řešení rozebírám v jiném článku
    $c1 = date('j')+3;
    $c2 = date('N')+2;
    $s = $c1 + $c2;
    $form->addText('soucet', sprintf('Zadejte součet %s+%s *:', $c1, $c2))
    	->setRequired('Je třeba zadat součet jako ochranu proti SPAMu')
    	->addRule(Form::EQUAL, 'Je třeba zadat součet jako ochranu proti SPAMu', $s);
    $form->addHidden('c1', $c1);
    $form->addHidden('c2', $c2);
    $form->addSubmit('send', 'Odeslat');

    Pak kdekoliv chci formulář zobrazit tak napíši jen:

    echo $form;

    Pro JS validaci (není nutná) stačí nahrát a nalinkovat soubor netteForm.js:

    <script type="text/javascript" src="../_js/netteForms.js"></script>
     
    <script type="text/javascript">
    	// řešení pro antispam popisuji jinde
    	document.getElementById("frm-soucet").value = parseInt(document.getElementById("frm-c1").value) + parseInt(document.getElementById("frm-c2").value);
    	document.getElementById("frm-soucet").parentNode.parentNode.style.display = "none";
    </script>

    Zpracování formuláře a odeslání ho na mail jde stejně jednoduše:

    if ($form->isSuccess()) {
    	$val = $form->getValues();
    	$mail = new Message;
     
    	$from = ($val['mail'] == '') ? 'Z webu <web @nejaky-web.cz>' : $val['jmeno'] . ' < ' . $val['mail'] . '>';
    	$mail->setFrom($from)
    		->addTo('Info <info @nejaky-web.cz>')
    		->setSubject('Poptávka z webu')
    		->setBody(sprintf("Dobrý den,\nJméno: %s\nE-mail: %s\nTelefon: %s\nZájem o: %s\nPoznámka:\n%s", $val['jmeno'], $val['mail'], $val['tel'], $val['zajem'], $val['pozn']))
    		->send();
    }</info></web>

    Jak dlouho byste toto dělali v čistém PHP?