MultiUpload, jaký add-on nebo třídu použít?

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Joacim
Člen | 229
+
0
-

Zdravím,

chtěl bych se zeptat zda-li používáte nějakou svoji třídu pro hromadné nahrávání souborů (fotek) a nebo používáte některý z addonů, ať již z nette nebo z jiných zdrojů.

Potřebuji multiupload pro fotky (pouze fotky) s tím, že je chci uložit vždy na jedno dané místo a data nahraji do DB (název, velikost, umístění, thumb, popisky, typ souboru), mohly by jste se prosím semnou podělit o zkušenosti, abych to nemusel celé psát od znova ? Nejlépe aby po načtení (ne nahrání) bylo možné přidat popisky ke každému souboru a až poté uložit jak fyzicky na server tak i info do DB. Pročítal jsem forum, ale moc rozumný s toho nejsem. Mám zprovozněnou svoji starou verzi, ale ta není čistě pro nette a ani MVC. Proto by mě zajímalo jak nejlépe něco takového implementovat pro nette. Mám nette 2.2.

Pro dané galerie potřebuji vytvářet i složky které budou mít jméno, ale název bude podle ID z DB

Multiupload Video
Koukal jsem na toto video, ale nevím jaký addon a ani jak jej implementovat a navíc tam nejde přidávat po načtení žádné popisky(možná až po nahrání souboru na server).

Oli
Člen | 1215
+
0
-

bylo možné přidat popisky ke každému souboru a až poté uložit jak fyzicky na server tak i info do DB

Tohle IMHO moc dobře nejde. Jelikož máš multiupload, tak potřebuješ nahrát fotku a potom k ní přidat popisek. Nebo naopak vytvořit popisek a k němu přiřadit fotku. Nevím o tom, že by šlo nějakým způsobem nahrát fotky (které se ve skutečnosti nenahrají), ke každé konkrétní fotce nastavit popisek a pak poslat na server naráz. Ale třeba to jde nějak jednoduše, jen o tom nevím.

Já to dělám tak, že pomocí nějakýho multiuploadu nahraju fotky na server a uložím si základní info k nim do databáze. Potom je možný si k jednotlivým fotkám doplnit informace, které jsou potřeba.

Nejpoužívanější je asi MultiFileUpload od Honzy Kuchaře. To mě ale nevyhovuje, protože to pracuje s databází.

Pokud by jsi chtěl nějakou low level knihovnu pro práci s uploadem, tak můžeš vyzkoušet můj DropzoneUploader. Teprve ho začínám používat, takže to není odladěný, ale zatím mě to funguje. Jde hlavně o to, že to uloží fotky na server a v události si zpracuješ uložení do databáze jak potřebuješ:

protected function createComponentDropzone($name)
{
	$dropzone = $this->dropzone->create();
	$dropzone->onSuccess[] = function (\Oli\Form\DropzoneUploader $dropzone, $targetPath, $name) {
		// save to db
	};
	return $dropzone;
}

A pak je tu ještě knihovna, která řeší asi všechno :-) https://github.com/…istry-Images

Joacim
Člen | 229
+
0
-

Má Nette něco pro vytváření složek na serveru nebo mám použít klasický **mkdir **?

Oli
Člen | 1215
+
0
-

Na vytváření nic nemá, mkdir je dostačující. Má jen na hledání souborů a složek https://doc.nette.org/cs/utils/finder

F.Vesely
Člen | 369
+
0
-

Ma tridu FileSystem

Joacim
Člen | 229
+
0
-

Mohu nějako získat $baseDir pro presenter kde chci vytvářet složky ? A jak nejlépe do modelu přidta DB connection přes injection ?

Editoval Joacim (1. 8. 2015 13:16)

Joacim
Člen | 229
+
0
-

Už mám basePath, problém je v tom, že potřebuji full url jak na localhostu tak i na serveru pro vytvoření adresáře ve složce www/img_gallery/ – jak se to dá nejlépe vyřešit ?

Oli
Člen | 1215
+
0
-

To jsou 2 věci. Pro vytvoření chceš wwwDir, ale pro vykreslení fotky basePath. BasePath je už v šabloně defaultně jako {$basePath}. wwwDir máš v configu. Odpověď na to máš víceméně tady: https://forum.nette.org/…-nebo-appdir#…

Joacim
Člen | 229
+
0
-

Ok, třídu ImageStorage mám jako model.
V config.neon mám:

services:
        securityManager: App\Model\SecurityManager
        router: App\RouterFactory::createRouter
        authorizatorFactory: App\Model\AuthorizatorFactory
        authorizator: @authorizatorFactory::create
        imageStorage: App\Model\ImageStorage(%wwwDir%/img_gallery)

a v prezenteru, kde potřebuji použít ukládání:


namespace App\Presenters;

use Nette;
use App\Model;
use App\Model\ImageStorage;

class GalleryPresenter extends ProtectedPresenter {

    /** @var Nette\Database\Context */
    private $database;

    /** @var ImageStorage */
    private $imageStorage;

    public function __construct(Nette\Database\Context $database) {
        $this->database = $database;
    }

    public function renderDefault() {
        $this->template->all_galleries = $this->database->query('select * from gallery;')->fetchAll();
        $this->template->gallery_cnt = count($this->template->all_galleries);

        if ($this->isAjax()) { // Pokud se jedná o ajax, přerenderujeme galleryList
            $this->redrawControl('galleryList');
        }
    }

    public function handleRenderGalleryAdd(array $gallery_data) {
        if ($this->isAjax()) {


            // Vrať Last ID a vytvoř novou složku podle právě přidané galerie
            $lastId = $insert->id;
            $new_gallery_folder = $this->context->httpRequest->url->basePath . 'img_gallery/' . $lastId . '/';

            // Create gallery folder
            $file = new Nette\Utils\FileSystem();
            $file->createDir($new_gallery_folder); // Zde to vytvoří folder ne do www/img_gallery ale do c:\mvc\www\img_gallery\7\
        }
    }

    public function injectImages(ImageStorage $storage) {
        $this->imageStorage = $storage;
    }

}

a hláška:

Nette\DI\ServiceCreationException

Service of type App\Model\ImageStorage needed by App\Presenters\GalleryPresenter::injectImages() not found. Did you register it in configuration file?

Editoval Joacim (2. 8. 2015 11:01)

Oli
Člen | 1215
+
0
-

A kam jsi dal tu třídu ImageStorage? Někam do app nebo její podsložky? Pokud jo, tak to je divný. Zkus ještě smazat cache. Pokud ne, tak na ni RobotLoader nevidí a musíš ji přesunout někam, kde vidí.

Ještě jedna drobnost: měl by jsi vytvářet složky a soubory na cestě wwwDir/neco. BasePath používej jen pro vypsání. Jde o to, že wwwDir je cesta ve file systemu k souboru, kdežto basePath je url adresa. Pro ukládání chceš file system cestu, ale při zobrazení na stránce url adresu.

Joacim
Člen | 229
+
0
-

s vypsáním pak není problém, horší je to s uložením. Uloženo mám v app/model/imagestorage třídu ImageStorage, gallerypresenter mám v app/presenters/gallerypresenter.

V temp/cache jsem promazal vše, ale výsledek stále stejný

Editoval Joacim (2. 8. 2015 11:15)

Oli
Člen | 1215
+
0
-

Jediné co mě ještě napadá, že vždy pojmenovávám soubor, který obsahuje třídu stejně jako třídu (respektive dělá to za mě IDE) 1:1 včetně velkých a malých písmen. Nevím jestli to na to může mít vliv, ale jinak nevidím nic, co by bylo špatně.

Joacim
Člen | 229
+
0
-

Soubor se jmenuje stejně jako třída, pracuju v PHP Stormu a nebo v NetBeans, taky jsem si to 3* kontroloval, je ještě jiný způsob jak dostat cestu file systému nebo relativní cestu která mě bude fungovat jak na localhostu s WIN tak i na serveru s linuxem? třeba jen registrací cesty v config neon, jelikož nevím kde mám chybu

Nette\DI\ServiceCreationException

Service of type App\Model\ImageStorage needed by App\Presenters\GalleryPresenter::injectImages() not found. Did you register it in configuration file?

Editoval Joacim (2. 8. 2015 11:40)

Joacim
Člen | 229
+
0
-

Už jsem to zprovoznil

    public function injectImages(\App\Model\ImageStorage $storage) {
        $this->imageStorage = $storage;
    }

a

services:
        securityManager: App\Model\SecurityManager
        imageStorage: App\Model\ImageStorage(%wwwDir%\img_gallery\)
        router: App\RouterFactory::createRouter
        authorizatorFactory: App\Model\AuthorizatorFactory
        authorizator: @authorizatorFactory::create

ale pokud použiji

$new_gallery_folder = $this->imageStorage . $lastId;


            // Create gallery folder
            $file = new Nette\Utils\FileSystem();
            $file->createDir($new_gallery_folder);

dostanu

Object of class App\Model\ImageStorage could not be converted to string

navíc, by mě zajímalo jak se to bude chovat pod linuxem když používá obrácená lomítka pro cesty

Joacim
Člen | 229
+
0
-

Použití createDir funguje spolehlivě jak na Win, tak na Linuxu

Tak jsem si stáhnul MultipleFileUpload přes composer, zaregistroval atd:.

Byl by někdo tak hodný a poradil mi jak postupovat nejlépe dál, zda li doinstalovat nějaký template pro lepší zobrazení atd:. nebo je nejjednodušší postupovat podle Ukázky ptám se proto, jelikož je to staré již přes rok a nevím zda li tu někdo nevykoumal něco lepšího

Editoval Joacim (4. 8. 2015 18:37)

Joacim
Člen | 229
+
0
-

Přidal jsem si jkuchar/MultipleFileUpload a dle Ukázky jsem si přidal některé fce:
add.latte

{block content}
<div class="row">
    <div class="col-sm-12">
        <div class="container-fluid">

            <!-- Page Heading -->
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">
                        Galerie
                        <small>Seznam galerií</small>
                    </h1>
                    <ol class="breadcrumb">
                        <li>
                            <i class="fa fa-photo"></i>  Galerie
                        </li>
                        <li class="active">
                            <i class="fa fa-plus"></i> Přidání
                        </li>
                    </ol>
                </div>
            </div>
            <!-- /.row -->

            {form $form}
                <ul class=error n:if="$form->errors">
                    <li n:foreach="$form->errors as $error">{$error}</li>
                </ul>

                <table>
                    <tr n:foreach="$form->controls as $input" n:class="$input->required ? required">
                        <th>{if $input->controlPrototype->type !== checkbox}{label $input /}{/if}</th>
                        <td>{input $input} {if $input->controlPrototype->type === checkbox}{label $input /}{/if}
                            <span class=error n:if="$input->errors">{$input->errors|implode:' '}</span>
                        </td>
                    </tr>
                </table>
            {/form}

        </div>
    </div>
</div>
{/block}

{block scripts}
{include parent}
<script src="{$basePath}/js/jquery-ui.min.js"></script>
<script src="{$basePath}/js/jquery.quicksearch.js"></script>
{/block}

{block head}
<link rel="stylesheet" href="{$basePath}/css/jquery-ui.min.css">
<link rel="stylesheet" href="{$basePath}/css/jquery-ui.structure.min.css">
<link rel="stylesheet" href="{$basePath}/css/jquery-ui.theme.min.css">
{/block}

GalleryPresenter.php


namespace App\Presenters;

use Nette;
use App\Model;
use App\Model\ImageStorage;
use Nette\Application\UI\Form;

/**
 * Description of GalleryPresenter
 *
 * @author
 */
class GalleryPresenter extends ProtectedPresenter {

    /** @var Nette\Database\Context */
    private $database;

    /** @var ImageStorage */
    private $imageStorage;

    public function __construct(Nette\Database\Context $database) {
        $this->database = $database;
    }

    public function renderDefault() {

        if ($this->isAjax()) { // Pokud se jedná o ajax, přerenderujeme galleryList

        }
    }

    public function handleRenderGalleryAdd(array $gallery_data) {
        if ($this->isAjax()) {


            // Insert new gallery to DB


            // Vrať Last ID a vytvoř novou složku podle právě přidané galerie


            // Create gallery folder

        }
    }

    public function handleRenderGalleryDelete(array $gallery_data) {
        if ($this->isAjax()) {

        }
    }

    public function injectImages(\App\Model\ImageStorage $storage) {

    }

    public function renderShow($gid) {

    }

    /**
     * Returns array of suggestions
     */
    public function checkConfiguration() {

        $www = $this->context->expand("%wwwDir%");
        $publicMFUDir = $www . "/MultipleFileUpload/";
        $jsDir = $www . "/js/";
        $messages = array();

        if (!file_exists($publicMFUDir)) {
            $messages[] = "Directory \"www/MultipleFileUpload\" not found! Copy or link content of \"libs/jkuchar/MultipleFileUpload/public\" folder to \"www/MultipleFileUpload\".";
        };

        if (!file_exists($publicMFUDir . "MFUFallbackController.js")) {
            $messages[] = "File MFUFallbackController.js is missing! Is should be in www/MultipleFileUpload. MFU can't work without this file.";
        };

        if (!file_exists($jsDir)) {
            $messages[] = "WARNING: Directory \"www/js\" not found! (trying to check, if jQuery is properly installed)";
        }

        if (!file_exists($jsDir . "jquery.js")) {
            $messages[] = "WARNING: File jquery.js not found! MultipleFileUpload does not work corectly without jQuery.";
        }

        if (!file_exists($jsDir . "nette.ajax.js")) {
            $messages[] = "WARNING: File nette.ajax.js not found! This is needed if you want to use AJAX. (https://componette.org/search/?q=vojtech-dobes%2Fnette-ajax-js)";
        }

        if (!file_exists($jsDir . "netteForms.js")) {
            $messages[] = "WARNING: File netteForms.js not found! This is needed if you want validation of forms.";
        }

        return $messages;
    }

    public function createComponentForm($name) {
        $form = new Form($this, $name);
        $form->getElementPrototype()->class[] = "ajax"; // activate AJAX in https://componette.org/search/?q=vojtech-dobes%2Fnette-ajax-js
        $form->addText("textField", "Text field")
                ->addRule(Form::FILLED, "This is required text field.");
        $form->addMultipleFileUpload("upload", "Attachments");
        //->addRule('MultipleFileUpload\MultipleFileUpload::validateFilled',"You have to upload at least one file!")
        //->addRule('MultipleFileUpload\MultipleFileUpload::validateFileSize',"Files are together too large.",100*1024);
        //$form->addMultipleFileUpload("upload2","Second file uploader");
        $form->addSubmit("send", "Submit your form!");
        $form->onSuccess[] = $this->handleFormSuccess;

        // Invalidace snippetů
        $form->onError[] = array($this, "handleRedrawForm");
        $form->onSuccess[] = array($this, "handleRedrawForm");
    }

    public function handleFormSuccess(Form $form) {
        $data = $form->getValues();
        // Let's pass our data to template
        $this->template->values = $data;
        $queueId = uniqid();
        // Moving uploaded files
        foreach ($data["upload"] AS $file) {
            // $file je instance HttpUploadedFile
            $newFilePath = \Nette\Environment::expand("%appDir%") . "/../uploadedFilesDemo/q{" . $queueId . "}__f{" . rand(10, 99) . "}__" . $file->getName();
            // V produkčním módu nepřesunujeme soubory...
            if (!\Nette\Environment::isProduction()) {
                if ($file->move($newFilePath))
                    $this->flashMessage("File " . $file->getName() . " was successfully moved!");
                else
                    $this->flashMessage("Error while moving file " . $file->getName() . ".");
            }
        }
    }

    public function handleRedrawForm() {
        // This invalidates snippet
        // on AJAX requests this causes redrawing of the form

        $this->invalidateControl("form");
    }

    public function renderAdd($gid) {

    }

}

s tím že pokud se dostanu na stránku ADD (add.latte) obdržím chybu

Undefined variable $form

Jak mám vytvořit komponentu $form ? Mám si zavolat v public function renderAdd($gid) funkci public function createComponentForm($name) ? V ukázce není Presenter pro Components (components/form.latte)

Editoval Joacim (5. 8. 2015 12:34)

Oli
Člen | 1215
+
0
-

public function createComponentForm($name) musí vracet $form. return $form

Joacim
Člen | 229
+
0
-

Přidal jsem do createComponentForm → return $form;
a stále to stejné.

Undefined variable: form

Screen

Editoval Joacim (5. 8. 2015 14:43)

Oli
Člen | 1215
+
+1
-

Jde o tohle

{form $form}

To form má být bez dolaru. Takovou proměnnou si nepředáváš a bere se to z toho názvu proměnný createComponentForm. Uvnitř formu už ten $form asi nevadí, ale nevím, takhle jsem to nikdy nevykresloval :-) Kdyžtak se můžeš podívat do dokumentace jak senormálně vykresluje formulář…

Joacim
Člen | 229
+
0
-

Oli napsal(a):

Jde o tohle

{form $form}

To form má být bez dolaru. Takovou proměnnou si nepředáváš a bere se to z toho názvu proměnný createComponentForm. Uvnitř formu už ten $form asi nevadí, ale nevím, takhle jsem to nikdy nevykresloval :-) Kdyžtak se můžeš podívat do dokumentace jak senormálně vykresluje formulář…

Jo už to funguje jak má, díky

Joacim
Člen | 229
+
0
-

Jak nastavím, abych měl pouze jeden button „Vybrat soubor“ a poté v okně filesystému jsem si vybral třeba 10 obrázků ? Ted tam mám nagenerováno 22* „Vybrat soubor“ což je hnus, myslel jsem si že ti bude již v Multiupload v composeru nebo musím stáhnout další balík jako je uploadify (User Interfaces u článku MultipleFileUploadv1.1.0)?