Excel (CSV) beheerde Drupal site

Er zijn twee gevallen waarbij je het beheer van inhoud van een Drupal site, beter niet via de Drupal interface uitvoert.
1) Je hebt een site gemaakt, maar je laat het beheer van de inhoud over aan niet-Drupallers.
2) Je hebt grote hoeveelheden nodes om in te voeren. Denk aan stocklijsten van producten die dagelijks worden bijgehouden.
In het eerste geval is het mijn ervaring dat zelfs het opslaan van een Excel bestand naar een CSV UTF-8, voor sommigen een drempel vormt. Ook het gedoe om in te loggen met paswoorden lijkt soms te moeilijk. Ik ben daarvoor bij een expert ter zake geweest: mijn vrouw. Het komt er op neer dat ik een eigen interface heb gemaakt voor computer dummies, los van enige Drupal voorkennis.
In het tweede geval kan je als Drupaller ook gebruik maken van deze interface, met dat verschil dat je weet wat er achter de schermen gebeurt en desnoods de nodige wijzigingen kunt aanbrengen. Vooral bij grote hoeveelheden nodes is deze interface handig.
Het principe achter deze werkwijze is natuurlijk Feeds, gecombineerd met Feeds Tamper. Ze werken via een CSV bestand (best UTF-8). Afbeeldingen moeten reeds op voorhand aanwezig zijn in een map op de server. Ze hoeven echter niet in het Drupal systeem gekend zijn (geen ID nodig), als je in Feeds de juiste verwijzing gebruikt (UUID). Je moet dan wel in je CSV bestand het pad opgeven naar de afbeelding. Spijtig genoeg kan dit niet vanuit je lokale computer. Het moet een map op de server zijn. (dit om veiligheidsredenen.)
Ik heb als case het gekende 'Artikel' inhoudstype gebruikt als test. Er zit een tekstveld, afbeeldingsveld (heb ik meervoudig gemaakt) en een meervoudig labelveld in.
De Feeds verwijzingen staan zo. Let op de details. Full HTML bij de tekst, Autocreate bij de labels, Afbeeldingen vervangen bij de afbeeldingen... De titel moet bij mij uniek zijn...
Met Feeds Tamper doe je de volgende bijsturingen.
Voor het tekstveld maak je dat mogelijke HTML ook zo wordt ingelezen (<b>, </br>, <I>,...). Voor de afbeeldingen ga ik eerst het meervoudig veld opsplitsen (explode) telkens als er een komma staat. Mogelijke spaties doe ik weg (Trim). Tenslotte ga ik een teken (ik neem hier #, kwestie van afspraak) omzetten naar het pad van de afbeeldingsmap. De gebruiker hoeft dus enkel de naam en extensie in te vullen van het bestand. In het Excel bestand zal zo elke afbeeldingsnaam, met een hashtag (#) worden voorafgegaan.
Een detail van de 'Find and replace text':
Bij de labels doen we iets een 'explode' en 'trim'. Sommige mensen gaan een spatie zetten achter een komma zoals in elke tekstverwerker! Met Trim die je die weg.
Het Excel bestand ziet er dan bvb zo uit:
De Feeds instellingen doen nu de rest.
In het Feeds type kies je dit:
en je maakt een Feed van dit type, waarbij je instelt waar de Feed het bestand moet halen.
Ik heb het Feedstype ook zo ingesteld dat reeds geïmporteerde nodes worden verwijderd als er in het nieuwe Excel bestand geen node meer is met dezelfde titel.
Hoe breng je nu de afbeeldingen en het Excel bestand op de server?
Merk op dat ik de termen Excel en CSV door elkaar gebruikt heb. Het is namelijk zo dat ik in de interface voorzie dat je een Excel bestand gebruikt, dat door php automatisch wordt omgezet naar een CSV UTF-8 bestand. Je kunt onderstaande pagina ook in Drupal integreren met de php filter module, anders maak je bvb in kladblok een pagina aan en breng ze naar de server.
Deze code werkt maar als de php spreadsheet librarie actief is. Dit doe je met composer: composer require phpoffice/phpspreadsheet. Na het uploaden wordt het Excel bestand geconverteerd naar een CSV bestand en altijd met dezelfde naam opgeslagen, ongeacht de naam die je uploadt.
Bewerkte versie op basis van deze bespreking:https://phpspreadsheet.readthedocs.io/en/develop/topics/reading-and-writing-to-file/
<?php
if(isset($_FILES['bestand'])){
$errors= array();
$file_name = $_FILES['bestand']['name'];
$file_size =$_FILES['bestand']['size'];
$file_tmp =$_FILES['bestand']['tmp_name'];
$file_type=$_FILES['bestand']['type'];
$file_ext=strtolower(end(explode('.',$_FILES['bestand']['name'])));
$expensions= array("xlsx");
if(in_array($file_ext,$expensions)=== false){
$errors[]="Het bestand moet een xlsx zijn";
}
if(empty($errors)==true){
move_uploaded_file($file_tmp,"sites/default/files/upload/dummysite.xlsx");
echo "Bestand succesvol opgeladen";
}else{
print_r($errors);
}
/* eerst met composer deze bib installeren!!
composer require phpoffice/phpspreadsheet
*/
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("sites/default/files/upload/dummysite.xlsx");
$spreadsheet= $reader->load("sites/default/files/upload/dummysite.xlsx");
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setUseBOM(true);
$writer->setDelimiter(';');
$writer->setEnclosure('');
$writer->setLineEnding("\r\n");
$writer->setSheetIndex(0);
$writer->save("sites/default/files/upload/dummysite.csv");
}
?>
<html>
<body>
<form action="" method="POST" enctype="multipart/form-data">
<p> Het laatste Excelbestand (.xlsx) staat op deze <a href="/sites/default/files/upload/dummysite.xlsx">link</a> en vormt de basis van de inhoud van deze site. </br>Wijzig het naar believen, maar de kolomkoppen moeten dezelfde naam behouden. De namen van afbeeldingen worden voorafgegaan door een #. Meerdere items worden door een komma gescheiden.</br>Elk uur zal de inhoud zichzelf aanpassen aan de nieuwe gegevens. Als je wilt kan je ook met deze <a href="https://fotoalbum.freewebsite.be/cron/RcT9g8RmxRy0qwVT6iZ2CkBN-pHOV5pIhb5_pABvCuOLVOkjUWiSjCs6DcjaN2K-vFHZ0CVJqA">link</a> onmiddellijk de gegevens aanpassen.</p>
<input type="file" name="bestand" />
<input type="submit"/ value="Verzenden">
</form>
</body>
</html>
Het formulier ziet er zo uit. Er werd ook een link voorzien om het het laatste Excel bestand te verkrijgen. De Feeds heb ik zo ingesteld dat er om het uur een cron de inhoud synchroniseert. Voor de ongedurigen onder jullie (=iedereen) is er ook een link die de cron onmiddellijk uitvoert.
Blijft nog het probleem van de afbeeldingen.. Hoe breng je die op de server, zonder enige Drupal tool.. Onderstaande code, laadt de afbeeldingen op en gaat ook resizen als ze te groot (>960 op 720px) zijn. Daarbij wordt er gekeken of het een portet of landschapsafbeelding is. GIF bestanden ga ik niet resizen omdat er soms animaties gebruikt worden die dan verloren gaan.
Afbeeldingen met dezelfde naam en extensie worden overschreven. Ik haat het Drupal gedoe met de -0, -1, -2 nummering. In mijn geval zou dit in het Excel bestand moeten aangepast worden. Te moeilijk. Als je niet wilt dat je afbeeldingen worden overschreven... gebruik een lokale map op je computer waar je de afbeeldingen in bewaart. Zo kan je nooit 2 identieke namen aan een afbeelding geven.
Bewerkte versie van deze handleiding:https://itsolutionstuff.com/post/how-to-upload-and-resize-image-in-php-example.html
<?php
if(isset($_POST['submit'])){
function fn_resize($image_resource_id,$width,$height) {
$verhouding= $width/$height;
$target_width=$width;
$target_height=$height;
if($width>960 || $height>720){
if ($verhouding>0){
$target_width =960;
$target_height =960/$verhouding;
}
else{
$target_width =720;
$target_height =720/$verhouding;
}
}
echo "breedte ".$width." hoogte ".$height." nieuwe breedte ".$target_width." nieuwe hoogte ".$target_height."</br>";
$target_layer=imagecreatetruecolor($target_width,$target_height);
imagecopyresampled($target_layer,$image_resource_id,0,0,0,0,$target_width,$target_height, $width,$height);
return $target_layer;
}
// Count total files
$countfiles = count($_FILES['file']['name']);
// Looping all files
for($i=0;$i<$countfiles;$i++){
$filename = $_FILES['file']['name'][$i];
// Upload file
//move_uploaded_file($_FILES['file']['tmp_name'][$i],'sites/default/files/upload/'.$filename);
// echo "Bestand ". $filename." werd opgeladen</br>";
$file = $_FILES['file']['tmp_name'][$i];
$source_properties = getimagesize($file);
$image_type = $source_properties[2];
//echo "file ".$file. "imagetype ".$image_type;
if( $image_type == IMAGETYPE_JPEG ) {
$image_resource_id = imagecreatefromjpeg($file);
$target_layer = fn_resize($image_resource_id,$source_properties[0],$source_properties[1]);
imagejpeg($target_layer,'sites/default/files/upload/'.$_FILES['file']['name'][$i]);
}
elseif( $image_type == IMAGETYPE_GIF ) {
//$image_resource_id = imagecreatefromgif($file);
//$target_layer = fn_resize($image_resource_id,$source_properties[0],$source_properties[1]);
//imagegif($target_layer,"sites/default/files/upload/".$_FILES['file']['name'][$i]);
move_uploaded_file($_FILES['file']['tmp_name'][$i],'sites/default/files/upload/'.$filename);
}
elseif( $image_type == IMAGETYPE_PNG ) {
$image_resource_id = imagecreatefrompng($file);
$target_layer = fn_resize($image_resource_id,$source_properties[0],$source_properties[1]);
imagepng($target_layer,'sites/default/files/upload/'.$_FILES['file']['name'][$i]);
}
}
}
?>
<form method='post' action='' enctype='multipart/form-data'>
<p>Met dit formulier kan je afbeeldingen uploaden die je wilt gebruiken op de site. Onthou de namen van deze afbeeldingen, want je gaat ze moeten gebruiken in het Excel bestand dat de inhoud beheert. vb mijnafbeelding.jpg, andereafbeelding.png,...</br>
Afbeeldingen (jpeg,png) worden beperkt tot 960 X 720 breedte-hoogte verhouding en automatisch geschaald als ze groter zijn. GIF bestanden worden niet geschaald.</br>
Je moet altijd <b>eerst je afbeeldingen uploaden</b> en pas daarna het Excel bestand.</p>
<input type="file" name="file[]" id="file" multiple>
<input type='submit' name='submit' value='Upload'>
</form>
Zo. Nu heb je 2 formulieren. Eén om de afbeeldingen eerst op de server te plaatsen en te resizen en ook eentje voor het Excel bestand dat naar een CSV bestand wordt omgezet.
Nu heb je dus een volledig Excel gestuurde website (toch voor dit inhoudstype, maar dit kan je uitbreiden). Je kunt dit uittesten op https://fotoalbum.freewebsite.be. Dit staat open voor iedereen om testdoeleinden. Wil je veiliger werken, maak dan dat de URL's van je uploadpagina's complexe strings bevatten. Vind je dit niet veilig? Google Drive werkt ook zo...