Schlagwort: Tech

  • DITA-Texte anpassen

    Ja, die Zeit der Anpassung ist da 😉

    Mit dem OT kommt ja schon ein Satz an Texten mit Übersetzungen mit. Natürlich passen einem da so manche Texte nicht wirklich und man möchte sie ändern. Oder man möchte vielleicht noch Texte hinzufügen, so wie ich einen umfangreichen, firmenspezifischen Copyright-Text.

    Ich habe hier bisher in der Not die Dateien in DITA_OT/xsl/common entsprechend geändert. Hat funktioniert, hat mir aber nicht so richtig gefallen, weil es eben Original-OT-Dateien sind, die ich editiere.

    Daher fand ich das StringsPluginExample aus dem Files-Bereich der DITA-Yahoo-Group richtig genial und habe auch gleich versucht, das zu installieren (Anleitung). Man kann damit seine eigenen String-Dateien definieren, die dann die des OTs einfach überschreiben. Eigentlich eine einfache Sache…!

    1. Leider ist nach der Installation beim Generieren einfach gar nichts passiert.
    2. Irgendwann stellte ich fest, dass so ein Override erst ab Version 1.4.2 des OT möglich ist.
    3. Kurz darauf versuchte ich bei meinem XMetal nach Anleitung ein OT-Upgrade durchzuführen (und zwar indem man die Variable DITA_OT_DIR auf das Verzeichnis der neuen version setzt). Hat nicht funktioniert.
    4. Dann habe ich wieder festgestellt, dass dieser Parameter mit der OT-Version 1.4.2 nicht funktioniert, aber dass JustSystems einen Patch anbietet.
    5. Mist, der Patch darf nur auf XMetal 5.1 angewandt werden. Und wenn man eine frühere Version hat, soll man doch bitte in Betracht ziehen erst einmal ein Upgrade von XMetal zu machen.

    Kurz: ich mache alles wie bisher und kommentiere meine Änderungen brav. Allerdings finde ich es sehr bedenklich, dass ich ein Tool habe, mit dem ich nicht mal eben so ein Upgrade meiner Architektur durchführen kann. Klar, wenn ich nur in Notepad++ arbeite und über die Kommandozeile publiziere, ist das eher problemlos 😉

    Ich frage mich, ob man das Problem z.B. auch mit Arbortext hat?

  • XHTML-Output anpassen

    Jeder, der mit DITA anfängt, kommt schnell an dem Punkt an dem er das starke Bedürfnis hat, den Output an das eigene Layout anzupassen. Die Standardlayouts im OT sind, sagen wir mal, sehr nüchtern oder auch eher 0.5 als 2.0 😉

    Und in einer Firma will man natürlich Logos und allerlei Schnick-schnack einbauen.

    So, und ich will nun beginnen einen schönen XHTML-Output zu bauen und habe nun damit angefangen die XSL-Stylesheets auseinanderzunehmen, um zu verstehen, was eigentlich wo passiert.

    Schöne Overrides

    Sehr interessant fand ich diese schöne modulare (oh ja, das Wort das jeden technischen Redakteur jauchzen lässt!) Vorgehensweise die eigenen Anforderungen einzubinden. Robert Anderson zeigt in diesem Webinar, dass man eigene Anpassungen immer auslagern sollte, seien es eigene Spezialisierungen oder eben auch „nur“ eigene Layoutdefinitionen. So muss man beispielsweise später keine Bedenken haben, wenn man ein Upgrade auf eine neue OT-Version machen möchte: man hat eben keine Original-OT-Dateien bearbeitet, sondern nur eigene, die in die OT-Dateien nur eingebunden werden.

    Das OT bietet dem User die Möglichkeit sogenannte Overrrides zu definieren, die die Standard-Einstellungen mit den selbst definierten überschreiben.  Diese Overrides werden in Plugins ausgelagert. In der Datei _dita2htmlImpl.xsl_ sind dafür ab Zeile 4166 Templates vorgesehen und zwar für Header, Footer, die head-section,Angaben zu CSS-Dateien und Skripten.

    Eine sehr einleuchtende Vorgehensweise, die schön modular und damit leichter pflegbar ist.

    Ich benutze ja XMetal und hier wurde eine solches Layoutmodul schon mitgeliefert. Wer einmal den Output Single HTML file advanced testet, wird sehen, dass da ein JustSystems-Design hinterlegt ist. Die entsprechenden Dateien findet man je nach Installation ungefähr hier: c:\Programme\Gemeinsame Dateien\XMetaL Shared\DITA_OT\demo\xhtmls.

    Hässliche Overrides

    Umso seltsamer fand ich, dass für einfache Anpassungen im OT-User-Guide ein ganz anderes Vorgehen geschildert wurde: einfach das XSL aus dem OT kopieren, entsprechend ändern und unter anderem Namen abspeichern. Und dann den Output über dieses eigene Stylesheet erzeugen (Quelle). Nun ja, klar das geht. Aber irgendwann wird es nahezu unmöglich sein herauszufinden, was Original-OT ist und was man selbst verbrochen hat. Und Updates sind dann sehr aufwändig.

    Schön oder hässlich? – Kommt drauf an

    So wie ich das bisher verstehe, ist „hässliche“ Variante erst sinnvoll, wenn ich den XHTML-Output komplett anders haben möchte. Dann verbiegt man sich komplett, indem man alle Änderungen in Overrides auslagert. Ich habe beim TWiki genau diese Erfahrung nämlich gemacht. Man kann dort die Default-Styles mit eigenen Styles überschreiben und den Rest übernehmen. Bei einem Update haben die selbstdefinierten Styles damals nicht mehr gut funktioniert, weil man sich in zu vielen Punkten auf Default-Styles verlassen hat und die aber beim Update signifikant verändert wurden.

    Los geht’s

    Bei mir wird leider nicht bei einigen „billigen“ Styleverbesserungen bleiben, sondern ich will strukturell auch einiges anders haben als im Standard-Output und daher versuche ich mal es in einem neuen Stylesheet auszulagern. Es wird spannend 🙂

  • Logfiles in XMetal – Nachtrag

    Einen kleinen Haken hat die Angelegenheit!

    Man sollte nicht aus Versehen das Verzeichnis löschen, das man als Logfile-Verzeichnis angegeben hat. Sonst bekommt man nämlich diese vielsagende Fehlermeldung beim Generieren!


    Tja, ich wusste durch diese Meldung zwar, dass irgendetwas nicht gefunden wurde, aber was?! Nachdem ich mir anschaute, welche Verzeichnisse ich kurz vorher „aufgeräumt“ hatte, wusste ich Bescheid…

    Wer also bei XMetal eine Fehlermeldung zur DitaRenditionsImpl bekommt und gar mit dem Error code -2,146828212, landet hoffentlich auf dieser Seite und erlangt die Erleuchtung. Meine Suche danach hat nämlich nichts ergeben 😉

  • Logfiles in XMetal

    Ich weiß nicht wie es anderen geht, aber bei meiner XMetal-Installation schlägt es fehl, wenn ich mir die die Output Logs anschauen will: There is no valid logfile.

    Ganz beliebt, wenn das Generieren gerade nicht geklappt hat und man gerne auf Fehlersuche gehen würde – grummel!

    Wenn man sich dann eine Weile auf die Suche macht, findet man dann das Logfile irgendwo in den Untiefen des Programmverzeichnisses, was ziemlich nervig ist.

    Umso besser, dass mein Kollege herausgefunden hat wie man das Verzeichnis seiner Wahl für die Logfiles definieren kann, um sie schneller im Zugriff zu haben 🙂

    1. Man wechsele in das Verzeichnis C:\Programme\XMetaL 5\Author\DITA\XACs\shared\ditaoptions\. (Je nachdem ob man XMetal hier natürlich installiert hat… *g*)
    2. Man öffne die Datei XMAXDitaOptions.js.
    3. Man kommentiere die Zeilen 110- 124 aus indem man /** davor setze und ein **/ dahinter:

    4. /**var logDir = basePrintDir + "\\log";

      var tmpDir = fso.GetSpecialFolder(2); //TemporaryFolder
      if (tmpDir) {
      if (!fso.FolderExists(tmpDir.Path+"\\XMetaL")) {
      fso.CreateFolder(tmpDir.Path+"\\XMetaL");
      }
      if (!fso.FolderExists(tmpDir.Path+"\\XMetaL\\renditions")) {
      fso.CreateFolder(tmpDir.Path+"\\XMetaL\\renditions");
      }
      logDir = fso.BuildPath(tmpDir.Path,"XMetaL\\renditions\\log");
      }
      if (!fso.FolderExists(logDir))
      fso.CreateFolder(logDir); **/

    5. Dann die folgende Zeile davor setzen und den Pfad entsprechend den eigenen Wünschen anpassen:
    6. var logDir = "C:\\Eigene Dateien\\DITA\\XMetal\\Logfiles";

    7. Dasselbe Prozedere nun für die Datei XMAUDitaOptions.js ab Zeile 116 ff vornehmen

    Und ab jetzt sollten die Logfiles zumindest leichter auffindbar sein. Das Problem, dass man sie nicht direkt aus XMetal heraus öffnen kann, bleibt erst einmal ungelöst.

  • Ordnung muss sein #2 – SubVersion

    So, ich habe nun eine tolle Ordnerstruktur.  Beim Schreiben mit DITA produziert man viele, viele Topics, also ganz viele einzelne Dateien. Diese Arbeitsweise ist geradezu prädestiniert dazu, die Arbeit auf mehrere Redakteursschultern zu verteilen.

    Tja, und was sagt man gerne über viele Köche und ihren Brei? Ja, man muss die Zusammenarbeit irgendwie steuerbar und kontrollierbar halten. Wenn man nun kein CMS hat, um einem die Verwaltungsarbeit abzunehmen, sollte man zumindest eine Sache auf jeden Fall verfolgen:  Versionsverwaltung. Und damit meine ich keine täglichen Kopien des Arbeitsordners auf irgendein Laufwerk! Das nimmt auf die Dauer zuviel Speicherplatz ein und man hat bei mehreren Autoren keinen Überblick wer was wann editiert hat. Ein paralleles, effizientes Arbeiten ohne Versionsfehler ist dann wirklich schwer.

    SubVersion (SVN) – eine kurze Einführung

    Hier kommt nun SVN in Spiel.  SVN ist eine Open-Source-Software mit der man die Versionen von Dateien und Ordner verwalten kann. Sie kommt vor allem in der Software-Entwicklung zum Einsatz. Jede Änderung einer Datei / eines Ordners wird in einer Version festgehalten, so dass man auch immer wieder auf alte Versionen zurückgehen kann, falls man in der neuen nur Müll produziert hat. Zudem kann über die Versionshistorie auch verfolgen, welche Änderungen gemacht wurden, wer sie wann gemacht hat und, wenn es Kommentare gibt, auch warum.

    SVN funktioniert so, dass man ein zentrales Verzeichnis hat, in dem die allgemein gültige Version der verwalteten Inhalte steckt, in meiner Branche also  die DITA-Dateien.  Das ist das sog. Repository.

    Nun kann sich jeder, sofern Zugriffsrechte vorhanden, eine lokale Arbeitskopie dieses Repositories auf seinen Arbeitsplatzrechner ziehen (auschecken) und anfangen daran zu arbeiten. Das Schöne hierbei ist, dass dies viele Kollegen auf einmal tun können und so parallel an derselben Doku arbeiten können. Am Ende des Tages Kollegin R. mit ihrer Arbeit hochzufrieden und möchten diese in das zentrale Repository laden. Bei SVN heißt das committen.

    Sobald die Änderungen im Repository sind, kann jeder andere Kollegen updaten, sprich: seine lokale Arbeitskopie aktualisieren und hat Kollegin Rs Änderungen automatisch drin. Eine feine Sache, so ist jeder auf dem aktuellen Stand.

    Aber oh weh – als Kollegin T versucht, ihre Arbeitskopie zu aktualisieren, um die Änderungen von Kollegin R in ihre Kopie zu übernehmen (dieser Vorgang des Zusammenführens heißt mergen), wird ihr angezeigt, dass es einen Konflikt gibt. Das heißt soviel wie, dass Kollegin R und Kollegin T an derselben Stelle eine Änderung vorgenommen haben und SVN natürlich nun nicht entscheiden kann, welche Änderung die bessere ist. Deswegen wird es Kollegin T auch angezeigt, damit sie diesen Konflikt irgendwie auflösen kann.

    Ordnung in SVN

    Im Prinzip könnte ich meine Ordnerstruktur direkt einchecken und damit arbeiten. In SVN gibt es jedoch auch noch ein paar Nützlichkeiten, was die Dateiorganisation anbelangt.

    • Tags: Damit wird mir ermöglicht einen bestimmten Stand mit einem Stichwort, also einem Tag, zu versehen, um später die jeweiligen Versionsstände aller beteiligten Dateien, die beispielsweise zu einem bestimmten Release gehören, auf einen Schlag wiederzufinden. Ein Beispiel für einen Tag wäre also die Releasenummer der Software, die man dokumentiert.
    • Branches: Nun kann es passieren, dass man schon längst wieder an der Doku für das nächste Release arbeitet, und es erreicht einen die Nachricht, dass es in der Version für das letzte Release einen happigen Fehler gab, der für die Kunden, die es nutzen, gefixt werden muss. Das ist der Punkt, wo man anfangen würde einen neuen Entwicklungszweig (branch) aufzumachen. Man arbeitet also an einer Stelle noch am Handbuch für das alte Release (im branch) und an der anderen für das neue (im sog. trunk, dem Hauptentwicklungszweig). Das Schöne ist auch, dass es die Möglichkeit gibt, diese zweige zusammenzuführen. Um beim genannten Beispiel zu bleiben: der Fix im alten Release könnte auch für das neue Release vonnöten sein. Hier könnte man dann wieder mergen.

    DITA-Doku in SVN reloaded

    Das heißt, die Ordnerstruktur mit SVN könnte also so aussehen:

  • Ordnung muss sein #1

    So lange man ohne CMS arbeitet, muss man auf die gute alte Ordnerstruktur zugreifen, um seine Dateien abzulegen. Einerseits bietet DITA einem natürlich einen Haufen toller Attribute mit denen man seine Topics filtern kann, andererseits kann man damit auf Dateisystemebene erst einmal nicht so viel anfangen.

    Wieviel Ordnung brauchen wir?
    Mit Ordnern neigt man gerne dazu tiefste Verschachtelungen zu bauen, was irgendwann ziemlich nervig zu navigieren sein kann. Außerdem gilt es zu bedenken, dass man bei DITA Topics auch keine scharfen Grenzen ziehen sollte, was ihre Zugehörigkeit anbelangt, denn man möchte sie ja vielleicht auch an verschiedensten Stellen wiederverwenden.

    Also sollte man sich gut überlegen, welche Selektionskriterien, sprich: Ordnerebenen man tatsächlich benötigt.

    Warum eigentlich nicht alles in einen Ordner schmeißen?
    Keine gute Idee! Alle Topics zu 2 verschiedenen Produkten in einem Ordner können folgende Probleme aufwerfen:

    • Benennungschaos: Es kann durchaus sein, dass man Topics denselben Titel geben muss, aber der Inhalt verschieden ist, weil es sich um verschiedene Produkte handelt, bei denen das „Ordner bearbeiten“ unterschiedlich funktioniert. Natürlich könnte man dann sagen, dass man den Produktnamen in den Dateinamen des Topics mitaufnimmt, aber daraus resultieren dann ellenlange Dateinamen, die verwirren und schlimmstensfalls irgendwelche technischen Probleme auslösen (Zeichenbegrenzung).
    • schlechte Auffindbarkeit:Da haben wir also 5 Produkte und ziemlich wahrscheinlich auch mehrere Autoren dafür. Und dann sind da pro Produkt auch so ca. 100 Topics verfügbar. Auweia! Viel Spaß beim Suchen 🙂

    Ich würde sagen, das spricht dafür, schon mal ein Metadatum Produkt zu definieren 🙂

    Wichtige Metadaten
    Selektionskriterien für die Doku können sein:

    • Sprache: Muss als DITA-Attribut vergeben werden, damit das DITA-OT variable Teile innerhalb des Topics entsprechend lokalisiert („Tip“, „Note“, „Short description“). Für einen Ordner spricht, dass je nach Sprachenanzahl die Topicanzahl wieder zu groß wird. Zudem sind die Screenshots je nach Sprache unterschiedlich
    • Produkt: Siehe oben. Allerdings muss man bedenken, dass es durchaus sein kann, dass es Topics gibt, die für mehrere Produkt gelten, aber nicht für alle.
    • Zielgruppe / Dokumenttyp: Dokumente, die an versch. Zielgruppen gerichtet sind oder auf versch. Dokumenttype, können trotzdem dieselben Dokuteile enthalten. Hier wird es schwierig zu unterscheiden, was man dann in welchen Ordner schmeißen würde.

    Lösungsansatz

    Auf diesem Level stehen als erstes Unterteilungskriterium das Produkt. Zusätzlich gibt es einen common-Ordner, der Topics enthält, die allgemein überall verwendet werden können / sollen.

    Ein Schwachpunkt ist, dass es immer auch Topics geben wird, die für mehrere Produkte gelten, aber nicht für alle. Macht man solche Topics dann trotzdem in _common_ oder in einen der Produktordner? Gute Frage, deren Antwort ich in der Praxis hoffentlich bald beantworten kann

    Das nächste ist die Sprache. Da die meisten Autoren in der Regel in nur einer Sprache arbeiten, sollte dieses Kriterium auch möglichst weit oben platziert sein.

    Auf der nächsten Ebene folgen direkt die Dateien mit den Maps und dann jeweils ein topic-, ein images-, und ein output-Ordner. Mal sehen, wie sich das bewährt. Zumindest bin ich nicht die einzige mit diesem Ansatz 🙂

    (Für das Protokoll: Ganz klar ist es am allerbesten, diese Verwaltungsebene einem CMS zu überlassen, weil man mit echter Metadaten-Vergabe viel flexibler arbeiten kann, als mit Ordnerstrukturen, die einem letztlich abverlangen, ein Topic absolut eindeutig irgendwo zuzuordnen. Aber CMSe sind noch nicht selbstverständlich in der Doku-Welt…)

  • topichead, topicref – Konfusion

    Das Problem mit der Strukturierung, das ich unlängst hatte, ist tatsächlich nicht so einfach zu lösen. Kurzzusammenfassung: Es ging darum zwei Unterkapitel in einem Oberkapitel zusammenzufassen, das selbst aber eigentlich keinen Inhalt enthält.

    title-only topics

    Es gibt einerseits den Ansatz  sog. title-only Topics zu benutzen, die lediglich den Titel und eine short description enthalten. Damit kann man dann die gewünschten „leeren“ Oberkapitel erzeugen. Einige argumentieren hier, dass man so eine <shortdesc> auslesen kann, um sie bei Bedarf anzuzeigen, z.B in einer Online-Hilfe. Das ist für mich schon ein smartes Argument, das dem Nutzer hier und da nützen kann. Ein Topichead hat eben keine Beschreibung.

    Keine unnötigen Topic-Hülsen!

    Jedoch wiegt es für mich schwerer, was für ein Wirrwarr dann auf Erstellerseite entsteht. Ein Topic mit nur einem Titel als Inhalt ist für mich kein Topic, da es kein abgeschlossenes Thema beschreibt o.ä. Punktum! Wenn man mit title-only Topics auch nur anfängt landet ganz man schnell in einem Haufen eigentlich inhaltsleerer Topics und das sollte man meiner Meinung nach tunlichst vermeiden.

    Keine Oberkapitel mehr?

    Andere Stimmen sagen, dass man einfach davon wegkommen sollte, solche „leeren“ Oberkapitel überhaupt zu benutzen. Aber gerade in klassischen Handbüchern finde ich das irgendwie kaum machbar, ohne dass der Nutzer leidet (wer sich für eine längere Diskussion interessiert, bitte hier lang zur YahooGroup).

    Kurzum

    Ich habe immer noch keine Lösung. Ich kann aktuell nur hoffen, dass das <topichead>-Verhalten in einer der nächsten OT-Versionen geändert wird. Aktuell fungiert ein <topichead> eben einfach nur als Zwischenüberschrift, jedoch ohne Link, sowohl im PDF als auch im HTML. Oder ich mach’s halt selber 🙁

  • Links auf Online-PDFs

    Heute: ein großartiger Fehler!
    Ich habe in meinem Topic einen Link auf ein PDF im Internet gesetzt.
    <xref href="http://redakteuse.de/blubber.pdf" format="pdf" scope="external">User Guide</xref>

    Im HTML-Output hat der Link auch super funktioniert, ABER im PDF-Output hat der Link nicht funktioniert.
    Aaah! Ich ahnte Böses in die Richtung, dass ich hier schon wieder geFOPpt werde und damit erst einmal gar nichts tun kann bis ich nicht ein neues PDF-Tool habe. Und so ist es auch. Nun muss ich irgendeinen Workaround finden, der vermutlich so aussehen wird, dass ich auf eine HTML-Seite verlinken muss (das geht nämlich wiederum) auf der der Nutzer dann selbst auf das PDF klicken muss. Ein unschöner, unnötiger Schritt. Aber so sieht es erst einmal aus 🙁

    Das war der Punkt, wo ich mich wieder an die Tugend erinnert habe, die ich dereinst als einsame Redakteurin unter lauter Software-Entwicklern gelernt habe: fluchen, was das Zeug hält, wenn es blöde Bugs gibt 😉

  • Topics strukturieren

    Eine typische Doku-Situation geht so: Man hat zwei Kapitel, die inhaltlich unter einen Oberkapitel passen und um die Struktur übersichtlicher zu machen, packt man die Zweikapitel mit ihren Überschriften unter eine Ober-Überschrift (siehe unten).

    1 Installation
    1.1 blubTunes installieren
    So installieren Sie…bla
    1.2 blobTunes installieren
    So installieren Sie…

    Ein Problem stellt sich mir aber dar, wenn ich so etwas in DITA umsetzen möchte. Ich schreibe also zwei Topic und stelle beim Zusammenstellen zu einer Map fest, dass ich die in einem Inhaltsverzeichnis eigentlich unter eine Ober-Überschrift packen möchte. Was mache ich nun?

    Es ist definitiv keine angebrachte Lösung ein Topic zu erstellen, das bis auf die Überschrift leer ist. Das ist zumindest meine Meinung. Daher bin ich auch der Suche nach einer anderen Lösung, die keinen unnötigen Topic-Overhead produziert.

    Nun gibt es für Maps das Element <topichead>, das in der Map einen Eintrag anlegt, der nur als Titel fungiert und nicht auf ein Topic verlinkt werden muss. Aber momentan sieht das wohl so aus, dass im Inhaltsverzeichnis der Inhalt dieses Elements zwar vorkommt, aber im Textfluss es keine solche Überschrift gibt. Im PDF-Output hatte es sogar gar keine Auswirkung 🙁

    Ich werde hier mal weiterrecherchieren. Mal wieder ein Problem, das ganz sicher sehr viele Redaktionen haben, wenn es um DITA geht, nur Lösungsmöglichkeiten findet man keine…

  • Screenshots ändern leicht gemacht

    Jeder Redakteur für web-basierte Software kennt das Problem, dass die Software, die man zum Doku schreiben benutzt, oft etwas anzeigt, was der Kunde in der Doku so nicht sehen soll.
    Zum Beispiel steht irgendwo noch „Beta-Version“ im Text oder es werden Testdaten angezeigt („Sie sind als Test Testosteron eingeloggt.“). Man macht also einen Screenshot und ändert den Text ziemlich umständlich in Photoshop. Kurz: eines der nervigen Elemente im Redakteursalltag.

    Für die Webentwicklung nutze ich den Firebug schon recht lange. Das ist ein Firefox-Addon mit man dem das HTML, CSS und Javascript einer Seite live editieren kann. D.h. man kann beispielsweise die Farbe ändern und es wird einem direkt im Browser angezeigt, wie sich das auf die Seite auswirkt. Das Add-on ist vor allem zum Fehlerfinden und CSS-Ausprobieren außerordentlich praktisch.

    Und bis heute bin ich nicht drauf gekommen, dass man Firebug eben auch dazu nutzen kann, das ganz oben genannte Problem zu umgehen (zum Glück war einer meiner Kollegen so schlau). Man editiert die Seite im Firebug und ersetzt oder löscht den unerwünschten Text. Ich habe hierzu einfach mal den Header-Text dieses Blogs geändert.

    So kann man direkt einen Screenshot machen bei dem man danach nicht in Photoshop herummachen muss bis man die richtige Schrift und Schriftgröße hat usw.

    Dass ich da nicht früher drauf gekommen bin… Liegt vielleicht auch daran, dass es ne Weile her ist, seit ich das Problem hatte, da ich ja kaum noch Kundendoku schreibe.

    PS: Nein, das ganze gibt nicht für den Microsoft Internet Explorer. Und wer’s noch nicht wusste: der IE ist doof 😉