besagt, dass Klipper die USB-Verbindung zum Drucker verloren hat. Das kann verschieden Gründe haben: Strom-Ausfall, USB-Kabel hat ein Problem, der Drucker ist in einen Fail-State gegangen.
Bei mir hatte sich ein Kabel der Z-Steppers in der Justierung des Druckbetts verfangen und das Bett konnte nicht mehr ganz zurück fahren. Also wenn der Fehler immer wieder während des Drucks auftritt, hat der Drucker wohl ein Problem und das ganz unabhängig von Klipper.
Update: Am Ende musste ich doch das USB-Kabel austauschen. Der USB-A Stecker am Pad rutsch schnell heraus und sitzt nicht wirklich fest beim mitgelieferten Kabel.
Wenn man sich die Anleitungen durchliest, wie man Marlin selbst compilieren kann, muss mn immer VSCode mit vielen Plugins und so installieren. Alles sehr aufwendig. Aber es geht auch viel einfacher. Dank https://github.com/frealmyr/marlin-build kann man es einfach per Docker bauen. Man muss nur auf eine Sache achten: USE_TAG angeben und in docker-compose einkommentieren und die Configs für diese Version nutzen.
# This file is to be used with docker-compose.yml, or sourced before using docker run
BOARD=STM32F103RE_creality
MARLIN_FIRMWARE=./out
MARLIN_CONFIGURATION=./ender3_marlin_config
USE_TAG=2.1.2
meine docker-compose.yml so:
version: "3.5"
services:
build:
container_name: marlin-build
image: frealmyr/marlin-build:latest
user: 1000:1000
stdin_open: true
tty: true
environment:
- BOARD
# - USE_LATEST=true # Use latest git tag
# - USE_REPO=https://github.com/frealmyr/Marlin # USe a different git repo
- USE_TAG
# - USE_BRANCH=bugfix-2.0.x # Use a branch instead of latest tag
# - FW_EXTENSION=hex
volumes:
- $MARLIN_FIRMWARE:/home/platformio/build
- $MARLIN_CONFIGURATION:/home/platformio/CustomConfiguration
# - ./build-marlin.sh:/home/platformio/build-marlin.sh # Use build script in repo instead of image
wie man sieht ist das Github-Projekt auszuchecken optional, die beiden Dateien reichen an sich.
Um nun Marlin 2.1.2 für den Ender 3 mit Creality Board 4.2.7 zu bauen muss man nur noch eines tun:
Manchmal möchte man etwas Auslösen, wenn in der Administration etwas gespeichert wird. Aber auch nur wenn es von dort kommt. Nicht wenn es per CLI oder Storefront ausgelöst wird. Da hilft der Context.
Da ich klammern nutze, um das Glas-Bed zu befestigen gibt es in der y-Ausrichtung einen Puffer am Rand. In der x-Ache muss natürlich der Offset des CR-Touch mit bedacht werden.
Den Filament-Sensor von Creality an einen Ender 3 mit Board Version 4.2.7 anzuschließen ist gerade mit dem Sonic Pad sehr sehr einfach. Man muss nur den Pin heraus bekommen, der zum Glück im Board-Diagramm gut ablesbar ist.
In der printer.cfg sieht es dann so aus:
[filament_switch_sensor RunoutSensor]
pause_on_runout: False #PAUSE is handled by macro
runout_gcode: PAUSE
insert_gcode: RESUME
switch_pin: PA4
Wenn man eigne Snippet-Sets anlegen möchte, bekommt man eine Auswahl an Base-Files angezeigt. Normal sind es die messages.de-DE und die messages.en-GB. Was aber wenn man eine eigene Sprache benötigt, die nicht da und auch nicht im Language-Pack definiert ist? Hier gilt Convention-over-Configuration. Die Snippet-Datei muss einfach nur auf eine bestimmte Art und Weise benannt sein, um als Base-File erkannt zu werden.
Es gibt zwei Komponenten wenn es um die Anpassung oder die Änderungen am Cart oder seiner Items geht. Die Namen sind aber nicht immer klar in der Bedeutung der verschiedenen Schritte.
Collector: Man sammelt hier nicht die zu ändernden Cart-Items sondern die Daten, die für die Änderungen benötigt werden. Also Datenbank, API-Abfragen und Berechnungen gehören hier rein.
Processor: Hier werden die im ersten Schritt gesammelten Daten auf die Cart-Items angewendet. Auch zusätzliche Items hinzufügen sollte hier geschehen. Wichtig ist natürlich eine Prüfung, ob es diese Items schon gibt.
Der Collector wird einmal ausgeführt. Der Processor kann mehr mals ausgeführt werden, abhängig davon wie viele Änderungen so geschehen.
Was passiert wenn man die Berechnungen nicht im Collector macht sondern im Processor aufgrund der vorhandenen Daten in den Items? Spannender Effekt, der einen echt Zeit kosten kann um hinter das Problem zu kommen.
Ein Rabat von 5EUR in pseudo-Code:
item.price = item.price - 5.00;
Nun wird der Processor 6mal ausgeführt. Ein netter Rabatt von 30EUR ist die Folge.
Im Collector sammelt man also im 1. Schritt alle neuen Daten zusammen und setzt diese im 2. Schritt im Processor an den richtigen Stellen (wenn nötig auch mehrmals).
Benutzer abhängige Ansichten oder Aktionen sind in der Shopware 6 Administration sehr selten. Aber gerade auf dem Dashboard kann toll Ansichten und Nachrichten unterbringen, die speziell für einen Benutzer oder eine Benutzergruppe gedacht sind. Aber dafür muss man wissen wer gerade eingeloggt ist, um z.B. ein Criteria mit den richtigen Filtern zu versehen.
Ich weiß, dass als ich die Namen der Trigger zum ersten mal benutzt hatte, ich keine Übersetzungen brauchte und auch etwas verwundert war, dass es einfach so ging. Hat sich nun wohl geändert und es läuft alles nach dem Schema sw-flow.triggers.XXXX.
Also checkout.order.export.success spaltet sich auf in:
Diese Meldung muss nicht wirklich was großes bedeuten, denn es kann sein, dass beim Go-Live einfach erst HTTPS aktiviert wurde und vorher nur HTTP da war.
Die APP_URL ist einmal in der .env zu finden und dann nochmal in der Datenbank in der Table system_config mit dem key core.app.shopId. Dort liegt ein JSON-Object, das auch nochmal die APP_URL enthalten kann.
Diesen Wert zu ändern hat geholfen. Wenn man komplett die Domain wechselt und noch eine Stage-Domain für den Shop bei Shopware einträgt, ist es natürlich etwas anderes.. aber für HTTP/HTTPS Probleme sollte es so ausreichen.
Ein einfacher Weg PHPUnit in einer GitLab-CI Pipeline zu nutzen. An PHP 8.2 arbeite ich noch. Da gab es Probleme mit der Socket Extension. AMQP Extension lief nach einigem Suchen im Internet.
Wenn man den Lieferschein erzeugt, fällt manchmal auf dass das eine Datum sich im Format stark unterscheidet. Es wäre so als würde es eine andere Locale als die anderen benutzen.. oder gar keine.
Der Fehler liegt direkt in der delivery_note.html.twig:
% block document_side_info_contents %}
{{ parent() }}
<tr><td>{% trans with {'%deliveryDate%': config.custom.deliveryDate|format_date('medium', locale=order.language.locale.code)} %}document.deliveryDate{% endtrans %}</td></tr>
{% endblock %}
[code]
Wenn man sich die anderen Datum-Formate in der base oder letter_header Datei anguckt fällt auf, dass hier das Locale nicht aus der Order stammt bzw nicht daraus gelesen wird. Korrekt wäre hier also:
[code]
% block document_side_info_contents %}
{{ parent() }}
<tr><td>{% trans with {'%deliveryDate%': config.custom.deliveryDate|format_date('medium', locale=locale)} %}document.deliveryDate{% endtrans %}</td></tr>
{% endblock %}
Sollte man einfach mal in Cura prüfen, ob die Größe des Beds richtig eingestellt ist. Marlin schien damit keine Probleme auf einem Ender 3 zu haben. Bei Klipper kommt es zu einem Fehler.
Nach dem ich einmal gesehen habe, dass jemand aus dem Meta-Informationen von page sich per über den Meta-Title den Namen der aktuellen Kategorie geholt hat und dieser nur der Name ist solange kein SEO-Title vergeben wurde, hier ein mal der korrekte Weg:
{{ page.header.navigation.active.name }}
Etwas versteckt, aber da findet man die Category-Entity und darin den Namen (natürlich auch mit entsprechender Übersetzung).
Eine Einzelhandels-Weihnachtsgeschichte von Miki D. Bork
Es ist ungefähr 16:30. Die Sonne ist schon komplett untergegangen. Beladen mit viel zu viel Tüten, die auch alle schon viel zu voll sind, hetzt man durch den vorweihnachtlichen Matsch. Die Straßenlaternen spiegeln sich leicht in den nassen Steinen der Fußgängerzone, dann sie werden bei weiten von den grellen Schaufenstern der Geschäfte überstrahlt. Schaufenster säumen den Weg zu den Eingängen der Geschäfte, die nicht direkt an die Einkaufsstraße anschließen sondern weiter nach hinten versetzt sind. In diesen Tunneln, die einen von allen Seiten den Besucher mit voller Gewalt in gleisenden Licht die Vielfalt des Angebots in die Augen hämmern stehen angelehnt an die Scheiben erschöpfte Menschen. Kinder laufen in die Geschäfte und lassen die von allen Eindrücken übersättigten Menschen für einen Moment eine Art von Ruhe erfahren, die eher ein Kapitulieren vor der Gesamtsituation ist. Wo kommen wir her? Wo gehen wir hin? Was ist der Sinn des Lebens? Alle diese Fragen verlieren gerade an Bedeutung. In welchen überfüllten Kaufhaus ist das nächste Klo? Hätte man den langen Weg zum Parkhaus doch antreten sollen, um ein paar der Einkäufe im Auto abladen zu können? Wie lange hat dieses verdammte Parkhaus überhaupt noch auf? Länger als 18:00? Dann grummelt der Bauch. Getrunken hat man auch länger nichts und eine Glasflasche mit Wasser mitzuschleppen, wäre auch zu schwer. Der Schneematsch nähert sich mit jedem einzelnen Kunden immer weiter den automatischen Schiebetüren des Geschäfts.
Während die Kinder in der Spielzeugabteilung die großen Regaler mit den verschiedenen Spielzeugserien begutachten versucht der Partner unbemerkt was zu kaufen. Wenn das Bargeld reicht. Für was zu essen wird es nicht mehr reichen und man muss in eine Bank, um neues Geld zu holen. Vorher noch Kontoauszüge um zu gucken ob man noch im Budget liegt.
Die McDonalds-Filiale platz aus allen Nähten. Die obligatorischen Pizzarien sind auch voll. Unter jeden Tisch stapeln sich die Einkaufstaschen. Das Plastik ist die kleine Grenze zwischen den wertvollen Waren und dem unbändigen Kräften der Natur, der sich als Schneematsch nun auch schon der Pizzaria und den Innenraum der Schuhe der Gäste bemächtigt hat. Das gedämmte Licht und der warme Geruch der Pizza könnte einen nun fast einnicken lassen, wenn nicht diese extreme Lautstärke herrschen würde. Am Nachbartisch verhindert eine Mutter mit viel Geschrei gerade so, dass das Kind auf der Suche nach seinem Einkauf die Geschenke-Tüte öffnet. Der Bruder liest konzentriert in der Anleitung seines neuen Computerspiels. FSK? +-4 Jahre sind sicher ok, solange es nicht FSK18 ist, ist es sicher ok. Zwei Monate später wird er glücklich sein, sich das Spiel gekauft haben, weil es dann doch auf dem Index gelandet ist.
Am Ende steht man im noch kalten in einer endlosen Schlange in Richtung Ausfahrt im Parkhaus und wartet, dass man endlich dort bezahlen und in die Freiheit entlassen werden kann. Sicher versucht gerade jemand das Kleingeld, das vom Weihnachtseinkauf sich in rauen Mengen angesammelt hat, an der mit einem Menschen besetzen Parkhauskasse los zu werden. Eine Münze fällt dabei herunter.
Der Beifahrer befreit sich aus Gurt, Tüten und quetscht sich aus der Tür. Tritt in noch mehr Matsch. Hätte man sich es also sparen können die Schuhe im Parkhaus noch mal sauber zu machen, bevor man ins Auto stieg. Alle denken sich: Scheiß auf die 2 Mark.. aber es sind 2 Mark. Die Uhr interessiert es nicht und sie rückt un erbärmlich weiter und macht den Kindern klar, dass diesen Samstag genau die TV-Serie die letzte Woche mit einer Doppelfolge aufwartete heute nicht zu sehen geben wird. Showview wäre der Retter in der Not gewesen, aber mit dieser endlosen Schlange an Autos, die sich bis auf die oberste Ebene des Parkhauses erstreckt, hatte niemand gerechnet. Wie auch? Eine Frage, die man auf das nächste Jahr
verschiebt, wenn man wieder in dieser Situation ist. Du schließt genervt deine Augen. Dieses CBD-Schlafspray macht echt intensive Träume. Leider sehr teuer, aber ein paar hatten sowie noch gefragt, was du dir zu Weihnachten wünscht. Praktisch. Du musst denen nur noch kurz den Link schicken.
Daten per Criteria abfragen und in einer Table oder einer Form darzustellen oder verarbeiten ist in der Shopware 6 Administration extrem einfach und fast zu 100% genau so wie in PHP. Aber manchmal will man auch auf eine sehr spezielle Art oder Weise Daten erhalten und diese dem Benutzer direkt als Download anbieten können.
Hier ein kleines Beispiel eines CSV-Downloads der von einem API-Controller mit der Route "/api/example-module/download/csv/{id}" kommt. Man kann aber genau so gut alles per Criteria abfragen und das CSV in JavaScript bauen.
Die Methode für den Download sieht mit Controller-Lösung so aus:
Das Prinzip ist an sich sehr einfach. Man baut sich ein a-Element mit gesetzten download-Attribute und simuliert einen Click darauf. Nur hinterlegt man hier die Daten als Data-URL und nicht die URL zum Controller direkt. Das würde nicht funktionieren als dann dort die Authorization-Infos im GET-Header fehlen würden.
Die Data-URL kann eben aus Daten von einem Controller gebaut werden oder direkt aus Daten, die JavaScript aggregiert wurden.
Sehr viel einfacher als die alten Lösungen mit FileSaver-Lib und viel hin und her wandeln von Blobs.
Falls man seinen Shopware 6 in einem Shared-Hosting Paket von Cyon (Schweiz) betreibt, ist man es ja schon fast gewohnt, dass es Probleme beim Bauen der Administration über CLI gibt. Momentan gibt es wohl Probleme mit Puppeteer. Die Lösung hier ist es vor eine entsprechende Umgebungsvariable zu setzen:
export PUPPETEER_SKIP_DOWNLOAD='true'
Dann geht es wieder.. nicht schnell aber das war es dort ja noch nie.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! administration@1.0.0 build: `mode=production webpack`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the administration@1.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /var/www/.npm/_logs/2022-11-24T11_18_35_338Z-debug.log
sollte man gucken, ob vorher ein Fehler dieser Art aufgetreten ist
Während die Intenetnutzung im ICE mit dem Smartphone ohne Probleme geht, kann es mit Linux schnell zu Problemen kommen. Das liegt am IP-Bereich und Docker. Zwar kann sich das Gerät mit Linux ohne Probleme mit dem eigentlichen Netzwerk verbinden, aber dann kann die Seite zum bestätigen der AGBs nicht geladen werden.
Anleitung: - Mit dem WLAN verbinden
- prüfen welcher IP-Bereich zu gewiesen wurde
-
ip addr
- gucken welcher Eintrag mit dem Bereich kolliedert
- diesen Eintrag entfernen
-
Meine erste Lösung war es den MD5-Hash als Dateiname zu nehmen. Dann später kam wollte ich doch lieber ein CustomField dafür nutzen (wie ich auch schon in der Shopware 5 Version ein Attribute genutzt hatte). Funktionierte alles super bis ich dann auf ein System kam, das nicht im dev-Mode lief. Am fileName hängt wohl mehr als man denkt und ich habe gelernt, dass man in einem produktiven Shopware 6 nicht den fileName ändern sollte. Kommt mit auf meine Liste der ganz großen Shopware 6 Nein-Nein's.
Eine wirklich gutes Upgrade für den Creality Ender 3 ist das Spider Hotend des selben Herstellers. Es gibt auch eine V2.0, die von der Größe besser passen sollte, als Ersatz des Stock-Hotends. Aber die 1.0 Version ist auch schon sehr gut und bedeutend günstiger.
Mit dem Hotend ist PETG Druck bei 100-120mm/s bei 245 Grad kein Problem mehr mit dem Ender 3 (Pro). Da das Hotend aber insgesamt höher ist würde der Lüfter nun gegen den Heizblock blasen und nicht mehr aus das frischgedruckte Filament. Es gibt bei Thingiverse Ersatz, der aber bei mir am Ende zu dicht über dem Filament und auch zu dicht an der Nozzle war. Deswegen habe ich dieses Design etwas angepasst. Gedruckt mit Resin (weil der Ender 3 ja gerade im Umbau war). Funktioniert super und auch wenn man etwas Filament vom Bed im ersten Layer abstehen zieht es nicht damit direkt den gesamten Layer wieder ab.
Oft will man einfach nur den Status der Stellung anpassen, weil irgendwas mit der Bestellung nicht stimmt oder man sie auf abgeschlossen setzen will, aber der Rest über das ERP/die WaWi läuft. Man will keine Mail an den Kunden senden. Leider ist die Mail immer direkt aktiviert und man muss immer daran denken diese zu deaktivieren.
Nervig.. also ein tolles kleines (wirklich kleines) Plugin-Projekt:
die Art und Weise erinnert mich etwas wie ich damals bei meinen cJSv2-Framework die Controller an Elemente gebunden und initialisiert habe.. der default-Name der Init-Methode war da auch init.
Der DomAccess-Helper ist super und sollte man so verwenden. Über den kann man auch den QuerySelector nutzen und dass schützt hoffentlich davor, dass ein Haufen klassischer Webdesigner versuchen JQuery wieder da rein zu bringen. Der HttpClient ist auch nett aber man kann genau so gut direkt mit fetch() arbeiten, wenn man nicht unbedingt irgendwelche Steinzeit Browser supporten möchte. Aber besonder mag ich von den Helpern DeviceDetection.isTouchDevice() weil man damit einfach unterschieden kann ob man gerade ein Touch oder ein MouseEnter
Event nutzen muss.
Wenn man in der Storefront an einem eignem JavaScript-Plugin arbeitet ist es beim Debuggen, oft nervig, dass der JavaScript Code nicht direkt lesbar ist. Oft weiß man beim eigenen Code direkt was los ist, aber gerade wenn 3rd Party Plugins auch mit rein spielen, kommt man nicht drum herum sich Exceptions mit lesbaren Stacktrace anzeigen zu lassen.
Das ist zum Glück extrem einfach:
* Port 9998 aus dem Docker-Container durchleiten
* Dem SalesChannel in dem man entwickelt die Domain "localhost" zu ordnen
* das Script bin/watch-storefront.sh starten
Nun kann man localhost:9998 im Browser aufrufen. Der JavaScript Code ist lesbar und Änderungen werden sofort übernommen ohne dass man die Storefront neu bauen muss.
Einmal schnell gucken, wie viele und welche Art von Produkten über welchen SalesChannel und URL zu kaufen sind.
SELECT x.url, 'single' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children < 1
group by x.url
union all
SELECT x.url, 'main' type, count(x.id)
FROM (
select d.url, p.id, p.parent_id, count(p2.id) children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
group by d.url, p.id, p.parent_id
)x
WHERE x.parent_id is null and x.children > 0
group by x.url
union all
SELECT x.url, 'variant' type, count(x.id)
FROM (
select d.url, p2.id, p2.parent_id, 0 children from sales_channel_domain d
join sales_channel sc on d.sales_channel_id = sc.id
join product_visibility pv on pv.sales_channel_id = sc.id
join product p on p.id = pv.product_id
left outer join product p2 on p.id = p2.parent_id
where sc.active = 1
)x
group by x.url
Gerade in Migrations kommt man manchmal in darum herum mit den UUIDs arbeiten zu müssen. Da es binary-Daten sind muss man etwas mit den anstellen, um die aus Shopware 6 bekannte Darstellung zu erreichen und auch um diese wieder die in Datenbank zu bekommen.
Lesen der Id: Um die aus Shopware 6 gewohnte Darstellung zu bekommen muss man in MySQL die HEX-Function verwenden. Zusätzlich muss der String noch in Kleinbuchstaben umgewandelt werden.
Schreiben der Id: Zuerst wieder alles in Großbuchstaben umwandelnt. Dann muss mit der MySQL-Function UNHEX der Hex-String wieder in binary Data umgewandelt werden.
Es bleibt zwar etwas umständlich, aber ist damit durch aus handhabbar. Da es in dem Sinne kein Autoincrement gibt für die UUIDs hilft dann dort Uuid::randomHex().
Ich arbeite nun doch schon einige Zeit mit Shopware 6. Während ich am Anfang vielen Konzepten etwas kritisch gegenüberstand, bin ich nun doch sehr von fast allen Dingen überzeugt. Ich habe viele verschiedene Dinge schon mit Shopware 6 realisiert und einiges wäre in Shopware 5 nicht so einfach gewesen.
Gerade Vue.js in der Administration ohne irgendwelche zusätzlichen Lizenzen nutzen zu können ist super. Ich mochte immer Vue + Bootstrap und Symfony. Also am Ende fühle ich mich so was von extrem in meinen Vorlieben bestätigt... wenn auch Shopware diese Kombinationen nutzt muss ich ja schon immer richtig gelegen haben :-)
Varianten sind immer noch viel zu kompliziert. Daten in das Model für Emails rein zubekommen ist wirklich viel zu umständlich. N:M Relation in DAL ist umständlich bzw wie früher mit puren SQL. Entweder alles vorher löschen oder sich merken was genutzt wird und alles was nicht dazugehört löschen. Aber am Ende kommt man ja gut damit klar... ist eben wie mit JDBC oder PDO direkt zu arbeiten und dass habe ich lange genug gemacht.
So.. aber was kann man alles mit Shopware 6 so alles bauen? Ich habe bis jetzt nur für Kunden direkt entwickelt und falls jemand etwas hier von gerne hätte, geht das leider nur über Anfrage und dann wird ein Angebot erstellt.
1. Adressänderungen verhindern Wie schon bei Shopware 5 war es nötig, weil SAP sonst überfordert ist.
2. Register-Form erweitern Das zu Erweitern war am Ende sehr viel einfacher als gedacht und ich nutzt einfach das Data-Mapping Event für den Customer. Der richtige Weg? Für mich funktioniert er gut.
3. Blog Einträge Das kostenlose Blog-Plugin ist schon echt super. Es fehlt nur ein Flag um Posts von er Suche auszuschließen, Typen, Rechte über Rules und Datei-Anhänge. Ja.. ich weiß.. ich könnte mich da beteiligen und alles einbauen.. sollte ich wirklich machen. Aber bis jetzt war nur Zeit das Plugin zu erweitern um Rezepte aus einem Panipro-System zu importieren.
Typen sind natürlich dynamisch und es werden nur Typen auf der linken Seite angezeigt, die auch gefunden wurden.
4. Bonuspunkte und passende Produkte Produkte die man nur oder auch gegen Bonuspunkte kaufen kann. Das war etwas komplexer und man musste über Collector und Processor des Carts eingreifen. Heute würde ich wohl einiges ein wenig anders machen, aber nicht viel und auch nur minimale Änderungen.
5. Konfigurator Style #1 So kann man sich z.B. Geschenkkörbe oder PCs/Notebooks konfigurieren. In der Administration kann man sich ein Config-Preset anlegen und es verschiedenen Products zuordnen.
6. Konfigurator Style #2 Ein Produkt das z.B. Hackfleisch beinhaltet so konfigurieren in welchen Formen man das Hack gerne geliefert bekommen würde. Wurde als Teil von Punkt 7 entwickelt.
7. Paket verkauf Erst alle Pakete verkaufen und dann erst das Tier schlachten. Spart Lagerkosten und andere Aufwände. Hier habe ich gelernt warum Varianten noch immer umständlich sind und CustomFields zu syncen zwischen Varianten schlechter ist als eine eigene Entity dafür zu erstellen. Aber die Anforderungen waren zuerst so das CustomFields die einfacher Lösung waren.
Etwas was bei Shopware 6 so komplett fehlt und wo ich schon mehrmals gehört habe, dass es Kunden/Benutzern Probleme macht, ist die Hauptvariante bei Shopware 6. Die Hauptvariante ist immer die Variante, die im Dialog beim Erzeugen der Varianten zuerst gewählt wurde. Es gibt danach keine Möglichkeit mehr es zu ändern.
An sich ist die Hauptvariante aber auch nur ein FK am Product. Man kann dort einfach eine andere Variante eintragen und schon hat man seine neue Hauptvariante. Also alles auf DB-Ebene sehr einfach.
Auch das in die Administration einbauen war dann relativ einfach. Man brauchte nur zusätzlich ein Repository und eine Criteria für Varianten (WHERE parent_id = ... ). Die Id kann man direkt per v-model setzen am Product.
Gerade wenn man ein Produkt konfigurieren kann, ist es wichtig, dass CartItems nicht einfach aufaddiert werden, sondern jede Konfiguration als eigenes CartItem im Warenkorb abgelegt wird. Das CartItem von Shopware 6 hat auch ein stackable-Flag. Nun könnte man glauben, wenn man dieses auf false setzt, dass nicht das vorhandene CartItem geändert sondern ein neues angelegt wird, wenn erkannt wird, dass das vorhandene nicht stackable ist. Falsch! Man bekommt eine Exception.
Die Lösung ist zum Glück sehr einfach. Man muss selbst die Id des CartItems ändern um ein neues anzulegen. Also wenn man setStackable(false) setzt auch gleich die Id neu setzen. Oder man baut sich ein allgemeines Plugin, dass es macht.
BeforeLineItemAddedEvent:
if (!$event->getLineItem()->isStackable()) {
$event->getLineItem()->setId(Uuid::randomHex());
}
Manchmal möchte man aus einem Tomcat heraus Processe und Programme aufrufen. Wie bei SQL-Injections muss man das natürlich stark absichern, aber das Konvertieren von Bildern und Video geschieht meistens auf diese Art und Weise.
Was aber wenn der Tomcat per ProcessBuilder nichts mehr aufrufen kann außerhalb seines Verzeichnisses, obwohl er alle Recht haben sollte? Wenn es ein System mit systemd ist (z.B. ein Ubuntu) ist, kann es einfach eine Security-Einstellung sein, die den Aufruf verhindert.
Hier muss man die Start-Config für den Service anpassen. "ProtectSystem=false" ist zum Prüfen der Lösung ganz gut, sollte aber später durch eine genauere Anpassung der ReadWritePaths geöst werden.
Bei Shopware 6 ist es etwas anders das Platzhalter-Bild für Produkte ohne Bilder zu ersetzen. An sich ist es ganz einfach, wenn man weiß wie es geht. Auch Anleitungen aus dem Internet von 2019 sind nicht mehr aktuell, denn es geht nun noch einfacher!
DAs Bild ist ein SVG-Icon mit dem Namen 'placeholder'. Man kann mit der Suche schnell herausfinden, wo es überall verwendet wird. Nun muss man nur ein eigenes Icon in per Plugin einbringen. Das ist auch an sich sehr einfach. Man legt einen Pfad in seinem Plugin an "src/Resources/app/storefront/dist/assets/icon/myiconpack/" und kopiert dort seine SVG-Datei rein. Die SVG kann auch gerne als placeholder.svg bennant sein. Nun muss man den Twig-Tag "sw_icon" noch um zwei Werte ergänzen. "pack" ist der oben im Pfad genutzte Icon-Pack Name und der "namesapce" ist einfach der Name des Plugins in dem man sich gerade befindet.
Einfach mal die Meta-Description mit einem Prefix versehen, um kleinere SEO-Probleme zu beheben. Klingt einfach und führt schnell zur Verzweiflung. frontend/detail/header.tpl angepasst und es funktioniert nicht. Keywords ändern ist kein Problem nur die Meta-Description des Artikels will sich nicht ändern.
Lösung Wie man schnell vermutet wird die eigene Änderung überschrieben. Der Schuldige ist das SEO-Plugin und da muss man die index.tpl anpassen. Die Änderung kann z.B. so aussehen:
Manchmal will man nur ein paar Bilder für einen Webshop sortieren und ganz unerwartet hat man ein Kunstwerk geschaffen ... naja vielleicht keine echte Kunst.. aber sowas was eine leichte Mischung aus Warhol und Frank Miller sein könnte. Mit viel Fantasie.
Wenn man Landingpages oder Kategorien als Banner in einem Bannerslider verlinkt würde man meistens gerne auch einen Text darin unterbringen. Nur ein Bild trifft oft nicht genau den Begriff den man darstellen möchte, da in einem Shop alles ja doch sehr nahe am gleichen Thema ist.
Text in Bannern bringt aber einige Probleme mit sich:
1) Der Text skaliert nicht mit und wird schnell unschön abgeschnitten
2) Der Text läßt sich nicht einfach und schnell ändern
3) Das Styling des Textes ist nicht mehr änderbar
4) Man braucht oft einen Grafiker der den Text in das Bild einbaut
Genau diese Problem hat gerade Vape-Buddys und brauchte eine einfache, schnelle und kosten günstige Lösung. Diese Lösung besteht darin den Title des Banners als Text-Overlay über das Bilds zu legen. Hier wird auch alles über % positioniert
und passt sich dem Viewport an. Sollte der Text zu groß sein greift CSS ellipsis und schneidet den Text schön ab.
An sich ist es garnicht sooo schwer Nuxeo mit einem Keycloak zu verbinden und dann die Benutzerverwaltung allein über das Keycloak abzuwinkeln. Leider ist die Dokumentation dazu sehr dürftig und zu großen Teilen einfach veraltet und lückenhaft. Hier wird einmal in kurzer Form erklärt wie man das mit einer aktuellen Version von Nuxeo 10.10 bewerkstelligen kann. Man sollte das 10.10 Repository von Github einmal per Maven komplett selbst gebaut haben. Wir hatten die HF53-Version und ein Grundsetup als Docker-Image ist unter annonyme/nuxeo:HF53 zu finden. Besser ist aber wenn man sich das vollständig selbst baut. Das Docker-Repository hilft beim Bauen.
Die Erweiterung für Nuxeo
Das Repository für die Nuxeo Platform Login Keycloak Erweiterung ist Teil des Nuxeo Mono-Repository und kann direkt mitgebaut werden. Die Anleitung dazu ist vollkommen veraltet, aber ich nehme sie hier als Basis. Man braucht um dieses benutzen zu können:
Die Dateien aus der Zip der Adapter-Dist, die JAR vom Nuxeo Platform Login Keycloak sowie die JAR des UserMapper Services müssen alle in das selbe plugin/ Verzeichnis kopiert werden wie in der Anleitung erklärt wird. Das config/ Verzeichnis wie im Repository einfach auch rüber kopieren. Der Inhalt der JSON-Datei kann direkt aus dem Nuxeo kopiert werden und
ist die Config-Datei für den Keycloak Tomcat-Adapter und hat also an sich nichts mit Nuxeo zu tun. Dem entsprechend ist die Dokumentation zu der Datei auch um Welten besser als bei den Nuxeo Komponenten.
In der Anleitung wird alles in ein Template-Verzeichnis kopiert. Ein Template ist ein Profile für verschiedene Nuxeo-Konfigurationen und es können mehrere davon gleichzeitig verwendet werden. Den Docker-Container muss man dann also mit NUXEO_TEMPLATES: docker,keycloak starten.
Das war es dan nauch. Beim Login in Nuxeo einfach einen Account aus dem Keycloak verwenden und der Benutzer sie wie die im Keycloak zugeordneten Rollen/Gruppen werden ins Nuxeo übernommen.
Wenn man nochmal mit dem Administrator-Konto ins Nuxeo will und dieser noch nicht im Keycloak angelegt ist, muss man nur direkt /nuxeo/login.jsp aufrufen und bekommt die Nuxeo-Anmeldung ohne auf die Realm-Anmeldeseite des Keycloak weiter geleitet zu werden.
Es sind keine weiteren Konfigurationen an Nuxeo nötig. Wenn man sich ein Docker-Image baut muss also nur die keycloak.json aus dem config-Verzeichnis des Templates ersetzt werden können.