Blog ...

In dem Blog dokumentiere und beschreibe ich zum einen die Technik hinter dieser Webseite. Zum anderen findet Ihr auch noch ein paar Beiträge zu anderen, meist technischen, Themen (Cloud, Azure, Python, PV ...). Wenn euch nur bestimmte Themenbereiche interessieren, dann könnt Ihr die Themen entweder über das dropdown-menü einschränken oder aber die Suche verwenden ... Zeige 5 Stories pro Seite von 16

Das erstellen von Mail-Rules in Exchange shared mail-in accounts

O365, Outlook, Rules, Mail-In

Das erstellen von Server basierten Regeln ist in Exchange/Outlook nicht ganz so einfach wie in Notes/Domino, daher hier einmal kurz die Schritte die hierzu notwendig sind.

Wenn man in EXO die Berechtigung auf eine Mail-In hat, dann ist die default Einstellung diese, dass diese automatisch im Outlook Client eingebunden wird. Unter dem Menue „Files“ kommt man zur Einrichtung von Regeln.

Wenn eine shared mail ueber den Automatismuss eingebunden wurde, dann wird der Account nicht in den Rules angezeigt.

Die shared Mail-In muss wie ein Mail-Account eingerichtet werden.

Beim Login Dialog wird dann der Name der Shared Mailbox eingegeben und mit Connect bestaetigt.

Jetzt kommt die Aufforderung zur Passworteingabe. Da eine Shared Mailbox kein Passwort hat, muss der Account genommen werden, welcher Zugriff auf diese hat. Also hier auf „Sign in with another account“ auswaehlen.

Wenn das funktioniert hat, dann kommt eine Erfolgsmeldung.

Nach einem Neustart von Outlook kann man nun den account der Shared mailbox in den Rules auswaehlen und diesem dann entsprechen auch neue Regeln zuweisen. Dies funktioniert wie auch beim eigenen Account in Outlook.

Der rules wizard.

Nach dem erstelllen der Rule und dem benamen.

Beim speichern der Rule ist es nun wichtig, das man die Serverauswahl trifft und somit die Rule auf dem Server fuer alle gleich ausgefuehrt wird und nicht nur auf einem Client wenn hier das Outlook gestartet ist.

Automatisiertes erstellen von Pins auf Pinterest via RSS

Domino, Pinterest, RSS, Social

Ich hatte ja bereits die Moeglichkeit geschaffen Bilder von meiner Seite via Agent auf Pinterest zu teilen. Fuer mich als Seitenbetreiber ist das aber zu umstaendlich, bzw. moechte ich ja auch bestimmen was genau und in welchem Board das ggf. auftauchen soll. Auch moechte ich ggf. wissen welche Bilder von meiner Seite aus gepint wurden.

Pinterest bietet hier zwei Moeglichkeiten Pins automatisiert zu erstellen. Das eine ist eine CSV-Datei (welche aber wohl nur fuer Video-Pins verwendet werden kann) oder aber via RSS-Feed. Da ich aber eh schon einen RSS-Feed fuer die Seite habe und hiermit schon etwas Erfahrung habe, waehle ich diesen Weg. Dieser ist auch weit aus angenehmer, da er viel besser automatisiert werden kann.

Fuer die Zuordnung zu einem Board habe ich hierzu ein Form erstellt, welches den Boardnamen erfasst und noch ein paar technische Felder.

Das Form enthaellt zugleich die benoetigten RSS-Formatierungen. Es wird ueber eine entsprechende URL aufgerufen. Der WebQueryOpen-Agent generiert das eigentliche XML beim oeffnen und schreibt es in das RichText-Feld.

board = StrRight(doc.getItemValue("Query_String_Decoded")(0), "=")
	
	Set locView = db.Getview("HV8020")
	Set pinDC = pinView.Getalldocumentsbykey(board, true)
	Set pinDoc = pinDC.Getfirstdocument()	
	
	rssString = ""
	
	Do While Not pinDoc Is Nothing
		Set locDoc = locView.Getdocumentbykey(pinDoc.Getitemvalue("loc_unique")(0), true)
		
		If Not(locDoc Is Nothing) Then
			pTitle = "" & locDoc.Getitemvalue("searched_type")(0) & ": " & locDoc.Getitemvalue("gs_name")(0) & "" & Chr(13)
			pLink = "https://www.workm.de/WV5500?OpenView&RestrictToImage=" & pinDoc.Getitemvalue("uniqueID")(0) & ""  & Chr(13)
			pDesc = "" & locDoc.Getitemvalue("Description")(0) & ""  & Chr(13)
			tX = StrRightBack(pinDoc.Getitemvalue("Subject")(0),"\")
			pEncl = ||  & Chr(13)
			pPubD = "" & pinDoc.Getitemvalue("pinPublished")(0) & ""  & Chr(13)
			pGUID = || & pinDoc.Getitemvalue("uniqueID")(0) & ||  & Chr(13)
			pCat = "" & pinDoc.Getitemvalue("laTop")(0) & "" & Chr(13)
			pItem = "" & pTitle & pLink & pDesc & pEncl & pPubD & pGUID & pCat & "" & Chr(13)
		End If
		
		rssString = rssString + pItem	
		
		Set pinDoc = pinDC.Getnextdocument(pinDoc)	
	Loop
	
	If (rssString = "") Then
		rssString = ""
	End If
		
	' Call doc.Replaceitemvalue("socialArg", arg)
	Set smItem = New NotesRichTextItem(doc, "RSS") 
	Call smItem.Appendtext(rssString)

Wenn man dann das entsprechende Dokument im Browser aufruft sieht das ganze so aus. Das ganze habe ich natuerlich ueber einen Validator mal abgeprueft.

Nun kann ich bei Pinterest in meinem Account den RSS-Feed entsprechend konfigurieren.

Die Zuweisung der Bilder erfolgt nun im Notes indem ich eine Verknuepfung der Bilder zu dem jeweiligen Board-Dokument erstelle. Dadurch wird der RSS-Feed automatisch aktuallisiert und Pinterest zeigt das Bild nach spaetestens 24h an. Wenn ein Board neu erstellt wird, sogar gleich.

Um jetzt mitzubekommen ob jemand ein Bild von meiner Seite aus Pinned muss ich die Pin-ID im HTML hinterlegen. Die Pin-ID muss ich hierzu aber erst ermitteln. Hierzu nutze ich wieder eine Rest-API welche Pinterest zur Verfuegung stellt.

Es wird der Benutzer und das Board uebergeben. Ueber den link, welcher den unique-Identifier von mir enthaellt, kann ich die Zuordnung machen und die Pin-ID in mein Image-Dokument zurueckschreiben. Hierdurch wird dann auch das HTML auf meiner Seite entsprechend angepasst.

Pinterest prueft die Seite entsprechend nun oefters. Was Pinterest allerdings nicht macht, es aktuallisiert nicht die Informationen! Wenn ich im Nachhinein eine Bewertung der Lokation durchfuehre, die Bilder aber bereits veroeffentlich habe, dann wird diese Beschreibung hier nur bei neuen Pins uebernommen.

Erstellen von Kartendaten mit Lokations-Informationen

Google, Here, Domino, Maps

Bevor man Karten erstellt, braucht man noch Daten der Lokation (also fuer welche Gegend man die Karte erstellen will und was man ggf. einzeichnen moechte). Hier ergibt sich bei meinen Bildern ein erstes Problem, ich habe fuer die Kamera das GPS deaktiviert, somit sind in den Bilddaten keine Infos diesbezueglich hinterlegt.

Also musste ich erst einmal die Lokation zu den Bildern ermitteln (zumindest von denen, wo ich mich noch erinnere wo das war). Hierzu verwende ich die Google „Place“ Suche. Hierzu uebergebe ich der Suche die nachfolgenden Daten und schaue was Google darueber findet (via REST-API).

Die Trefferquote ist hierbei bei mir bei ueber 90%. Ich bewege mich wohl auf ausgetretenen Pfaden (Ausnahme bilden hier ein paar Lokationen im Kosovo).

Die Google Places API gibt aber nicht viel her, deshalb ist bei der Abfrage eigentlich nur die eindeutige ID interessant und ggf. die Adresse (wenn mehrere Treffer zurueckkommen, weil es mehrere Treffer gibt, dann mache ich eine Selektion ueber die Adresse).

Ach ja, das ganze benoetigt noch einen Account in der Google-Cloud (haate ich ja schon bzgl. Google-Vision erklaert).

Wie man hier schon sehen kann, gibt es noch einen zweiten Key fuer die Karten (hierzu spaeter mehr). Zuerst erweitern wir einmal die Informationen der Places-API. Hierzu gibt es noch eine Detailsabfrage, welche die eindeutige Google-ID benoetigt.

Hierueber bekommen ich dann die detailierten informationen (Oeffungungszeiten, Telefon, Lat, Lng, Website ...). Der Script-Agent schreibt diese Information in das Image-Dokument.

Danach versuche ich die Daten noch ueber Here zu verifizieren. Hier findet nicht ganz so viele Lokationen wie Google, aber ich verwende deren Karten lieber (mehr Moegglichkeiten zur Konfiguration und guenstiger, wobei ich bei beiden nicht an die Begrenzung komme). Fuer Here wird auch ein Account benoetigt und man kann sich entsprechendt eine API key damit erstellen.

Mit den nun vorhanden Informationen kann ich zur jeweiligen Lokation eine Karte erstellen. Wenn Here die Lokation kennt, dann hiermit, sonst mit der Googlemaps API.

Die Grafik kann mit der API URL heruntergeladen werden und an das entsprechende Lokations-Dokument angehaengt werden. Hierzu verwende eine einfache JavaLib.

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

public class GetImageFromUrl {

    public static boolean getImageFromUrl(String imageUrl, String filePath) {
        try {
            URL url = new URL(imageUrl);
            InputStream is = url.openStream();
            OutputStream os = new FileOutputStream(filePath);
            byte[] b = new byte[2048];
            int length;
            while ((length = is.read(b)) != -1) {
                os.write(b, 0, length);
            }
            is.close();
            os.close();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

Welche dann ueber Ls2J in meiner LotusScript-Aktion aufgerufen werden kann.

Dim jSession As New JavaSession
	Dim jClass As JavaClass
	
	Set jClass = jSession.GetClass( "GetImageFromUrl" )     
	
	Set doc = dc.GetFirstDocument
	
	Do While Not doc Is Nothing
		fName = "c:\temp\" & doc.GetItemValue("unique")(0) & ".png"
		lat = Replace(Cstr(doc.GetItemValue("gsp_lat")(0)), ",", ".")
		lng = Replace(Cstr(doc.GetItemValue("gsp_lng")(0)), ",", ".")
		mapURL = "https://image.maps.ls.hereapi.com/mia/1.6/mapview?apiKey={key}" & lat & "," & lng & "&z=16&w=640&h=480&t=3&f=4&i=1&pip=-4"
		
		If jClass.getImageFromUrl(mapURL, fName) Then			
			Set rtitem = New NotesRichTextItem( doc, "mapHere" )
			Set object = rtitem.EmbedObject( EMBED_ATTACHMENT, "", fName)
			
			Call doc.Save(True, False, True)
			
			Kill fName
		End If
		
		Set doc = dc.GetNextDocument( doc )
	Loop

Wenn ich nun eine Lokation erstellt habe, dann kann ich diese auch anderen Bildern zuweisen.

Auf meiner Seite werden bei den Bildern, wo mir die Lokation eingefallen ist, entsprechend der Name angezeigt.

Und wenn man die Lokation anklickt, dann wird entsprechend die Karte angezeigt mit den entsprechenden Detailinformation (soweit bekannt).

Fuer die Maps von den Lokationen verwende ich als format PNG, bei den Laender- und Stadt-Karten SVG, da diese besser skalieren auf dem mobiles. So kommen also die erweiterten Information auf diese Seite, keine Hexerei ...

Erstellen von Social-Media sharing actions

Domino, Social-Media, Shareing

Zum Thema Social-Media bin ich ja etwas zwiespaeltig eingestellt. Ich hab ja nichts gegen das Teilen von Content auf diesen Plattformen, allerdings habe ich etwas gegen das tracking dieser Plattformen. Diese Plattformen nieten zur einfachen Einbindung in die eigene Website schoen vorgefertigte Plug-ins an, welche aber etwas mehr Funktionalitaet haben als nur das Teilen des Contents. Deshalb habe ich das Teilen etwas anders geloest, ohne hierfuer auf die zur Verfuegung gestellten Plug-ins oder Code-Bloecke zurueckzugreifen.

Ich habe mal 4 Optionen zur Verfuegung gestellt (Facebook, Twiter, Pinterest und Mail).

Die Platformen bieten alle (noch) eine Option zum Teilen via URL-Link an, ueber welchen die Plattform aufgerufen werden kann und ueber Parameter dann die entsprechende Info uebergeben werden kann. Der Umfang der Parameter ist aber unterschiedlich und wie bei Facebook inzwischen auf einen Parameter (den direkten Link) sehr eingeschraenkt.

Als Beispiel hier einmal der Link fuer Pinterest (mit entsprechenden Variablen) ...

https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fwww.workm.de%2FWV5500%3FOpenView%26RestrictToImage%3D" + _shortID + "&media=https%3A%2F%2Fwww.workm.de%2F" + uKey + "%2F$File%2F" + PreviewFilename + "%3FOpenElement" + "&description=" + _imgAlt

Man hat ueber diesen Weg allerdings keine direkte auswertbarkeit ob jemand die Aktion ausgefuehrt hat. Daher habe ich das ganze auf einen Agenten ausgelagert, welchem die Plattform als auch der Schluessel zum entsprechenden Bild mitgegeben wird.

Der Agent ermittelt die entsprechenden Werte fuer den auszufuehrenden Link und erstellt auch noch ein Dokument fuer mich als Counter.

	
	Call doc.Replaceitemvalue("Form", "F7000")
	Call doc.Save(true, false, true)
	
	arg = atfRight(session.Documentcontext.Query_String(0), "&")
	Call doc.Replaceitemvalue("socialArg", arg)
	sid = atfRight(arg, "=")
	Call doc.Replaceitemvalue("socialSid", sid)
	typ = atfLeft(arg, "=")
	Call doc.Replaceitemvalue("socialTyp", typ)
	
	Call doc.Save(True, False, True)

	Print "Content-Type: text/html"
	Print "Status: 302 Moved Temporarily"
	
	Select Case typ
		Case "FBS"
			' FaceBook Single Story > Image Gallery
			Print "Location: https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.workm.de%2FWV5500%3FOpenView%26RestrictToImage%3D" & sid
			
		Case "FBM"
			' FaceBook Multi Article > Artikel
			Print "Location: https://facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.workm.de%2FWV1500%3FOpenView%26RestrictToArticle%3D" & sid
			
		Case "TWFS"
			' Twitter Single
			Print "Location: https://twitter.com/intent/tweet?url=www.workm.de%2FWV5500%3FOpenView%26RestrictToImage%3D" & sid
			
		Case "PRS"
			' Pinterest Single
			Set sDoc = sView.Getdocumentbykey(sid, true)
			If Not(sDoc Is Nothing) Then
				Print "Location: https://pinterest.com/pin/create/button/?url=https%3A%2F%2Fwww.workm.de%2FWV5500%3FOpenView%26RestrictToImage%3D" & sid & "&media=https%3A%2F%2Fwww.workm.de%2F" & sDoc.getItemValue("uKey")(0) & "%2F$File%2F" & sDoc.getItemValue("PreviewFilename")(0) & "%3FOpenElement&description=" & sDoc.getItemValue("GV_labelAnnotations")(0)
			Else
				' ...
			End If
	End Select

Der “Counter” ist allerdings in der Hinsicht limitiert, als dass er nur das aufrufen der Aktion dokumentiert. Wenn der Anwender die Aktion abbricht bekomme ich das nicht mehr mit.

Beim oeffnen des Links wird noch nicht das ausgewaehlte Bild angezeigt! Diese erfolgt erst nach dem publizieren!

Damit das allerdings auch funktioniert, muss die Seite, welche dem Link mitgegeben wird entsprechende Tags enthalten. Das sind die OpenGraph tags.

Das erfolgt entsprechend Notes via @Formula im entsprechenden $$ViewTemplate.

Das ganze funktioniert fuer Facebook, Twitter und Pinterest in etwa gleich. Nur das sharing via Mail wird ueber einen mailTo link generiert.

Eigentlich dachte ich ja, ich koennte ueber das log-Dokument spaeter validieren ob der Anwender das Bild tatsaechlich geteilt hat. Bei FB gebe ich hierzu eine App-ID mit an, allerdings gibt das die API von denen nicht her, man kann deren Plug-in Loesung nicht komplett nachbilden. Aber dafuer muss ich bei meiner Loesung nicht mit meinen (euren) Daten bezahlen :).

Erstellen der sitemap und des rss-feeds mit Domino

Domino, RSS, Sitemap

Warum ueberhaupt eine Sitemap erstellen? Nun gut, man koennte es Google und konsorten einfach selbst ueberlassen die Seite richtig zu indexieren, oder aber dabei behilflich sein ueber eine Sitemap (Vor allem wenn man Content in tieferen ebenen hat, der sich sonst nicht so leicht finden laesst.).

Die Sitemap.XML ist vom Notes-Design her auch nichts anderes als eine Ansicht.

Damit das ganze auch einfach als sitemal.xml aufgerufen werden kann, wird die Ansicht mit dem alias sitemap.xml gespeichert. Dann muss nur noch ein entsprechendes $$ViewTemplate erstellt werden.

Das ganze sieht dann im Browser so aus.

Das ganze habe ich dann entsprechend bei Google, Bing etc. entsprechend ueber deren Consolen hinterlegt und diese habe diese als Valide erkannt und scannen die Seiten nun entsprechend.

Mit dem rss Feed verhaellt es sich im Prinzip aehnlich. Auch hier wird erst einmal eine entsprechende Ansicht erstellt. Der Content ist aufgrund des Formates etwas komplexer (auch wegen der Bilder).

Auch hier wird dann entsprechend ein $$ViewTemplate erstellt.

Auch das laesst sich im Browser entsprechend aufrufen.

Aber interessanter ist natuerlich das einbinden des rss-Feeds in eine entsprechende App. Hier mal als Beispiel auf dem iPad Bloglovin.

Oder auf dem iPhone mit Newsify oder Feedly.