Symfony/Sonata Table Tool

Sorry mal wieder an meine deutschen Leser, dieser Artikel ist in Englisch, da er für mehrere Leute interessant sein wird:

======
A collegue of mine just created the Symfony/Sonata Table Tool on GitHub.

This tool is a Access or Filemaker replacement written in PHP using Symfony 2.8 (LTS) and Sonata Admin.

With this tool you can create Tables, Fields and Relations (Foreign-Keys) in a web-Tool. All neccessary files and config-parameters for Sonata Admin are generated. So for table-editing you can use sonata admin with all features (access-lists,…).

 

Open Media Vault: RAID1 OS Platte

So, hab ja schon seit langem mein OMV und bin voll zufrieden. Allerdings hatte meine Lösung noch einen Schönheitsfehler: Die Betreibssystem-Platte war ungespiegelt. Pfui 🙂

Doch zum Glück gibt es diese schöne Anleitung hier. Sieht ja ganz spannend und einfach aus, war es dann leider doch nicht so.

Aber zurück zum Anfang:

Ausgangslage: eine uralte Platte als OS-Platte (150 GB), 2x 1TB als Daten-Platten (brauche aktuell nicht mehr), diese natürlich RAID1, geht ja über die Oberflächer super zu machen.

2 neue Platten bestellt (2x Seagate ST3250312CS, also je 250 GB).

Vorgegangen ziemlich nach Anleitung:

  1. Neue Platte in den Slot 2 von meinem HP Microserver eingebaut (natürlich offline, Server ist leider nicht Hot-Swappable) und Kiste hochgefahren. Soweit kein Problem.
  2. Jetzt von dieser Anleitung die Schritte unmittelbar nach dem Reboot aus Schritt 4 bis so ziemlich vor Schritt 7 gemacht. Wichtig, den Reboot noch nicht machen, da er sonst immer noch von der alten Platte bootet. Vielmehr muss im /boot/grub/grub.cfg folgendes noch angepasst werden:
    1. es gibt eine Zeile wo „insmod ext2“ steht, dort gehören danach noch

      insmod raid
      insmod mdraid1x

      rein (wirklich ein x am Ende von mdraid1x!!)

    2. dieses „search –no-floppy …“ kommt mehrmals in der Datei vor, es gehört an jeder Stelle angepasst auf die neue UUID
    3. es gibt dann noch eine Zeile „linux   /boot/vmlinuz-3.2.0-4-amd64 root=UUID=“ (oder so ähnlich), dort gehört auch die alte UUID durch die neue ersetzt
  3. in der Datei /etc/mdadm/mdadm.conf die Raid-Definition eintragen:

    cp /etc/mdadm/mdadm.conf /etc/mdadm/mdadm.conf_orig
    mdadm –examine –scan >> /etc/mdadm/mdadm.conf

    Wichtig!! Die Datei /etc/mdadm/mdadm.conf unbedingt noch manuell prüfen, ob da eh keine doppelten Einträge am Ende sind!

  4. und bevor jetzt wirklich rebootet werden kann, muss die InitRD neu aufgebaut werden: „update-initramfs -u“ (vgl. diese Anleitung hier). Was sicher auch nicht schlecht ist, den ersten Teil aus der Anleitung von Schritt 7 zu machen, allerdings auf der sdb (also der neuen Platte). Das mit dem fdisk und dieser komischen Zeichenfolge t – 1 – fd – […]. Das setzt den Partitionstyp auf Raid Autodetect

Jetzt sollte er eigentlich booten können und dabei auch das RAID1 als Root-Laufwerk erkennen. Prüfen kann man das über:

df -h

dort gibt es irgendwo eine Zeile die so ähnlich aussieht:

/dev/disk/by-uuid/246b2c9d-b8af-41e6-b45f-c15416c1214f 141G 4,1G 130G 4% /

wichtig ist also das „/“ am Ende, das bedeutet, dass das wirklich als „Root-Laufwerk“ eingehängt wird. Ggf. gibt es meherere Zeilen mit dem Eintrag und nur in einer davon sollte dieses „by-uuid“ stehen.

Die dort angegebene UUID müßste mit „blkid“ verglichen werden können und dort auf das Raid (bei mir: /dev/md0) zeigen.

Falls er beim Booten meint, er findet die Root-Partition nicht, geht ein CLI-Fenster auf. Dort einfach

mdadm –assemble -scan
CTRL-D

rein und dann nochmals die oben angegebenen Schritte prüfen. ggf. gibt es irgendwo einen Tippfehler oder war das „update-initramfs“ nicht sauber durchgelaufen,…

 

Nun kann man mit

grub-install /dev/sdb

den Grub-Bootloader auf die neuen Platte installieren.

 

Anschließend kann man die alte OS Platte ausbauen und die neue in den Slot eins einsetzen. Jetzt sollte er perfekt und fehlerfrei von der neuen Platte booten. Bei mir hat er anschließend sehr lange (15 Minuten) gebraucht, um die Quotas zu prüfen, also nicht schrecken.

 

War auch das erfolgreich, so die neue (zweite) Platte in den Slot 2 einsetzen. Die Kiste sollte immer noch sauber hochkommen. Wir können dann – wie schon zuvor – die Partitionstabelle von sda (der neuen Platte, auf die ja schon das gesamte OS kopiert wurde, aber in Slot 1) auf sdb (der neuen leeren Platte, in Slot 2) übertragen:

sfdisk -d /dev/sda | sfdisk –force /dev/sdb

Und jetzt noch das RAID „wiederherstellen“ (d.h. eigentlich auf die neue 2. Platte im Slot 2 übertragen) (vgl ab Step 8 von dieser Anleitung mal wieder):

swapoff -a
mdadm /dev/md126 -a /dev/sda1
mdadm /dev/md127 -a /dev/sda5

Das kann schon seine Zeit dauern, sieht man aber schön im OMV-Webinterface unter RAID-Verwaltung. Das wars.

eigener SSH-Based Dyndns-Dienst

Hab mich auch anstecken lassen, von diesem DynDNS-Trend und mir meinen eigenen SSH-Based DynDNS-Server gebaut.
SSH-Based heißt hier, dass die Aktualisierung nicht über eine Webseite oder so klappt, sondern dass sich der Rechner mittels SSH anmelden muss und so seine neue IP-Adresse bekannt gibt.

Voraussetzung dafür ist, dass am DNS-Server also ein SSH-Dienst läuft und der DNS-Server von außerhalb erreichbar ist.

Ich hab meine Inspirationen vom „thesysadmin“ geholt, da ich ebenfalls einen Debian-Based DNS-Server einsetze (genauer: einen raspbian :-))

So, was braucht man alles:

– bind9 als DNS-Server

– einen SSH-Server (open-ssh-server)

– einen SSH-Client (linux, oder z.B. auch Putty unter Windows).

 

Los gehts:

Zuerst wie von thesysadmin vorgeschlagen, eine Domäne (oder Subdomäne) für den DNS-Dienst herrichten. Dann die entsprechenden Keys bauen. Ich hab hier auch symetrische Schlüssel (a HMACMD5) erzeugt. Das muss man sogar, sonst gehen die hier vorgestellten Scripts nicht so.

 

Jetzt hab ich auch den Schlüssel in der Datei „/etc/bind/named.keys“ hinterlegt, genau wie bei thesysadmin angegeben.

 

So, damit waren alle Gemeinsamkeiten fertig.

 

Jetzt wird ein neuer User angelegt, nennen wir ihn mal „dynuser“.

Entsprechender Eintrag in der /etc/passwd:

dynuser::1002:1003:DynDNS service:/home/dynuser:/bin/bash

Eintrag in der /etc/group:

dynuser:x:1003:

Homeverzeichnis anlegen, Passwort setzen und Berechtigungen setzen:

mkdir ~dynuser

mkdir ~dynuser/.ssh

passwd -d dynuser

Da die Logik über SSH läuft, hab ich mir da Gewisses von Gitolite abgekuckt:

in der ~dynuser/.ssh/authorized_keys gehört das da rein (alles in eine Zeile!!)

command=“/usr/local/bin/fundd-update test“,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB…………

Nach dem „no-pty“ gehört der „normale authorized_keys“ Eintrag. also AAAB… steht hier für den Public-Key des SSH-Schlüssels.

Nach „fundd-update“ gehört der Hostnamen-Eintrag, in meinem Fall also „test“ für „test.dyn.example.com“.

 

Die Datei „/usr/local/bin/fundd-update“ sieht so aus:

#/bin/sh
#
# fundd-update
#
# starte per sudo das nsupdate
#
# Version
# V1.0, 20140705, rene@pilz.cc, initial-version

# eval my ip
IP=`echo $SSH_CLIENT|cut -f1 -d‘ ‚`

sudo /root/fundd-update-2 $1 $IP

 

Ich rufe also über sudo ein Script auf, dass dann die wirkliche Aufgabe erledigt. Hab mich zu dieser Variante entschieden, da ich so die maximale Flexibilität habe (autorized_keys ruft script auf, dass dann sudo aufruft). Das Script liegt unter root, damit es vom normalen User nicht eingesehen werden kann und es läuft auch als root, damit hat es auf sämtliche Verzeichnisse und Dateien (auch im etc-Verzeichnis) Zugriff.

Hier die /root/fundd-update-2 Datei (Wichtig! das bei SECRET=.. ist eine Zeile!

#!/bin/sh
#
# fundd-update-2
#
# fuehrt das eigentliche update durch
#
# Version
# V1.0, 20140705, rene@pilz.cc, initial-version

DOMAIN=dyn.example.com

echo „set ip for entry $1.$DOMAIN to $2“

# get password for given element
SECRET=`grep -A3 „key $1.$DOMAIN“ /etc/bind/named.keys|grep secret|cut -f3- -d‘ ‚|tr -d ‚“;’`

nsupdate << EOF
server localhost
zone $DOMAIN
key $1.$DOMAIN $SECRET
update delete $1.$DOMAIN.
update add $1.$DOMAIN. 600 A $2
send
EOF

echo „update finished, RC=$?“

Ich „grepe“ mir hier aus der /etc/bind/named.keys-Datei den entsprechenden Key raus. Da dieser Symetrisch ist, ist das auch der key, der für den dnsupdate verwendet wird.

Die anderen Parameter kommen ja aus /usr/local/bin/fundd-update, dort wurde bereits aus der ssh-session die IP-Adresse herausgelöst.

Der Update wieder sehr ähnlich dem von thesysadmin, danke nochmals dafür 🙂

 

Damit das mit dem sudo auch so funtkioniert muss noch das in die /etc/sudoers rein:

dynuser ALL = NOPASSWD: /root/fundd-update-2

 

 

Wie rufe ich das ganze jetzt auf?

Unter Linux:

ssh dynuser@dyn.example.com

Unter Windows, mit Putty:

plink -l dynuser -i keyfile.ppk dyn.example.com

Idealerweise für das keyfile.ppk kein Passwort setzen, damit man den Aufruf auch automatisieren kann.

 

Mein Supercomputer ist angekommen

Heute ist er endlich gekommen, mein Supercomputer. Jetzt hab ich auch so ein Geschoß im  Keller. Mal überlegen, was mache ich damit wohl: Wetterkarten rechnen, Aktienkurse vorhersagen, Raketenflugbahnen verfolgen?

Keine Ahnung, aber hauptsache ich hab einen Supercomputer. Man weiß ja nie, wozu man ihn braucht.

Ist ein  16-Core Adapteva, d.h. ein Kickstarterprojekt, dass jetzt ausgeliefert wurde.

Von der Größe her ungefähr wie der Raspi, nur halt mit einem 16core Coprozessor bzw. 32 Gigaflops peak.

 

Hier das obligatorische Unboxing. In Betrieb geht der – keine Ahnung wann – aber ich werde es sicher posten.

Steam unter Fedora (64bit)

Hab mal wieder was ausprobieren müssen. Nachchdem ich ja erfolgreich ein Game („Broken Age„) auf Kickstarter supported hab, und vor ein paar Tagen mein Steam-Key gekommen ist, wollte ich es gleich ausprobieren.

Steam selbst ist ja bei RPM Fusion dabei, das geht also ganz easy. Nachdem ich das ganze für meine nVidia-Karte eh bereits machen hab müssen, war das alles schon erledigt.

Jetzt einfach Steam installieren und gut ists.

Leider noch nicht ganz, weil wir sind ja unter 64 Bit unterwegs. In meinem Fall musste da noch die 32 Bit GraKa-Treiber installiert werden. Wie gesagt, ich hab nVidia und zwar die properitären aus RPMFusion. Da gibts eine gute Anleitung dazu. Bei mir hat ein

yum install xorg-x11-drv-nvidia-libs.i686

genügt und alles lief super.

Mein Weihnachtsgeschenk

Der Beitrag ist jetzt etwas spät, da es wirklich am 24.12. eingetroffen ist. Also definitiv zu Weihnachten. Montiert hab ich es dann am 25. aber die Unboxing-Freude war gleich am 24. „riesengroß“.

Ich hab mir eine SAT-TV-Streaming Box von TBS gekauft. Und zwar die Moi.

Kann DVB-S und DVB-S2, also alles was man braucht. Inkl. 2 CI-Slots, also da kann man Dekoder-Module reinschrauben ohne Ende. Betrieben wird alles mit TVHeadend (ist aber alles fix und fertig, also wirklich „Plug and Play“).

Die Apps für Android und iPhone tun auch soweit. Also ich kann mir alles ansehen sowie neue Aufnahemen planen. Geplant ist natürlich fast ausschließlich für die Kinder. Na klar, was denn sonst.

Abgelegt werden die Aufnahmen im TS-Format (also riesig) auf meiner NAS-Box. Das klappt auch super. Man hat sogar einen Root-Zugang zur Box, also ohne was zu Rooten,… müssen, kommt man voll drauf. Ich bin aber wirklich noch ganz Clean, d..h nur die neueste Firmware drauf, und dann als Root einen NFS-Mount gesetzt (aber das ist eine Zeile in der /etc/fstab):

nas1:/export/MOI    /mnt/MOI    nfs     nolock 0 0

Damit hole ich mir von meinem NAS (Hostname: nas1) den NFS-Share /MOI als /mnt/MOI rein, perfekt, passt alles. Muss nur mehr als „Dateiablage“ im TV-Headend konfiguriert werden, das ist alles.

Hier die Fotos vom Unboxing:

zweifache Himbeeren

Bin durch Zufall auf google-authenticator gekommen und dachte mir, dass es das wohl auch für den Raspberry geben müßte.

Stimmt natürlich, gibts alles:

http://blog.remibergsma.com/2013/06/08/playing-with-two-facor-authentication-in-linux-using-google-authenticator/

bzw.

http://www.datageeks-msp.com/2013/04/11/using-google-authenticator-on-raspberry-pi/

Ich selbst hab es eigentlich nach der zweiten Variante gemacht und es hat alles auf Anhieb anstandslos hingehaut. Ich glaube, da gibt es einige (bzw. sehr viele) professionelle Lösungen, die nicht so unkompliziert funktionieren. Mal wieder dickes Lob!

 

Universal CSV Import Feature for Sonata-Admin / Symfony 2

Entschuldigung an meine deutschen Leser, diesen Betrag schreibe ich in Englisch, da er sicher auch für „nicht-Deutschsprachige“ Leser interessant ist.

—-

I would like to show you, how to generate a universal CSV-Import-Featore for Sonata-Admin / Symfony 2.
Works at my system without any troubles.

This solution can be used to import the CSV typically generated from the Sonata-Admin Screens. So you can modify the data and load it back to your DB using the GUI.

download-button

This feature uses the CSV-Reader and DB-Writer-Freatures of the ddeboer/data-import-bundle Data Bundle, therefore you have to add this line to „composer.json“:

"require": {
        [...]
       "ddeboer/data-import-bundle": "dev-master"

You can find more info about this bundle here.

I have created a standalone block that can be displayed in the Dash-Screen of Sonata-Admin to easily import the given Data.

csv-block

Step 1: Helper-Class

To easily add new tables to the import-feature, I’ve created a helper class that tells me, what could be imported and to which table to import:

<?php
// Fungus/ShortyBundle/Helper/CSVTypes.php
namespace FungusShortyBundleHelper;

class CSVTypes {
    const RAETSEL             = 1;
    const SPRUECHE            = 2;

    public static function getTypes() {
        return array(
                self::RAETSEL            => 'Rätsel',
                self::SPRUECHE          => 'Sprueche',

        );
    }

    public static function getTypesAndIds() {
        $all=self::getTypes();
        $return=array();
        foreach($all as $key=>$value) {
            $return[]=array("id"=>$key,"title"=>$value);
        }
        return $return;
    }

    public static function getNameOfType($type) {
        $allTypes=self::getTypes();
        if (isset($allTypes[$type])) return $allTypes[$type];
        return " - Unknown Type -";
    }

    public static function getEntityClass($type) {
        switch ($type) {
            case self::RAETSEL:         return "FungusShortyBundle:Raetsel";
            case self::SPRUECHE:        return "FungusShortyBundle:Spruch";
            default: return false;
        }
    }

    public static function existsType($type) {
        $allTypes=self::getTypes();
        if (isset($allTypes[$type])) return true;
        return false;
    }

}

To add another Table, just create a new constant, add a corresponding line to getTypes and add the corresponding Entity-Class to getEntitityClass.

Step 2: The Block for the Dashboard:

{% extends 'SonataBlockBundle:Block:block_base.html.twig' %}

{% block block %}
<table>
    <thead>
        <tr>
            <th colspan="3">CSV-Import</th>
        </tr>
    </thead>

    <tbody>
        <tr>
            <td>Import File directly:</td>
            <td>
                <form action="{{ path('shortyImportCSV') }}" method="post" enctype="multipart/form-data" >
                    <select size="1" name="fileType">
                        {% for oneCSV in allTypes %}
                            <option value="{{ oneCSV.id }}">{{ oneCSV.title }}</option>
                        {% endfor %}
                    </select> <br />
                    <input type="file" name="csvFile" /> <br />
                    <input type="submit" value="Upload" />
                 </form>
             </td>
        </tr>
    </tbody>
</table>
{% endblock %}

This file was saved as src/Fungus/ShortyBundle/Resources/views/Block/block_importCSV.html.twig

As you can see, you need a route called shortyImportCSV to make this working. Here my route:

shortyImportCSV:
     pattern: /admin/csv/import
     defaults:  { _controller: FungusShortyBundle:CSV:importFile }

Be sure to set the path (pattern) below /admin to make sure that the Sonata-Admin Firewall takes care of the access. Otherwise also no user-account is passed to your controller!

Step 3: Show up the block

This has to be done in the config.yml. It is neccessary to define a new service that calls the block:

sonata_block:
     default_contexts: [cms]
     blocks:
         sonata.admin.block.admin_list:
             contexts: [admin]
         fungus.block.service.importcsv: ~
sonata_admin:
     security:
         handler: sonata.admin.security.handler.acl
         # acl security information
         information:
             GUEST:    [VIEW, LIST]
             STAFF:    [EDIT, LIST, CREATE]
             EDITOR:   [OPERATOR, EXPORT]
             ADMIN:    [MASTER]
         # permissions not related to an object instance and also to be available when objects do not exist
         # the DELETE admin permission means the user is allowed to batch delete objects
         admin_permissions: [CREATE, LIST, DELETE, UNDELETE, EXPORT, OPERATOR, MASTER]
         # permission related to the objects
         object_permissions: [VIEW, EDIT, DELETE, UNDELETE, OPERATOR, MASTER, OWNER]

     title: fungus test
     dashboard:
         blocks:
             # display a dashboard block
             - { position: left, type: sonata.admin.block.admin_list }
             - { position: right, type: fungus.block.service.importcsv }
services:
    fungus.block.service.importcsv:
       class: FungusShortyBundleBlockImportCSVService
       arguments: [ "fungus.block.service.uploadCSV", @templating, @service_container ]
       tags:
         - { name: sonata.block }

As you can see, we also need a „Block-Class“ that is called to display the new block, here it is:

<?php

// src/Fungus/ShortyBundle/Block/ImportCSVService.php

namespace FungusShortyBundleBlock;

use SymfonyComponentHttpFoundationResponse;

use SonataAdminBundleFormFormMapper;
use SonataAdminBundleValidatorErrorElement;

use SonataBlockBundleModelBlockInterface;
use SonataBlockBundleBlockBaseBlockService;
use SonataBlockBundleBlockBlockContextInterface;
use FungusShortyBundleHelperCSVTypes;

class ImportCSVService extends BaseBlockService
{

    private $container = null;

    public function __construct($name, $templating, $container=null)
    {
        parent::__construct($name, $templating);
        $this->container = $container;  
    }

    public function getName()
    {
        return 'Import CSV';
    }

    public function getDefaultSettings()
    {
        return array();
    }

    public function validateBlock(ErrorElement $errorElement, BlockInterface $block)
    {
    }

    public function buildEditForm(FormMapper $formMapper, BlockInterface $block)
    {
    }

    public function execute(BlockContextInterface $blockContext, Response $response = null)
    {
        // merge settings
        $settings = array_merge($this->getDefaultSettings(), $blockContext->getSettings());

        $curBlock='FungusShortyBundle:Block:block_importCSV.html.twig';
        if (!$this->container->get('security.context')->isGranted("ROLE_SUPER_ADMIN")) {
            $curBlock='FungusShortyBundle:Block:block_empty.html.twig';
        }

        return $this->renderResponse($curBlock, array(
            'block'     => $blockContext->getBlock(),
            'allTypes'  => CSVTypes::getTypesAndIds(),
            'settings'  => $settings
            ), $response);
    }
}

I’ve added some very-simple security-enhancement: only the ROLE_SUPER_ADMIN’s are able to see this block. The File block_empty.html.twig is just an empty textfile, therefore „nothing“ will be displayed if not in ROLE_SUPER_ADMIN.

Step 4: the Input Controller

Looks like we are nearly finished yet. We just have to add the controller that does the whole work. Thats all.

<?php

// src/Fungus/ShortyBundle/Controller/CSVController.php

namespace FungusShortyBundleController;

use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationTemplate;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use FungusShortyBundleHelperCSVTypes;
use DdeboerDataImportReaderCsvReader;
use DdeboerDataImportSourceStreamSource;
use DdeboerDataImportWorkflow;
use DdeboerDataImportWriterDoctrineWriter;

class CSVController extends Controller
{

    public function importFileAction(Request $request) {

        // Get FileId to "import"
        $param=$request->request;
        $fileId=(int)trim($param->get("fileId"));

        $curType=trim($param->get("fileType"));
        $uploadedFile=$request->files->get("csvFile");

        // if upload was not ok, just redirect to "shortyStatWrongPArameters"
        if (!CSVTypes::existsType($curType) || $uploadedFile==null) return $this->redirect($this->generateUrl('shortyStatWrongParameters'));

        // generate dummy dir
        $dummyImport=getcwd()."/dummyImport";
        $fname="directly.csv";
        $filename=$dummyImport."/".$fname;
        @mkdir($dummyImport);
        @unlink($filename);

        // move file to dummy filename
        $uploadedFile->move($dummyImport,$fname);            

        echo "Starting to Import ".$filename.", Type: ".CSVTypes::getNameOfType($curType)."<br />n";

        // open file
        $source = new StreamSource($filename);
        if ($source===false) die("Can't open filestream $filename");
        $file = $source->getFile();
        if ($file===false)   die("Can't open file $filename");

        // Create and configure the reader
        $csvReader = new CsvReader($file,",");
        if ($csvReader===false) die("Can't create csvReader $filename");
        $csvReader->setHeaderRowNumber(0);

        // this must be done to import CSVs where one of the data-field has CRs within!
        $file->setFlags(SplFileObject::READ_CSV |
            SplFileObject::SKIP_EMPTY |
            SplFileObject::READ_AHEAD);

        // Set Database into "nonchecking Foreign Keys"
        $em=$this->getDoctrine()->getManager();
        $em->getConnection()->exec("SET FOREIGN_KEY_CHECKS=0;");

        // Create the workflow
        $workflow = new Workflow($csvReader);
        if ($workflow===false) die("Can't create workflow $filename");
        $curEntityClass=CSVTypes::getEntityClass($curType);
        $writer = new DoctrineWriter($em, $curEntityClass);
        $writer->setTruncate(false);

        $entityMetadata=$em->getClassMetadata($curEntityClass);
        $entityMetadata->setIdGeneratorType(DoctrineORMMappingClassMetadata::GENERATOR_TYPE_NONE);

        $workflow->addWriter($writer);

        $workflow->process();

        // RESetting Database Check Status        
        $em->getConnection()->exec("SET FOREIGN_KEY_CHECKS=1;");

        // After successfully import, some files need special treatment --> Reset some DB fields
        if ($curType==CSVTypes::SPRUECHE) {
            $q=$em->createQuery("UPDATE FungusShortyBundle:Spruch s
                            SET s.dupeChecked = false");
            $q->execute();
        }

        return $this->render('FungusShortyBundle:CSV:csv_import.html.twig');
    }

}

This controller directly writes to screen (echo). The given twig-template is just a „going back“.

Currently also very little security-checks are done.

Here is the content of csv_import.html.twig:

<hr>
Import done!<br>
<a href='{{ path('FungusMain') }}'>Back</a>

As you can see, the twig for that is very very basic. FungusMain is mainly the root path, thats all.

I did dome tricks with the input-controller:

  1.  $file->setFlags … –> This has to be done to be able to import CRs within one field
  2. $em->getConnection()->exec(„SET FOREIGN_KEY_CHECKS=0;“);  –> This has to be done to be able to overwrite a table that is referenced at another table. Don’t know it that works with an DB other than mySQL
  3. $entityMetadata->setIdGeneratorType(DoctrineORMMappingClassMetadata::GENERATOR_TYPE_NONE); –> This is neccessary to define the ID-Field of the row. See more later.

More about the ID-Field or the setIdGeneratorType:

Normally you can not set the ID of an entity. The DB itself picks out the next free id and uses that. So if you import a csv-file that has also the ID-Field given the following could occur:

  1. The record/entity with the given ID exist –> The Ddeboer-Bundle will just overwritte that entity and all is good.
  2. The record/entity with the given ID _DOES_NOT_ exist –> The Dbeboer-Bundle will let the DB choose a new ID.

So if you import a CSV more than one time that has IDs given but that IDs do not exist in the DB, you will get dublicate entries.

Therefore something described here has to be done:

  1. this setIdGeneratorType has been done by myself
  2. you have to add a „setId“-Method as described here to any of your entities you are enabling to import.

Have fun 🙂

(Comments are welcome 🙂

Himbeeren im Regelbetrieb

Jetzt hab ich es doch tatsächlich geschaft und für meinen Raspberry noch eine Aufgabe gefunden.

Einer meiner alten Server ist schon längst überfällig und kann ersetzt werden. Was für eine Aufgabe für meinen PI, einen 350 MHz-Starken P2 zu ersetzen 🙂

 

Natürlich ist es ganz wichtig, dass der PI ein standesgemäßes Gehäuse und einen entsprechenden neuen Arbeitsplatz hat:

Hier dann ein Foto vom Regelbetrieb:

Und hier jetzt inkl. Bildschirm. Erkennt ihr das Logo links oben 🙂

 

So, noch ein Wort zum OS:

Hab zuerst mal Noob ausprobiert und es ist schon sensationell, was die da hingebracht haben. Funktioniert wirklich sowas von smooth und easy, ist allerdings – wie der Name schon sagt – für Noobs. Das würde mich ja nicht stören, frist aber knappe 2 GB von meiner SD-Card weg.

Also doch raspbian. Auch da gibts nichts zu mäckern.

 

Offtopic: Modellbauhobby.at

Jetzt mal was komplett Offtopic.

Mein Bruder und ich sind gerade dabei, eine Diorama-Seite aufzubauen. Er möchte seine Modellbau-Werke gerne öffentlich herzeigen. Nachdem wir unterschiedlich viel Zeit haben, kann es etwas länger dauern, bis die Seite soweit ist, aber man kann ja immer wieder mal vorbeischauen.