WBCE CMS – Way Better Content Editing.
You are not logged in.
Hallo zusammen!
Ich habe hier einen "interessanten" Effekt/Fehler bei der Installation eines Templates (Caminar).
Vermutlich liegt es nicht am Template selbst, sondern am Core. Deshalb habe ich dieses Unterforum hier gewählt.
Die Erstinstallation klappt vollkommen problemlos.
Wenn allerdings später ein Upgrade des Templates installiert wird, wird es zwar installiert (und lässt sich auch korrekt nutzen), aber der Eintrag in der Datenbank (Tabelle ..._addons) wird nicht aktualisiert. Und so ist die Anzeige im Backend falsch. So scheint es zumindest...
Genauer: Es passiert Folgendes:
* Die Versionsnummer der neu zu installierenden Template-Version wird gegen die Version des Templates auf der Platte geprüft
* Wenn sie größer (also neuer) ist:
* Die Daten aus der info.php der alten(!) Version auf der Platte werden ausgelesen und in die DB geschrieben
* Die neue Template-Version wird korrekt aus der ZIP-Datei in das templates-Verzeichnis ausgepackt
Und so ist die Inkonsistenz da.
Das lässt sich einfach testen, indem vor dem Template-Upgrade in der info.php des Templates auf der Platte manuell ein Datenfeld markiert wird. Ich hatte in "$template_description" einen Kommentar geschrieben. Der landet dann zusammen mit der kompletten alten info.php in der Datenbank/im Backend.
In der zuständigen
wbce/admin/templates/install.php
sieht das von der Reihenfolge her korrekt aus:
[== PHP ==]
// Unzip template to the template dir
$list = $archive->extract(
PCLZIP_OPT_PATH,
$template_dir,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// Delete the temp zip file
if (file_exists($temp_file)) {
unlink($temp_file);
}
// Chmod all the uploaded files
$dir = dir($template_dir);
while (false !== $entry = $dir->read()) {
// Skip pointers
if (substr($entry, 0, 1) != '.' and $entry != '.svn' and !is_dir($template_dir . '/' . $entry)) {
// Chmod file
change_mode($template_dir . '/' . $entry);
}
}
// Load template info into DB
load_template($template_dir);
"Irgendwie" kommt da die Reihenfolge durcheinander, das "load_template()" scheint vor dem Auspacken abzulaufen!?!?!
Ich bin noch nicht schlau draus geworden...
Vielleicht kann zumindest jemand von euch den Effekt reproduzieren. Dann weiß ich zumindest, dass es nicht an meinen Systemen liegt.
(WBCE 1.6.2)
Offline
Die "gute" Nachricht:
ich kann es reproduzieren und es ist definitv unabhängig vom Template (habe es mit "justthefacts" ausprobiert).
Die "schlechte " Nachricht:
keine Ahnung was da schief läuft ...
... nein in Europa verwenden wir beim Programmieren nicht € statt $ ...
Offline
Nach vielem Rumprobieren (mangels echter PHP-Ahnung) habe ich das Problem zumindest ein wenig eingekreist.
In wbce/admin/templates/install.php, die deutschen Kommentare sind von mir zur ergänzenden Erklärung:
Das initiale require() aus dem $temp_dir klappt korrekt (die neue info.php),
ebenso wie folgender require() etwas später die alte, noch aktuelle info.php einliest:
[== PHP ==]
// Check if this module is already installed
// and compare versions if so
$new_template_version = $template_version;
if (is_dir(WB_PATH . '/templates/' . $template_directory)) {
if (file_exists(WB_PATH . '/templates/' . $template_directory . '/info.php')) {
// dieses require() klappt und liest korrekt die alten Daten ein
require(WB_PATH . '/templates/' . $template_directory . '/info.php');
// Version to be installed is older than currently installed version
if (versionCompare($template_version, $new_template_version, '>=')) {
if (file_exists($temp_file)) {
unlink($temp_file);
} // Remove temp file
$admin->print_error($MESSAGE['GENERIC_ALREADY_INSTALLED']);
}
}
$success_message = $MESSAGE['GENERIC_UPGRADED'];
} else {
$success_message = $MESSAGE['GENERIC_INSTALLED'];
}
Aber dieser require() scheint so etwas wie einen "read-only-Effekt" zu haben. Die folgenden requires() kommen nicht zur Geltung:
[== PHP ==]
// Unzip template to the template dir
$list = $archive->extract(
PCLZIP_OPT_PATH,
$template_dir,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// das Test-copy() zeigt, dass nach dem unzip nun tatsächlich die neue Version im Template-Verzeichnis liegt
// $f = $template_dir . '/info.php';
// if (!copy($f, $f.'x')) {
// echo "failed to copy $file...\n";
// }
// ABER die folgenden requires() wirken irgendwie nicht, beide Test-Zeilen nicht!?!?!?!
require($template_dir . '/info.php');
require(WB_PATH . '/templates/' . $template_directory . '/info.php');
// Die alten Werte z.B. in $template_version bleiben erhalten
$admin->print_success("UNZIPPED-1 " . $template_version . " " . $template_dir);
Im etwas weiter unten folgenden
[== PHP ==]
load_template($template_dir);
werden (geplant) die Daten aus info.php wie eben in den Test-requires() eingelesen aber ebenso nur die alten Werte erkannt/erhalten!
Und so steht in der Datenbank jeweils der Datensatz der eben überschriebenen alten Template-Version. Und nicht der neue.
Kapieren tue ich nicht, was da schief läuft. Irgendein Cache-Mechanismus?
Wenn mir noch etwas einfällt, teste ich weiter...
Last edited by chriz (17.05.2024 21:54:45)
Offline
Nein, das ist doch kein "Read-Only-Effekt". Sondern wohl wirklich ein Puffer/Cache, der da irgendwie dazwischen kommt.
[== PHP ==]
// Unzip template to the template dir
$list = $archive->extract(
PCLZIP_OPT_PATH,
$template_dir,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// das copy() zeigt, dass nun tatsächlich die neue Version auf Platte liegt
// lässt sich in einer Shell o.Ä. überprüfen
$f = $template_dir . '/info.php';
if (!copy($f, $f.'-copy')) {
echo "failed to copy $file...\n";
}
// Das Einlesen der Kopie zeigt korrekt die neuen Daten
require(WB_PATH . '/templates/' . $template_directory . '/info.php-copy');
$admin->print_success("UNZIPPED + Kopie " . $template_version);
// Aber beim Einlesen des Originals kommen die Werte aus den alten Dateien, die gar nicht mehr existieren, zum Vorschein
require(WB_PATH . '/templates/' . $template_directory . '/info.php');
$admin->print_success("UNZIPPED-Original " . $template_version);
Faszinierend
Offline
Ich habe nun eine sehr ähnliche Fehlerbeschreibung gefunden:
https://stackoverflow.com/questions/488 … in-php-5-6
... und werde damit weiter forschen.
Treffer!
Eine kleine Änderung in der php.ini lässt alles wieder funktionieren!
; How often (in seconds) to check file timestamps for changes to the shared
; memory storage allocation. ("1" means validate once per second, but only
; once per request. "0" means always validate)
;opcache.revalidate_freq=2
; MODIFIED:
opcache.revalidate_freq=0
Das ist natürlich keine wirkliche Lösung, aber ein Workaround. Und nun scheint die Ursache klar zu sein.
Mehrfaches Nutzen von require() auf dieselbe Datei innerhalb eines Requests schlägt fehl (bzw. liest die alten Daten ein), wenn die Datei im OS verändert wurde, wie es hier im Template/Modul-Install durch das $archive->extract() geschieht.
Last edited by chriz (18.05.2024 10:47:43)
Offline
Slugger, mk70
Und hier eine mögliche Lösung:
Direkt nach dem Unzippen ins Template-Verzeichnis die info.php im Cache invalidieren:
[== PHP ==]
// Unzip template to the template dir
$list = $archive->extract(
PCLZIP_OPT_PATH,
$template_dir,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// Make especially the require('info.php') in load_template() below
// aware of the new contents of info.php!
opcache_invalidate($template_dir . '/info.php');
Dann kann auch die Workaround-Einstellung in der php.ini aus Beitrag #5 wieder weg.
Ich vermute, dass das nicht nur das install.php für die Templates betrifft!
Weitere Kandidaten sind:
* admin/modules/install.php
* admin/languages/install.php
Last edited by chriz (19.05.2024 07:41:47)
Offline
mk70, florian
Danke für den Hinweis und den Lösungsansatz, ich schaue mir das die Tage genauer an.
Sorgen sind wie Nudeln: man macht sich meist zu viele.
Offline
Ich vermute, dass das nicht nur das install.php für die Templates betrifft!
Weitere Kandidaten sind:
* admin/modules/install.php
* admin/languages/install.php
Bei languages wohl eher nicht, dort gibt es ja keine info.php und die entsprechende Sprach .php wird direkt "verwuschtelt".
Last edited by bernd (20.05.2024 19:46:09)
... nein in Europa verwenden wir beim Programmieren nicht € statt $ ...
Offline
Ich vermute, dass das nicht nur das install.php für die Templates betrifft!
Weitere Kandidaten sind:
* admin/modules/install.php
* admin/languages/install.phpBei languages wohl eher nicht, dort gibt es ja keine info.php und die entsprechende Sprach .php wird direkt "verwuschtelt".
Ach ja, direkt verwurschtelt, stimmt. Aber beinahe: :
[== PHP ==]
// require($sFilePath); it's to large
// read contents of the template language file into string
// ...
Allerdings ist in dem languages/install.php auch eine Abfolge drin, die vielleicht überprüft werden sollte:
Und zwar das rename() und kurz dahinter require():
[== PHP ==]
rename($temp_file, $language_file);
// Chmod the file
change_mode($language_file, 'file');
// Load language info into DB
load_language($language_file);
// Restore to correct language
require(WB_PATH . '/languages/' . LANGUAGE . '.php');
Nachtrag: Das gilt (noch) für WBCE 1.6.2. In jüngeren Commits wurde admin/languages/install.php von Florian stark überarbeitet.
Das habe ich gerade erst gesehen...
Last edited by chriz (24.05.2024 16:32:12)
Offline
Vielen Dank für den Hinweis und die weitere Erforschung. Auf so was soll man erstmal kommen...
So weit ich sehe, tritt das Problem nicht grundsätzlich auf, sondern nur auf Webspaces, bei denen opcache aktiviert ist.
Leider gibt es Hoster, die aus Sicherheitsgründen opcache_invalidate deaktivieren (ergoogeltes Beispiel), es bedarf also noch Prüfungen, ob es a) die Funktion gibt und b) diese ausgeführt werden kann (Funktion isClearable ganz unten), da braucht es also doch wieder noch etwas mehr Code drumherum.
Last edited by florian (22.05.2024 07:56:28)
Sorgen sind wie Nudeln: man macht sich meist zu viele.
Offline
Wenn es so viele Konfigurations-Unwägbarkeiten bzgl. opcache gibt, macht es vermutlich mehr Sinn, das Problem zu umschiffen, als zu versuchen, alle Sonderfälle abzudecken.
Ich denke da z.B. an ein etwas anderes Vorgehen beim Entpacken und in die DB Einlesen.
So dass ein möglicherweise aktivierter Cache gar kein Problem darstellt.
Schaue ich mir nachher mal etwas an.
Offline
Hier nun ein Lösungsweg, ohne opcache speziell zu behandeln.
Die Daten für den Eintrag in der DB werden nun aus dem initialen unzip-Verzeichnis gelesen, nicht mehr aus der eventuell durch opcache fragwürdigen Abfrage des template-Verzeichnisses.
Bei der Gelegenheit habe ich auch noch das Aufräumen im Fehlerfall an einer Stelle komplettiert.
Das neue install.php ist angehängt, der diff zum Original ist dies:
root@pi6:/var/www/html/wbce-hhk/admin/templates# diff install-orig.php install.php
144a145,149
> // Remove the temp unzip directory and the temp zip file
> rm_full_dir($temp_unzip);
> if (file_exists($temp_file)) {
> unlink($temp_file);
> }
156,158d160
< // Delete the temp unzip directory
< rm_full_dir($temp_unzip);
<
205,209d206
< // Delete the temp zip file
< if (file_exists($temp_file)) {
< unlink($temp_file);
< }
<
220,221c217,225
< // Load template info into DB
< load_template($template_dir);
---
> // Load template info into DB, but no longer from $template_dir.
> // Instead from original unzipped dir, due to possible opcache issues otherwise!
> load_template($temp_unzip);
>
> // Remove the temp unzip directory and the temp zip file
> rm_full_dir($temp_unzip);
> if (file_exists($temp_file)) {
> unlink($temp_file);
> }
Last edited by chriz (22.05.2024 10:14:53)
Offline
Das neue install.php ist angehängt,
Irgendwie klappt das mit dem Anhängen nicht. Keine Fehlermeldung, aber auch kein Anhang dran...
Nächster Versuch...
Last edited by chriz (22.05.2024 10:19:53)
Offline
Dann halt so:
[== PHP ==]
<?php
/**
* WebsiteBaker Community Edition (WBCE)
* Way Better Content Editing.
* Visit http://wbce.org to learn more and to join the community.
*
* @copyright Ryan Djurovich (2004-2009)
* @copyright WebsiteBaker Org. e.V. (2009-2015)
* @copyright WBCE Project (2015-)
* @license GNU GPL2 (or any later version)
*/
/**
* pclzip_extraction_filter
* PclZip filter to extract only files inside Add-on root path
*/
function pclzip_extraction_filter($p_event, &$p_header)
{
global $addon_root_path;
// don't apply filter for regular zipped WBCE Add-on archives w/o subfolders
if ($addon_root_path == '/.') {
return 1;
}
// exclude all files not stored inside the $addon_root_path (subfolders)
if (strpos($p_header['filename'], $addon_root_path) == false) {
return 0;
}
// remove $addon_root_path from absolute path of the file to extract
$p_header['filename'] = str_replace($addon_root_path, '', $p_header['filename']);
return 1;
}
/**
* find_addon_root_path
* Returns WBCE Add-on root path inside a given zip archive
* Supports nested archives (e.g. incl. Add-on folder or GitHub archives)
* @return string
*/
function find_addon_root_path($zip)
{
// get list of files contained in the zip object
if (($zip_files = $zip->listContent()) == 0) {
return '';
}
// find first folder containing an info.php file
foreach ($zip_files as $zip_file => $info) {
if (basename($info['filename']) == 'info.php') {
return '/' . dirname($info['filename']);
}
}
return '';
}
// do not display notices and warnings during installation
error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING);
// Include required files
require '../../config.php';
require_once WB_PATH . '/framework/addon.precheck.inc.php';
require_once WB_PATH . '/framework/functions.php';
require_once WB_PATH . '/include/pclzip/pclzip.lib.php';
// Setup admin object, skip header for FTAN validation and check section permissions
$admin = new admin('Addons', 'templates_install', false, true);
if (!$admin->checkFTAN()) {
$admin->print_header();
$admin->print_error($MESSAGE['GENERIC_SECURITY_ACCESS']);
}
// Output admin backend header (this creates a new FTAN)
$admin->print_header();
// Check if user uploaded a file
if (!(isset($_FILES['userfile']) && isset($_FILES['userfile']['name']))) {
$admin->print_error($MESSAGE['GENERIC_SECURITY_ACCESS']);
}
// Check write permissions for templates folder
if (!is_writable(WB_PATH . '/templates/')) {
$admin->print_error($MESSAGE['GENERIC_BAD_PERMISSIONS']);
}
// Create unique file within WBCE /temp folder
$temp_dir = WB_PATH . '/temp/';
$temp_file = tempnam($temp_dir, 'wb_');
$temp_unzip = WB_PATH . '/temp/unzip/';
// Move uploaded file to WBCE /temp folder and deal with possible upload errors
if (!$_FILES['userfile']['error']) {
// Try uploading file to WBCE /temp folder
if (!move_uploaded_file($_FILES['userfile']['tmp_name'], $temp_file)) {
$admin->print_error($MESSAGE['GENERIC_BAD_PERMISSIONS']);
}
} else {
// work out error message
$error_code = $_FILES['userfile']['error'];
switch ($error_code) {
case UPLOAD_ERR_INI_SIZE:
$key = 'UPLOAD_ERR_INI_SIZE';
break;
case UPLOAD_ERR_FORM_SIZE:
$key = 'UPLOAD_ERR_FORM_SIZE';
break;
case UPLOAD_ERR_PARTIAL:
$key = 'UPLOAD_ERR_PARTIAL';
break;
case UPLOAD_ERR_NO_FILE:
$key = 'UPLOAD_ERR_NO_FILE';
break;
case UPLOAD_ERR_NO_TMP_DIR:
$key = 'UPLOAD_ERR_NO_TMP_DIR';
break;
case UPLOAD_ERR_CANT_WRITE:
$key = 'UPLOAD_ERR_CANT_WRITE';
break;
case UPLOAD_ERR_EXTENSION:
$key = 'UPLOAD_ERR_EXTENSION';
break;
default:
$key = 'UNKNOW_UPLOAD_ERROR';
}
$admin->print_error($MESSAGE[$key] . '<br />' . $MESSAGE['GENERIC_CANNOT_UPLOAD']);
}
// remove temporary unzip folder if exists to avoid unzip process fails
rm_full_dir($temp_unzip);
// create PclZip object to extract Addon zip archives
$archive = new PclZip($temp_file);
// extract Add-on files into WBCE temp folder
$addon_root_path = find_addon_root_path($archive);
$list = $archive->extract(
PCLZIP_OPT_PATH,
$temp_unzip,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// Check if uploaded file is a valid Add-On zip file
if (!($list && file_exists($temp_unzip . 'info.php'))) {
// Remove the temp unzip directory and the temp zip file
rm_full_dir($temp_unzip);
if (file_exists($temp_file)) {
unlink($temp_file);
}
$admin->print_error($MESSAGE['GENERIC_INVALID_ADDON_FILE']);
}
// Include the templates info file
unset($template_directory);
unset($theme_directory);
require($temp_unzip . 'info.php');
// Perform Add-on requirement checks before proceeding
preCheckAddon($temp_file);
// Check if the file is valid
if (!isset($template_directory)) {
if (file_exists($temp_file)) {
unlink($temp_file);
} // Remove temp file
$admin->print_error($MESSAGE['GENERIC_INVALID']);
}
// Check if this module is already installed
// and compare versions if so
$new_template_version = $template_version;
if (is_dir(WB_PATH . '/templates/' . $template_directory)) {
if (file_exists(WB_PATH . '/templates/' . $template_directory . '/info.php')) {
require(WB_PATH . '/templates/' . $template_directory . '/info.php');
// Version to be installed is older than currently installed version
if (versionCompare($template_version, $new_template_version, '>=')) {
if (file_exists($temp_file)) {
unlink($temp_file);
} // Remove temp file
$admin->print_error($MESSAGE['GENERIC_ALREADY_INSTALLED']);
}
}
$success_message = $MESSAGE['GENERIC_UPGRADED'];
} else {
$success_message = $MESSAGE['GENERIC_INSTALLED'];
}
// Set template dir
$template_dir = WB_PATH . '/templates/' . $template_directory;
// Make sure the template dir exists, and chmod if needed
if (!file_exists($template_dir)) {
make_dir($template_dir);
} else {
change_mode($template_dir, 'dir');
}
// Unzip template to the template dir
$list = $archive->extract(
PCLZIP_OPT_PATH,
$template_dir,
PCLZIP_CB_PRE_EXTRACT,
'pclzip_extraction_filter',
PCLZIP_OPT_REPLACE_NEWER
);
// Chmod all the uploaded files
$dir = dir($template_dir);
while (false !== $entry = $dir->read()) {
// Skip pointers
if (substr($entry, 0, 1) != '.' and $entry != '.svn' and !is_dir($template_dir . '/' . $entry)) {
// Chmod file
change_mode($template_dir . '/' . $entry);
}
}
// Load template info into DB, but no longer from $template_dir.
// Instead from original unzipped dir, due to possible opcache issues otherwise!
load_template($temp_unzip);
// Remove the temp unzip directory and the temp zip file
rm_full_dir($temp_unzip);
if (file_exists($temp_file)) {
unlink($temp_file);
}
// Print success message
$admin->print_success($success_message);
// Print admin footer
$admin->print_footer();
PS: Und nun auch als Pull-Request: https://github.com/WBCE/WBCE_CMS/pull/558
Last edited by chriz (24.05.2024 07:40:11)
Offline
mk70
Zur Zeit schaue ich mir das admin/modules/install.php an.
Es ist nahezu identisch programmiert wie das admin/templates/install.php. Aber der störende Caching-Effekt tritt dort nicht auf!?!??!?!
Ich versteh's noch nicht...
Nachtrag 1:
Doch, in admin/modules/install.php passiert es auch.
Es ist nur nicht so deutlich, weil ausgerechnet die $module_version hier anders behandelt wird:
Schon vor dem Unzip/Caching wird $new_module_version explizit gespeichert und dann später in upgrade_module() per global übergeben und in der DB eingetragen.
Die anderen Felder aus info.php bleiben aber auf den alten/gecache-ten Werten. Also doppelt inkonsistent...
Nachtrag 2:
Ich habe nun auch admin/modules/install.php vergleichbar angepasst.
Damit das klappt, mussten auch framework/functions.php und admin/modules/manual_install.php angepasst werden.
Alles zusammen ist in obigem Pull-Request https://github.com/WBCE/WBCE_CMS/pull/558
Das funktioniert nun (zumindest bei mir).
Allerdings ist der Lösungsweg nicht sehr intuitiv, da nun hier ein $temp_unzip Pfad an framework/functions.php übergeben und dort als $sModulePath weiterverarbeitet wird...
Last edited by chriz (24.05.2024 19:29:41)
Offline
florian
Danke, klappt reibungslos. Hab's gemerged.
Sorgen sind wie Nudeln: man macht sich meist zu viele.
Offline