Mehrsprachigkeit mit Oracle APEX

Oracle APEX bietet out-of-the-box die Möglichkeit, eine Anwendung in mehreren Sprachen zu veröffentlichen. Wie das funktioniert, möchte ich Ihnen in diesem Beitrag näher erläutern.

TIPP!

Bitte beachten Sie auch meinen Post zum Thema

Gefahren und Fallen beim Umgang mit APEX Translations

In diesem Beitrag sind noch weitere nützliche Tipps enthalten, die Ihnen viel Zeit sparen werden.

Szenario

Wir möchten eine Anwendung schreiben, die in 2 Sprachen, Englisch und Deutsch, verfügbar ist. Die Sprache soll sich automatisch ändern, wenn die Browsersprache geändert wird.

Als Standardsprache für die Entwicklung legen wir die englische Sprache fest, da wir uns international positionieren möchten.

Als kleine Zusatzanforderung möchten wir, dass beim Wechsel der Sprache das Datumsformat sich automatisch auf das jeweilige  lokale Format anpasst (für deutsch: DD.MM.YYYY, für englisch: DD/MM/YY).

Vorgehensweise

  1. Festlegen der Standardsprache (in welcher Sprache wird entwickelt?)
  2. Festlegen, auf welche Weise die Sprache gewechselt werden kann
  3. Applikationseinstellungen für das Sprachhandling vornehmen
  4. Applikation in eine andere Sprache übersetzen
  5. Weitere notwendige Aktionen
  6. Formatmaske für Datumsfelder angeben
  7. Tipps und Tricks beim Umgang mit Translation Files

Zu 1: Festlegen der Standardsprache (in welcher Sprache wird entwickelt?)

Am Anfang sollte man sich überlegen, in welcher Sprache die Anwendung realisiert wird (Primary Language). APEX nutzt diese als Basis für alle weiteren Übersetzungen.

Wie wir bereits festgelegt haben, soll die Standardsprache für die Entwicklung englisch sein.

TIPP!

Entwickeln Sie in der Sprache, die für Sie am wichtigsten ist und die als erstes veröffentlicht werden soll. Alle weiteren Übersetzungen können später jederzeit noch hinzugefügt werden.

Die Standardsprache wird automatisch immer dann verwendet, wenn eine Sprache aktiv ist, für die es keine Übersetzung gibt!!!

Bitte beachten Sie auch, dass jede Änderung an den Labels oder Texten in der Standardsprache Änderungen in allen Übersetzungen nach sich zieht!

Zu 2: Festlegen, auf welche Weise die Sprache gewechselt werden kann

Legen sie fest, wie die Sprache innerhalb der Anwendung geändert werden kann. Hierbei sollte man sich überlegen, ob die Sprachanpassung automatisch oder durch benutzereingriff erfolgen soll.

APEX bietet ihnen hierzu folgende Möglichkeiten zur Sprachauswahl:

– Übernahme der Browsersprache

– Einstellen der Sprache über eine Umgebungsvariable namens FSP_LANGUAGE_PREFERENCE

– Einstellen der Sprache über eine selbst definitere Umgebungsvariable

In diesem Beispiel soll sich die Sprache analog zu der im Browser eingestellten Sprache verhalten. D.h. sie soll automatisch wechseln, wenn die Browsersprache geändert wird.

Zu 3: Applikationseinstellungen für das Sprachhandling vornehmen

Zuerst machen wir die globalen Einstellungen für das Sprachhandling in der Applikations-Definition.

  • Über „Shared Components“ -> „Definition“ -> „Globalization“ müssen wir 2 Einstellungen machen.
    • „Application Primary Language“ auswählen

Wählen Sie hier die Standardsprache, die wir zuvor als Primary Language festgelegt haben, aus (in unserem Beispiel „English (en)“.

    • “Application Language Derived From” auswählen.

Wählen Sie hier „Browser (use browser language preference)“ aus.

    • Optional geben wir hier schon mal das Standard-Datumsformat für die englische Sprache an (DD/MM/RRRR)
Define global language

Define global language

Zu 4: Applikation in eine andere Sprache übersetzen

Nun wollen wir die eigentliche Übersetzungsarbeit durchführen. Dazu gehen Sie bitte, wie folgt, vor.

  • Über „Shared Components“ -> „Translate Application“ (in der Region „Globalization“) gelangen Sie in das Translation Menü. Dieses Menü muss nun, mehr oder weniger, von oben nach unten durchgearbeitet werden.
  • Klicken Sie auf “1. Map your primary language application to a translated application” und dann auf “Create”.

Hier wird nun zuerst einmal die Sprache erzeugt, die veröffentlicht werden soll. Wir machen nun die notwendigen Eingaben und setzen folgende Werte:

    • Translation Application: 1501
    • Language Code: German (Germany) (de)
    • Comments: <Irgendein Kommentar>

TIPP!

In dem Feld “Translation Application” wird eine eindeutige ID erwartet, die man jedoch selbst vergeben muss. Wenn man viele Applications auf einer Instanz erzeugt hat, wird das etwas schwierig mit der Eindeutigkeit. Deshalb meine Empfehlung: Überlegen Sie sich eine Regel, wie Sie IDs für Übersetzungen verwenden. Sie können z.B., wie in unserem Beispiel, die ID der Primary Language (in unserem Falle 150) nehmen und eine Stelle anhängen und diese dann einfach für jede Übersetzung um 1 erhöhen (1501 deutsch, 1502 italienisch, 1503 spanisch, usw.)

  • Über die Breadcrumb Region gehen wir zurück zum Translation Menü und erzeugen das Translation-File.
    • Klicken auf „Seed and export the translation text of your application into a translation file.”
    • Sprache auswählen und Texte erzeugen

Hier werden nur die von uns angelegten Sprachen angezeigt. In unserem Beispiel existiert nur die „1501 de“. Diese wählen wir aus und klicken auf „Seed Translatable Text“.

Wir gelangen nun in den XLIFF Export. Hier können wir entscheiden, ob wir die Übersetzungen für die gesamte Anwendung oder nur für bestimmte Seiten erzeugen wollen. Zudem kann ausgewählt werden, ob alle Übersetzungstexte oder nur die, die eine Übersetzung benötigen, exportiert werden sollen.

Wir wollen nun die gesamte Anwendung übersetzen und auch nur die Texte, die eine Übersetzung benötigen.

Wir wählen hier nun die Application aus, markieren die Option „Only those elements requiring translation“ und klicken auf „Export XLF File for Application“, um das Übersetzungsfile zu erzeugen.

Es wurde nun eine Datei mit allen notwendigen Übersetzungen erzeugt (f150_1501_en_de.xlf). Dies ist nun die Basis, auf der wir die Texte übersetzen müssen. In unserem kleinen Beispiel können wir die Texte nun direkt in dem erzeugten File ändern (Bitte nur jeweils die „Target“-Texte ändern!!!). Bei einer richtigen Anwendung kann das allerdings ziemlich viel Aufwand werden.

Hinweis!

Unter Punkt 7 finden Sie noch einige Tipps und Tricks für das Übersetzen der Texte und den Umgang mit den Translation-Files.

  • Haben wir die Texte im Translation-File übersetzt, müssen wir die Übersetzung noch veröffentlichen.
    • In der Breadcrumb Region klicken wir auf „Translate Application“ und dann auf „4. Apply your translation file and publish“.
    • Auf der Seite „XLIFF Translation Files“ klicken wir nun auf „Upload XLIFF“ um das Translation File hochzuladen. Wir machen folgende Eingaben:

Title: 1501_de

Description: <irgendeine Beschreibung für das Import File>

XLIFF File: <auswählen der Datei mit unseren Übersetzungen, in unserem Fall f150_1501_en_de.xlf>

    • Nachdem wir auf „Upload…“ geklickt haben, wird uns die importierte Datei angezeigt. Klicken Sie nun auf den Title der Datei und wählen im folgenden Dialog die Sprache aus, für die die Übersetzung bestimmt ist (“apply to” – in unserem Fall „1501 de“).
    • Klicken Sie nun auf „Apply XLIFF Translation File“, prüfen Sie im folgenden Dialog, dass bei „Create Application“ die richtige Applikation angezeigt wird (ggf. korrigieren) und dann oben rechts auf „Publish Application“, um die Sprache schließlich zu veröffentlichen.

Hinweis!

APEX erzeugt für jede veröffentlichte Sprache eine eigene Applikation. Im Hinblick auf die Performance ist das sicherlich eine sehr gute Lösung. Jedoch werden Sie früher oder später sehen, dass dies auch einige Nachteile hat. Wenn wir nämlich nun PLSQL-Code verändern, der nichts mit irgendwelchen Übersetzungs-relevanten Texten zu tun hat, müssen wir trotzdem alle Übersetzungen erneut veröffentlichen, da APEX teilweise auch funktionalen Code dupliziert und für jede Übersetzungs-Applikation explizit vorhält. Mehr dazu finden Sie unter „Gefahren und Fallstricke beim Umgang mit APEX Translations“.

Zu 5: Weitere notwendige Aktionen

Übersetzen von internen APEX Messages

Wenn Sie die Anwendung nun aufrufen und Ihren Browser auf z.B. „German (de)“ umstellen, werden Sie feststellen, dass einige Labels noch in englischer Sprache dargestellt werden. Z.B. werden in einer Reportmaske mit Seitennavigation die „Next“/“Previous“ Labels noch in Englisch dargestellt.

Grund hierfür sind die APEX internen Messages, die nicht out-of-the-box in das Translation-File exportiert werden. Eine Aufstellung aller internen Messages finden Sie unter folgendem Link in Kapitel „Table 16-3 Internal Messages Requiring Translation“.

http://download.oracle.com/docs/cd/E10513_01/doc/appdev.310/e10499/global.htm#BABFHCJA

http://apex.oracle.com/i/doc/global_mess_reports.htm

Um diese internen Messages zu übersetzen, gehen Sie bitte wie folgt vor.

  • „Shared Components“ -> unter „Globalization“ auf „Text Messages“ und dann auf „Create“ klicken.
  • Geben Sie bei Name den gewünschten Substitution String aus der Tabelle „Internal Messages Requiring Translation“ (zuvor erklärt) ein.
  • Wählen Sie die Sprache aus, die als Hauptsprache bei der Application angegeben wurde (bei uns „Englisch“).
  • Geben Sie den Übersetzungstext ein (in englisch).

Hier können auch Platzhalter eingegeben werden. Um z.B. den Info-Text in der Navigations-Popupliste (X_Y_of_Z: row(s) 1 – 10 of 12) zu ändern, geben Sie bei Name „WWV_RENDER_REPORT3.X_Y_OF_Z“ und bei Text z.B. „Zeile(n) %0 – %1 von insgesamt %2“ ein.

Beachte!

Diesen Vorgang müssen Sie für alle benötigten internen Messages durchführen.

  • Nun, wie unter Punkt 4 beschrieben, die übersetzten Applikationen neu erzeugen (der Punkt “Map your Primary Language Application to…” kann weggelassen werden, da die Anwendung bereits gemapt ist). Wenn die gerade angelegten Texte in der Hauptsprache erzeugt wurde, werden diese Texte ebenfalls mit in die jeweiligen XLF-Files eingetragen. Nach dem Übersetzen und „publishen“ müssten auch hier nun die Übersetzungen angezeigt werden.

TIPP!

Sollten diese internen Messages nun nicht im Translation-File auftauchen, lesen Sie bitte den Beitrag Interne Messages und dynamische Übersetzungen werden nicht in das XLF-File exportiert.

HINWEIS!

Alle Übersetzungstexte für interne Messages können auch hier komplett erstellt werden. Jedoch ist der Weg über die Translation-Files der bessere, wenn die Übersetzungsarbeit von einem Übersetzer durchgeführt wird.

Übersetzen von statischen Wertelisten

Wenn Sie statische Wertelisten verwenden die nicht über eine LOV implementiert, sondern mit „STATIC:…“ einfach bei dem entsprechenden Item eingegeben wurden, so werden diese Inhalte nicht übersetzt.

Hierfür sollten Sie auf jeden Fall eine statische LOV erzeugen und ausschließlich diese verwenden (auch im Sinne von Wiederverwendbarkeit sinnvoll!). Statische LOV-Werte werden beim erzeugen der Translation-Files (siehe oben) mit exportiert und können dann zusammen mit den anderen Texten übersetzt werden.

Übersetzen von dynamischen Wertelisten

Ein weiteres Problem besteht darin, LOV-Werte, die dynamisch aus der Datenbank ermittelt werden, zur Laufzeit zu übersetzen. APEX stellt dafür eine PLSQL-API zur Verfügung (APEX_LANG).

Wie das funktioniert, möchte ich Ihnen hier kurz zeigen.

Gegeben sei eine Wertelist, die verfügbare Sprachen darstellt und auswählbar macht. Diese Sprachen sind in einer Datenbanktabelle mit dem Namen „LANGUAGE“ eingetragen (In unserem Beispiel sind nur 2 Einträge in der Tabelle für „German“ und „English“).

Gehen Sie bitte wie folgt vor.

  • Übersetzungstexte für alle Werte der Tabelle LANGUAGE erzeugen.
    • Über „Shared Components“ -> „Translate Application“ (Globalization) -> “Optionally identify any data that needs to be dynamically translated …” -> “Create” einen neuen Text in der gewünschten Übersetzungssprache erzeugen.

Language: “German (Germany) (de)” auswählen

Translate From Text: “German” (das ist der Key bzw. der Text, der in der Tabelle vorhanden ist und übersetzt werden muss).

Translate To Text: „German“ (das ist der Text, der angezeigt werden soll).

Bild zu dynamic translation messages

    • “Create” klicken, um den Text anzulegen.
    • Das Gleiche machen wir nun mit allen Sprachen, die in der Tabelle vorhanden sind und übersetzt werden müssen.

Hinweis!

Ich habe hier als Übersetzungstexte die englischen Texte eingegeben, um das Szenario aufzuzeigen. Wenn Sie hier direkt die richtigen Übersetzungstexte eingeben, sparen Sie sich die spätere Übersetzung.

Beachte!

Wenn Sie diese dynamischen Texte in der Hauptsprache (in unserem Fall “Englisch”) eingeben, werden diese Texte, im Gegensatz zu den anderen Text-Messages, nicht in das XLIFF-File exportiert! Deshalb sollten Sie darauf achten, direkt die richtige Sprache auszuwählen.

  • Erzeugen einer LOV zur Selektion dieser Sprachen.

Beim Erzeugen der LOV müssen wir die Apex Language API (APEX_LANG) benutzen, um auf die Übersetzungstexte zuzugreifen. Das Statement für die LOV sieht dementsprechend wie folgt aus.

  • Nun müssen wir die LOV dem Item zuweisen, welches diese Werteliste beinhalten soll.
  • Nun, wie unter Punkt 4 beschrieben, die übersetzten Applikationen neu erzeugen. Wenn die gerade angelegten dynamischen Werte in der Hauptsprache erzeugt wurden, werden diese ebenfalls mit in die jeweiligen XLF-Files eingetragen. Nach dem Übersetzen und „publishen“ müssten auch hier nun die Übersetzungen richtig angezeigt werden.

HINWEIS!

Alle Übersetzungstexte für interne Messages können auch hier komplett erstellt werden. Jedoch ist der Weg über die Translation-Files der bessere, wenn die Übersetzungsarbeit von einem Übersetzer durchgeführt wird.

  • Weitere Informationen zum Umgang mit der APEX_LANG-API hierzu finden Sie unter

http://download.oracle.com/docs/cd/E10513_01/doc/appdev.310/e10499/global.htm#insertedID0

Übersetzen sonstiger dynamischer Inhalte

Nun kann sich auch noch die Notwendigkeit ergeben, Werte aus anderen Tabellen, die in Reports oder Formularen benötigt werden, zu übersetzen. Hierfür stellt APEX noch eine andere Funktion der APEX_LANG-API zur Verfügung.

APEX_LANG.MESSAGE

Das Handling hierfür ist fast identisch zu dem vorherigen Punkt („Übersetzen von dynamischen Wertelisten“). Der Unterschied besteht hauptsächlich darin, dass der Übersetzungstext in APEX an einer anderen Stelle eingetragen wird.

Zum Erstellen des Übersetzungstextes klicken Sie bitte unter „Shared Components“ -> „Text Messages“ (Globalization) -> und klicken auf „Create“. Bei Namen bitte den Text bzw. Key eintragen (der Text aus der Tabelle, der übersetzt werden soll), wählen die Sprache aus und tragen den Übersetzungstext ein. Fertig!

Nun können Sie innerhalb Ihres Codings (im Prinzip überall, wo Sie PLSQL-Code verwenden), mit der APEX_LANG-API auf diesen Text zugreifen.

Beispiel: (Dieses Statement würde die Städtenamen aus der Tabelle PERSONEN übersetzen, wenn zuvor die Städte als Übersetzungstexte angegeben worden sind)

SELECT APEX_LANG.MESSAGE(city) FROM PERSONEN;

Weitere Informationen zur APEX_LANG-API  finden Sie unter

http://download.oracle.com/docs/cd/E10513_01/doc/appdev.310/e10499/global.htm#insertedID0

ACHTUNG BUG in Version 3.2.1!!!

Wenn man zuvor eine Translation zu einer Sprache mit einer anderen ID erstellt und dann gelöscht hatte (z.B. zuvor die „151 de“), dann bleiben Fragmente in der Tabelle WWV_FLOW_TRANSLATABLE_TEXT$ die verhindern, dass eine deutsche Übersetzung mit einer anderen ID (jetzt 1501) erstellt werden kann. Als workaround geht folgendes:

  1. Die Translation, die Probleme macht, löschen.
  2. Gesamte Applikation exportieren
  3. Gesamte Applikation löschen
  4. Applikation importieren.

Nun kann die Translation wieder normal angelegt und damit gearbeitet werden.

Zu 6: Formatmaske für Datumsfelder definieren

Wie Sie Formatmasken in APEX definieren können, finden Sie hier.

Datumsformate applikationsweit defnieren

Einen Beitrag zum Thema “Localization” für Datums- und Zeitformate kommt bald. Sollten Sie Informationen dazu benötigen, können Sie mich gerne kontaktieren.

Zu 7: Tipps und Tricks zum Umgang mit Translation Files

In diesem Beitrag wurde nun erklärt, wie man mit APEX eine Anwendung mehrsprachig auslegen kann. Nun stellen sich allerdings noch folgende Fragen.

  1. Wer übersetzt denn nun eigentlich die ganzen Texte?
  2. In welcher Form stelle ich dem Übersetzer die Texte zur Verfügung?

Naja, die Antwort auf die 1. Frage ist recht einfach – die Übersetzer!

Im Unternehmen haben wir vielleicht Leute die gut genug englisch sprechen, um die englische Übersetzung zu machen. Aber was ist mit den anderen Sprachen? Hierfür benötige ich natürlich einen oder vielleicht sogar mehrere Übersetzer.

Und schon sind wir bei der 2. Frage. In welcher Form kann ich den Übersetzern meine Texte zur Verfügung stellen?

Das Problem

Die Translation Files (XLF-Files) sind so unübersichtlich, dass ein Übersetzer mir dieses wahrscheinlich nach spätestens 5 Minuten um die Ohren schlagen würde.

Also was tun?

  • Nicht benötigte Labels und Texte von den Translations ausschließen

APEX bietet eine Möglichkeit, ein paar unnötige Übersetzungen zu unterdrücken. Das macht auch durchaus Sinn, da eh schon genügend Texte als Übersetzungen erkannt und vorgeschlagen werden, die gar keine Übersetzung benötigen.

    • Unnötige Region Labels unterdrücken

Bei den Region-Attributes gibt es die Möglichkeit, die Option „exclude title from translation“ anzuklicken. In diesem Fall wird der Region-Title nicht als Übersetzung eingetragen. Diese Option macht Sinn für Hilfs-Regions, die nicht angezeigt werden.

    • Templates von den Translations ausschließen

Bei den Templates gibt es die Möglichkeit, ein Template komplett auszuschließen. Sie sollten also prüfen, ob statische Texte in Ihren Templates enthalten sind. Wenn nicht, markieren Sie die entsprechenden Templates als „non-translatable“ (wie folgende Abbildung zeigt).

Bild zu Define option Template Translatable

Define option Template Translatable

  • Nur die Texte in das Translation File exportieren, die eine Übersetzung benötigen

Sie können beim erzeugen des XLF-Files die Option „Only those elements requiring translation“ auswählen. Dadurch werden schon mal eine nicht benötigte Texte ausgeschlossen.

  • Übersetzungstexte über APEX Dialog bearbeiten

APEX bietet einen Dialog, um Übersetzungstexte direkt im APEX Builder zu bearbeiten. Hinweis!

Beim späteren Erzeugen des Translation Files (XLF) werden die Änderungen erkannt und dort eingetragen.

Bild zu Manually edit translations

Manually edit translations

  • XLF-File in eigene Tabelle laden und Maske bauen

In meinem letzten Projekt haben wir ein JAVA-Programm geschrieben,  um das komplette XLF-File in eine eigene Datenbanktabelle zu schreiben. Für diese Tabelle haben wir einen einfachen änderbaren Report entwickelt, mit dem wir die Übersetzungstexte ganz einfach bearbeiten konnten. Ein zweites kleines JAVA-Programm hat die übersetzten Texte aus der Tabelle gelesen und ein neues APEX-konformes XLF-File erzeugt. Dieses konnten wir dann importieren, um damit die benötigen übersetzten Applications zu erzeugen.

Bei Interesse stellen wir Ihnen diese JAVA Programme gerne zur Verfügung (natürlich kostenlos – auch gerne mit Source-Code, damit Sie diese Programme für sich anpassen können).

  • Übersetzungstexte aus Repository auslesen

Zu diesem Punkt habe ich einen eigenen Post geschrieben.

Übersetzungstexte aus dem APEX Repository auslesen

Einen tabellarischen HTML-Bericht per Apex Mail versenden

April 23, 2015 1 Kommentar

Beim Betrieb einer Oracle Apex-Anwendung ist es oft notwendig tabellarische SQL-Berichte per Mail zu versenden. Dies kann eine Admin-Mail über den Zustand der Apex-Anwendung sein aber auch inhaltliche Benachrichtigungen an bestimmte Benutzerkreise oder Stakeholder der Apex-Anwendung. Gerade im Letzteren Fall sollte die Formatierung des SQL-Berichtes stimmen und das Layout ansprechend sein. Auf Nummer sicher geht man, wenn man in diesem Fall einen PDF-Bericht erzeugt und ihn im Anhang der Mail mitsendet. Dies ist allerdings auch entsprechend aufwendig.

Als einfachere Alternative bietet sich an, den SQL-Bericht als HTML-Tabelle in einer HTML-Mail per Apex Mail zu versenden.

Im Folgenden stelle ich eine generische Funktion vor, die einen beliebigen SQL-Select ausführt und die Ergebnismenge als HTML-Tabelle rendered.

Zusätzlich stelle ich ein Beispielskript vor, das die HTML-Tabelle per Apex Mail versendet. Dabei bereite ich das Layout des Berichts über mehrere allgemeine CSS-Regeln auf.

testmail

Funktion PRINT_RESULT:

Hierbei habe ich einfach die Funktion in http://www.java2s.com/Tutorial/Oracle/0601__System-Packages/Printtablewithdynamicquery.htm um die HTML-Option erweitert.

create or replace FUNCTION "PRINT_RESULT" ( p_query IN VARCHAR2, p_markup IN VARCHAR2 DEFAULT 'TEXT')
 RETURN CLOB
 AUTHID CURRENT_USER
 IS
 /**
 * Prints the result of the SQL-Statement (p_query) as TEXT-Output or HTML-Output (p_markup)
 * This function enhances the following code
 * http://www.java2s.com/Tutorial/Oracle/0601__System-Packages/Printtablewithdynamicquery.htm
 */
l_theCursor INTEGER DEFAULT dbms_sql.open_cursor;
l_columnValue VARCHAR2(32676);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
l_even BOOLEAN;
l_row_css VARCHAR2(1000);
l_return_str CLOB :='';
BEGIN
DBMS_SESSION.SET_ROLE('ALL');

dbms_sql.parse(l_theCursor,p_query,dbms_sql.native);

dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl);

for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;

l_status := dbms_sql.execute(l_theCursor);

IF p_markup = 'HTML' THEN
l_return_str := l_return_str || '

'; END IF;for i in 1 .. l_colCnt loop
l_columnValue := l_descTbl(i).col_name;

IF p_markup = 'HTML' THEN
l_columnValue := ''|| l_columnValue || '

';
END IF;
l_return_str := l_return_str ||substr(l_columnValue, 0, 300);

end loop;

IF p_markup = 'HTML' THEN
l_return_str := l_return_str || '

';
ELSE
for i in 1 .. l_colCnt loop
l_return_str := l_return_str || rpad( '----', 200, '-' );
end loop;
END IF;

l_return_str := l_return_str ||CHR(10);

l_even := FALSE;
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
IF p_markup = 'HTML' THEN
IF l_even THEN
l_row_css := 'style="background-color:#f5f5ff;"';
ELSE
l_row_css := '';
END IF;

l_return_str := l_return_str || '
<tr '||l_row_css||'>';
END IF;

for i in 1 .. l_colCnt loop

dbms_sql.column_value( l_theCursor, i, l_columnValue );
IF p_markup = 'HTML' THEN
l_columnValue := ''||l_columnValue || '

';
END IF;

l_return_str := l_return_str || substr(l_columnValue, 0, 300);

end loop;

IF NOT l_even THEN
l_even := TRUE;
ELSE
l_even := FALSE;
END IF;

IF p_markup = 'HTML' THEN
l_return_str := l_return_str || '

';
END IF;
l_return_str := l_return_str || CHR(10);

end loop;

IF p_markup = 'HTML' THEN
l_return_str := l_return_str || '

';
END IF;

RETURN l_return_str;
EXCEPTION
when others then dbms_sql.close_cursor( l_theCursor ); RAISE;
END;
/

Versand der Apex-Mail inkl. CSS-Regeln:

declare
    l_mail_text       CLOB;
    l_mail_text_html  CLOB;
    l_statement       VARCHAR2(32676);    
    l_mail_id         NUMBER;
    
    PROCEDURE setup_apex_mail
    IS
      l_security_group_id NUMBER;
    BEGIN
      SELECT workspace_id
      INTO    l_security_group_id
      FROM   apex_workspaces
      WHERE  workspace='YOUR_WORKSPACE'; /* Use your workspace name here */ 
  
      wwv_flow_api.set_security_group_id(l_security_group_id);
    END;    
      
    FUNCTION get_html_mail_header
      RETURN VARCHAR2
    IS
      l_home_url  VARCHAR2(1000);
    BEGIN
      l_home_url := 'intranet.home-site';
  
      RETURN '<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
    <meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE" />
    <style type="text/css">#content, body { color:#324242; font-family: Arial, sans-serif; width:60em; border:1px solid #EEE; padding:3em 5em 5em; margin:2em; } body { color:#324242; } a { color:#1F4F80; } .i
  nfo { font-size:75%; color:#324242; padding:0; } .details { background:#1F4F80 none repeat scroll 0 0; color:#CDD6DF; font-weight:normal; border:2px solid #F18900; text-align:left; } .content th { col
  or:#CDD6DF; font-weight:normal; } .details td { color:#ffffff; font-weight:bold; } .header { background:#1F4F80 none repeat scroll 0 0; border:2px solid #F18900; text-align:center; border-collapse:col
  lapse; color:#CDD6DF; } .header td { padding:4px 0; } .detailsth { background:#1F4F80; color:#4D5508; font-weight:bold; color:#ffffff; } .event { margin:auto; text-align:center; width:85%; } .statusDe
  scr { font-size:0.85em; line-height:2em; } .hide { display: none; } td {border:1px solid #999;} th {border:1px solid #99b; background-color:#dde;}  </style>
  
   </head>
   <body text="#000000" bgcolor="#ffffff">
    <div id="content">
     <a href="'||l_home_url||'">
  
      <i>Home-Site</i>
     </a>
  
    <div style="display:none">
      <br/><br/><br/>
  
                  	 to display this HTML-Mail properly  <br/>
                  	 enable **HTML-format** in your Mail-client <br/>
    </div>
  
     <br />
     <br />
     <br />
     <b>Hello ,</b>
  
     <br />
     <br />';
  
    END;
  
  
    FUNCTION get_html_mail_footer
      RETURN VARCHAR2
    IS
    BEGIN
  
      RETURN '
     <br />
     <br />
     <br />
     <b>
      <span class="firstLetter">Regards,</span>
     </b>
     <br />
     <br />
     Your ApexTipps-Team
     <br />
     <br />
      This email is being sent automatically, please do not answer it
      </div>
   </body>
  </html></div>';
  
    END;
      
    
  BEGIN
      l_mail_text := get_html_mail_header;
      
      l_mail_text := l_mail_text || 'Apex DB-Info '||'<br/>'||'<br/>'||sysdate;
      
      l_statement := 'select owner, table_name, tablespace_name, status, num_rows
                      from all_tables
                      where owner=''HR''';

      l_mail_text := l_mail_text || '<br/>' ||'<br/>' || '<strong>HR-Tables</strong> '||'<br/>'||'<br/>'||
        print_result(l_statement, 'HTML');

      l_mail_text := l_mail_text || get_html_mail_footer;
    
      setup_apex_mail;

     l_mail_id := apex_mail.send(
          p_to       => 'some.address@email.domain',   -- change to your email address
          p_from     => 'some.address@email.domain',
          p_subj     => '[Apex-Info] Query-Report',
          p_body     => l_mail_text,
          --p_body     => null,
          p_body_html=> l_mail_text);


end;
/

BEGIN
  APEX_MAIL.PUSH_QUEUE;
END;
/


Kategorien:Allgemein

Flexibler Resource-Gantt-Chart mit Oracle Apex und AnyChart / AnyGantt (2)

Januar 14, 2015 1 Kommentar

Die Standard Charting-Features von Oracle Apex bieten einen schnellen unkomplizierten Weg, um eine Vielzahl einfacher Charts zu erstellen. Sobald weitere Anforderungen dazukommen, stößt man allerdings schnell an die Grenzen der Apex-Wizards. Da der Oracle Apex – Umgebung aber die volle Funktionalität der AnyChart – Library zur Verfügung steht, lassen sich wesentlich anspruchsvollere Charts erstellen. Vereinfachend kommt hinzu, dass die AnyChart – Library über eine gute Online Dokumentation und XML-Referenz verfügt.

Im letzten Beitrag stellte ich den Rahmen für die händische Einbettung von AnyGantt in Apex vor.

In diesem Beitrag, erweitere ich das Beispiel, um die weiteren Punkte der Anforderungsliste:

  • A1 Der Resource-Gantt-Chart soll über Suchfelder gesteuert werden
  • A2 Wenn ein Suchfeld geändert wird, soll der Gantt-Chart „The-Ajax-Way“ aktualisiert werden
  • A3 Der Gantt-Chart soll auf deutsch sein
  • A4 Feiertage sollen markiert werden
  • A5 Es sollen mehrere Balkenfarben zur Verfügung stehen, die mit einem schönen Verlauf dargestellt werden sollen
  • A6 Im Balken soll eine Beschriftung erscheinen
  • A7 Per Maus-Hover soll bei Bedarf eine Info zum Balken ein- und ausgeblendet werden
  • A8 Per Mausklick auf den Balken, soll ein Zusatzdialog zum Gantt-Chart geöffnet werden.

 

Hier ist meine Demo-Seite zu dem Beispiel:

https://apex.oracle.com/pls/apex/f?p=DEMO_JENS_MARRE:ANY_GANTT

(Login über: demo/demo)

 

Die Anforderungen A1 und A2 habe ich bereits im letzten Beitrag implementiert. Alle anderen Anforderungen werden im On-Demand-Prozess „generate_gantt_xml“ umgesetzt. Letztendlich muss hierfür der AnyGantt-Syntax bedient werden. Ich erkläre die Umsetzung der Anforderungen als XML-Kommentar direkt in dem Beispiel-XML-Code weiter unten.

In meiner Demoapplication und dem Beispiel-XML-Code verwendet ich feste Datumswerte und nicht den per Ajax geposteten Datumswert P4_DAY.

 

Workarounds:

Generell werden im Header-Teil der XML-Datei die allgemeinen Chart-Einstellungen und die verschiedenen Balkenstyles definiert. Für jeden Balken können im Body-Teil der XML-Datei AnyChart-Attribute einzeln und dynamisch gesetzt werden (z.B. Balkentext, Hovertext, …).

Dabei stößt man allerdings auf folgendes Problem: Farbwerte können in den Styles nicht dynamisch gesetzt werden, was die Umsetzung von Anforderung A5 vor ein Problem stellt. Ich verwende dafür folgenden Workaround:

Ich ermittle dynamisch die möglichen Farbstile in dem angefordertem Zeitraum und kopiere für jeden Farbstil einen eigenes Tag „period_style“ in die XML-Datei. Jeder Stil erhält als Namen den Hex-Wert der Grundfarbe des Balkens und kann so im Body-Teil der XML-Datei für die Balken referenziert werden.

 

Für die Umsetzung der Anforderungen A6 und A8 gibt es ein weiteres Problem: In der aktuellen AnyGantt-Version legt sich der Balkentext über den Mausklick-Trigger.

Hierfür habe ich folgenden Workaround gefunden: Während des Maus-Hovers, also im Hover-Style, lösche ich den Balkentext und geben dadurch den Trigger für den Mausklick wieder frei. Bewegt sich die Maus wieder vom Balken weg, erscheint auch der Balkentext wieder.

 

Beispiel XML-Datei

<anygantt>
  <settings>
    <navigation enabled="True" position="Top" size="30">
      <buttons collapse_expand_button="false" align="Near" />
      <text>Dienstplan</text>
      <font face="Verdana" size="10" bold="true" color="White" />
      <background>
        <fill type="Gradient">
          <gradient>
            <key color="#B0B0B0" position="0" />
            <key color="#A0A0A0" position="0.3" />
            <key color="#999999" position="0.5" />
            <key color="#A0A0A0" position="0.7" />
            <key color="#B0B0B0" position="1" />
          </gradient>
        </fill>
        <border type="Solid" color="#494949" />
      </background>
    </navigation>

    <locale>

<!-- A3:
     Defining months
     Using a german locale
-->
      <date_time_format week_starts_from_monday="True">
        <months>
          <names>Januar,Februar,März,April,Mai,Juni,Juli,August,September,
                        Oktober,November,Dezember</names>
          <short_names>Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez</short_names>
        </months>
        <!--
             <time am_string="AM" short_am_string="A" pm_string="PM" short_pm_string="P" />
        -->

<!-- A4:
     Defining general weekdays
     Holidays are defined in timeline > calendar > execptions

     A3: 
     Using a german date format
 -->
        <week_days>
          <names>Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag</names>
          <short_names>So,Mo,Di,Mi,Do,Fr,Sa</short_names>
        </week_days>
        <format>
          <full>%dd.%MM.%yyyy %HH24:%mi</full>
          <date>%dd.%MM.%yyyy</date>
          <time>%HH.%mm.</time>
        </format>
      </date_time_format>
    </locale>

  </settings>
  <datagrid width="180">
    <columns>
      <column attribute_name="RowNum" width="30">
        <header>
          <text>#</text>
        </header>
      </column>
      <column attribute_name="Name" cell_align="LEFTLEVELPADDING" padding="0" 
            width="150">
        <header>
          <text>Mitarbeiter</text>
        </header>
      </column>
    </columns>
  </datagrid>

  <timeline>

<!-- A1:
     The beginning and the end of the time frame
     This is set dynamically based on the day entered by the user
-->

    <scale start="18.08.2014" end="22.09.2014"
           show_start="01.09.2014" show_end="08.09.2014"
           />

    <plot line_height="30" item_height="20" item_padding="5" >

<!-- A4:
     Render weekends properly
     note the opacity value
-->
      <non_working_days show="true">
        <fill enabled="true" type="Solid" color="#aa2222" opacity="0.1" />
      </non_working_days>
      <grid>
        <vertical>
          <line enabled="true" color="DarkSeaGreen" thickness="1" opacity="0.2"/>

<!-- Do not fill even and odd columns
      this is just a hint for the feature
-->
          <!--
          <even>
            <fill enabled="true" color="DarkSeaGreen" opacity="0.5" />
          </even>
          <odd>
            <fill enabled="true" color="White" opacity="1" />
          </odd>
           -->

        </vertical>
      </grid>
    </plot>

<!-- A4: Defining holidays
     general weekdays are defined in 
     settings > locale > date_time_format > week_days
     The actual holidays are rendered dynamically based on time frame 
     that the user has entered
     e.g. check: 
 http://ora-sql-plsql.blogspot.de/2012/04/ermittlung-von-feiertagen-per-table.html
 -->
    <calendar>
      <exceptions>

<exception is_working="false" start_date="01.01.2014" end_date="01.01.2014" />
<exception is_working="false" start_date="06.01.2014" end_date="06.01.2014" />
<exception is_working="false" start_date="27.02.2014" end_date="27.02.2014" />
<exception is_working="false" start_date="03.03.2014" end_date="03.03.2014" />
<exception is_working="false" start_date="17.04.2014" end_date="17.04.2014" />
<exception is_working="false" start_date="18.04.2014" end_date="18.04.2014" />
<exception is_working="false" start_date="21.04.2014" end_date="21.04.2014" />
<exception is_working="false" start_date="01.05.2014" end_date="01.05.2014" />
<exception is_working="false" start_date="29.05.2014" end_date="29.05.2014" />
<exception is_working="false" start_date="09.06.2014" end_date="09.06.2014" />
<exception is_working="false" start_date="19.06.2014" end_date="19.06.2014" />
<exception is_working="false" start_date="15.08.2014" end_date="15.08.2014" />
<exception is_working="false" start_date="03.10.2014" end_date="03.10.2014" />
<exception is_working="false" start_date="31.10.2014" end_date="31.10.2014" />
<exception is_working="false" start_date="01.11.2014" end_date="01.11.2014" />
<exception is_working="false" start_date="25.12.2014" end_date="25.12.2014" />
<exception is_working="false" start_date="26.12.2014" end_date="26.12.2014" />
         </exceptions>
    </calendar>
  </timeline>


  <styles>
    <period_styles>
    <!--
        A5: 
        I have found no way to set the color-values dynamically
        My workaround: Check dynamically which colors need to
        be rendered in the entered time frame and dynamically create
        an own period style for each color.
        Each period style is an exact copy of this style only distinguished
        by its color-values.        
        
        The gradients are computed via plsql using my object type UI_COLOR
        https://jmarre.wordpress.com/2014/08/16/blending-colors-in-oracle-plsql
    -->
      <period_style name="#FF3300_TIMES0">

        <bar_style>

          <middle shape="Full">
            <border enabled="true" type="Solid" thickness="1" color="#400D00" />
            <fill enabled="true" type="Gradient">
              <gradient angle="-90">

                <key color="#FF9980" position="0" />
                <key color="#FF3300" position="1" />
              </gradient>
            </fill>
          </middle>

          <labels>
    <!--
        A6: 
        Each bar defines its own title using AnyChart attributes
    -->          
            <label anchor="Center" valign="Center" halign="Center">
                 <font face="Verdana" size="10" bold="true" color="Black" />
                 <text><![CDATA[{%Title}]]></text>
            </label>
          </labels>
          <states>
        <!--
             A7:
             The specific bar style for hover events
        -->
            <hover>
              <middle>
                <border enabled="true" type="Solid" thickness="2" color="#661400" />
                <fill enabled="true" type="Gradient">
    <!--
        A5: 
        On hover the gradient is turned upside down
        Again, the gradients are computed via plsql using my object type UI_COLOR
        https://jmarre.wordpress.com/2014/08/16/blending-colors-in-oracle-plsql
    -->                
                  <gradient angle="90">
                    <key color="#FF9980" position="0" />
                    <key color="#FF3300" position="1" />
                  </gradient>
                </fill>
              </middle>

    <!-- A7, A8:
         This is a workaround:
         I could not find any way to render both for each bar: link and label
         Somehow the label overwrites the link for the bar
         The workaround here is to remove the label on hover
         This makes the link somehow visible again
    -->
              <labels>
                <label anchor="Center" valign="Center" halign="Center">
                     <text></text>
                </label>
              </labels>

            </hover>
          </states>
        </bar_style>

        <!-- 
              A7:
              The actual tooltip on mouse hover
              Each bar defines its own title using AnyChart attributes
        -->
        
        <tooltip enabled="true" name="periodTooltip">
           <text><![CDATA[{%Longtitle}
{%Name}
{%Week} {%StartDay}
{%Time}
{%Comment}]]>
           </text>
           <font color="Black"/>
           <fill enabled="true" type="Solid" color="White" opacity="0.9" />
           <border enabled="true" type="Solid" color="DarkRed" thickness="1" />
           <margin left="10" top="2" right="10" bottom="2" />
        </tooltip>
      </period_style>
      
<!-- 
   ....
   more period styles for other colors
   ----
-->   

    </period_styles>
  </styles>
  <resource_chart>
<resources>
  <resource id="100063" name="BLAKE" />
  <resource id="100061" name="CLARK" />
  <resource id="100068" name="JONES" />
  <resource id="100060" name="SCOTT" />
  <resource id="100073" name="FORD" />
  <resource id="100064" name="TURNER" />
  <resource id="100062" name="HARPER" />
</resources>

<periods>
   <period resource_id="100073" start="15.09.2014 09:00" end="15.09.2014 17:00" 
              name="test" style="#7D7DBE_TIMES0">
   <actions>
     <action type="navigateToURL" url="https://apextipps.wordpress.com/?x=110184," 
              target="_self"/></actions>
    <attributes>
      <attribute name="Title">U</attribute>
      <attribute name="Longtitle">Urlaub</attribute>
      <attribute name="StartDay">15.09.2014</attribute>
      <attribute name="Week">KW38</attribute>
      <attribute name="Time"></attribute>
      <attribute name="Comment"></attribute>
    </attributes>
   </period>   
   
   <period resource_id="100073" start="16.09.2014 09:00" end="16.09.2014 17:00" 
              name="test" style="#7D7DBE_TIMES0">
   <actions>
      <action type="navigateToURL" url="https://apextipps.wordpress.com/?x=110185," 
              target="_self"/>
   </actions>
    <attributes>
      <attribute name="Title">U</attribute>
      <attribute name="Longtitle">Urlaub</attribute>
      <attribute name="StartDay">16.09.2014</attribute>
      <attribute name="Week">KW38</attribute>
      <attribute name="Time"></attribute>
      <attribute name="Comment"></attribute>

    </attributes>
   </period>  
   
<!-- more periods -->

   </periods>
  </resource_chart>
</anygantt>

Kategorien:APEX 4.0, Charting

Flexibler Resource-Gantt-Chart mit Oracle Apex und AnyChart / AnyGantt (1)

November 20, 2014 2 Kommentare

Die Standard Charting-Features von Oracle Apex bieten einen schnellen unkomplizierten Weg, um eine Vielzahl einfacher Charts zu erstellen. Sobald weitere Anforderungen dazukommen, stößt man allerdings schnell an die Grenzen der Apex-Wizards. Da der Oracle Apex – Umgebung aber die volle Funktionalität der AnyChart – Library zur Verfügung steht, lassen sich wesentlich anspruchsvollere Charts erstellen. Vereinfachend kommt hinzu, dass die AnyChart – Library über eine gute Online Dokumentation und XML-Referenz verfügt.

In den folgenden Beiträgen stelle ich ein entsprechendes Beispiel für einen Resource-Gantt mithilfe von AnyGantt vor.

Hier ist meine Demo-Seite zu dem Beispiel:

https://apex.oracle.com/pls/apex/f?p=DEMO_JENS_MARRE:ANY_GANTT

(Login über: demo/demo)

Dabei habe ich folgende Anforderungen an den Gantt-Chart in Apex gestellt:

  • A1 Der Resource-Gantt-Chart soll über Suchfelder gesteuert werden
  • A2 Wenn ein Suchfeld geändert wird, soll der Gantt-Chart „The-Ajax-Way“ aktualisiert werden
  • A3 Der Gantt-Chart soll auf deutsch sein
  • A4 Feiertage sollen markiert werden
  • A5 Es sollen mehrere Balkenfarben zur Verfügung stehen, die mit einem schönen Verlauf dargestellt werden sollen
  • A6 Im Balken soll eine Beschriftung erscheinen
  • A7 Per Maus-Hover soll bei Bedarf eine Info zum Balken ein- und ausgeblendet werden
  • A8 Per Mausklick auf den Balken, soll ein Zusatzdialog zum Gantt-Chart geöffnet werden.

In diesem Beitrag stelle ich den Rahmen für die händische Einbettung von AnyGantt in Apex vor. Im nächsten Beitrag, erweitere ich das Beispiel, um die weiteren Punkte dieser Anforderungsliste.

Das generelle Konzept für die Einbettung von AnyGantt in Apex ist Folgendes:

  • Für den Gantt-Chart wird ein leeres DIV-Element angelegt,
  • Die AnyGantt Flash-Datei wird im Page-Header per Javascript eingebunden
  • Der XML-Inhalt des Gantt-Charts wird über einen On-Demand-Prozess erzeugt und per Javascript abgerufen
  • Mit entsprechenden Apex-Items kann die Ansicht gesteuert werden. Für diese Auswahl-Items werden onchange Dynamic Actions angelegt, die den On-Demand-Prozess aufrufen

Zunächst benötigen Sie die AnyGantt-Dateien: AnyGantt.js, AnyGantt.swf

Laden Sie diese im ApexBuilder hoch unter

  • „Shared Components → Static Files“
  • Application: „No application“
  • Erstellen Sie eine neue Apex-Page
  • Klicken Sie auf Edit Page
  • Hinterlegen Sie die JavaScript-Datei unter „JavaScript → File URLs“
  • #WORKSPACE_IMAGES#AnyGantt.js
  • Die Flashdatei AnyGantt.swf wird später dynamisch über Javascript eingebunden

Legen Sie eine HTML-Region für die Suchkriterien an und legen Sie dort Page-Items an, z.B.

  • Name: P4_DAY
  • Display As: Datepicker

Legen Sie eine Dynamic Action für diese Page-Items an

  • Identification
  • Name: „REFRESH_GANTT“
  • When
  • Event : Change
  • Selection Type: Item
  • Item(s) : <die Page-Items, die Sie vorher angelegt haben>
  • True-Action
  • Action: Execure Javascript
  • Code: loadGantt();

    Bemerkung: Die Funktion loadGantt wird weiter unten beschrieben.

Legen Sie eine HTML-Region für den Gantt-Chart an und tragen Sie unter „Region Source:“ ein

<div id=”chartDiv” name=”chartDiv”></div>

Legen Sie einen neuen Apex Application Prozess an:

  • „Shared Components → Application Processes → Create“
  • Name: „generate_gantt_xml“
  • Point „On Demand Run“
  • Type: PL/SQL
  • Process Text

BEGIN
htp.p(‘
<anygantt>
<settings>
<navigation enabled=”True” position=”Top” size=”30″>
<buttons collapse_expand_button=”false” align=”Near” />
<text>Resource-Gantt Chart</text>
<font face=”Verdana” size=”10″ bold=”true” color=”White” />
</navigation>

<locale>

<date_time_format week_starts_from_monday=”True”>
<months>
<names>Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember</names>
<short_names>Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez</short_names>
</months>

<week_days>
<names>Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag</names>
<short_names>So,Mo,Di,Mi,Do,Fr,Sa</short_names>
</week_days>
<format>
<full>%dd.%MM.%yyyy %HH24:%mi</full>
<date>%dd.%MM.%yyyy</date>
<time>%HH.%mm.</time>
</format>
</date_time_format>
</locale>

</settings>
<datagrid width=”180″>
<columns>
<column attribute_name=”RowNum” width=”30″>
<header>
<text>#</text>
</header>
</column>
<column attribute_name=”Name” cell_align=”LEFTLEVELPADDING” padding=”0″ width=”150″>
<header>
<text>Mitarbeiter</text>
</header>
</column>
</columns>
</datagrid>

<timeline>

<scale start=”04.08.2014″ end=”08.09.2014″
show_start=”18.08.2014″ show_end=”25.08.2014″
/>

<plot line_height=”30″ item_height=”20″ item_padding=”5″ >

<non_working_days show=”true”>
<fill enabled=”true” type=”Solid” color=”#aa2222″ opacity=”0.1″ />
</non_working_days>
<grid>
<vertical>
<line enabled=”true” color=”DarkSeaGreen” thickness=”1″ opacity=”0.2″  />
</vertical>
</grid>
</plot>
</timeline>

<styles>
<period_styles>

</period_styles>
</styles>
<resource_chart>
<resources>
<resource id=”109697″ name=”BLAKE” />
<resource id=”109700″ name=”CLARK” />
<resource id=”109756″ name=”JONES” />
<resource id=”109755″ name=”SCOTT” />
<resource id=”109699″ name=”FORD” />
<resource id=”109698″ name=”TURNER” />
</resources>
<periods>
</periods>

</resource_chart>
</anygantt>
‘);
END;

Bekemerkung: Dies ist ein statischer Beispiel XML-Code, um das Gerüst für den Resource-Gantt Chart in Oracle Apex zum Laufen zu bringen. Im nächsten Beitrag erweitere ich den AnyGantt-XML-Code, um die eigentlichen Anforderungen an den Chart abzubilden.

Tragen Sie eine neue Javascript Funktion ein, die den AnyGantt-Chart initialisiert und die Funktion zum Laden des XML-Codes bereitstellt, im Page-Header unter

  • JavaScript → Function and Global Variable Declaration“

var chart= new AnyGantt(‘#WORKSPACE_IMAGES#AnyGantt.swf’);

with(chart) {
width = “100%”;  // skaliert die Anzeige dynamisch mit dem Browserfenster
height = “500”;
name = “#CHART_NAME#”;
initText = “#FLASH_INIT#”;
resourcesLoadingText = “#FLASH_RESOURCES#”;
noDataText = “#FLASH_NO_DATA#”;
templateLoadingText = “#FLASH_TEMPLATES#”;
// To allow jq modal windows
wMode = “transparent”;
src = “/i/flashchart/anygantt_4/swf/Preloader.swf”;
}

/*
* Calls the on-demand-prozess  generate_gantt_xml and posts all necessary data
* to retrieve the content
*/
function loadGantt() {
chart.setLoading(“Loading new chart…”);

$.post(‘wwv_flow.show’,
{
“p_request”: “APPLICATION_PROCESS=generate_gantt_xml”,
“p_flow_id”: $v(‘pFlowId’),
“p_flow_step_id”: $v(‘pFlowStepId’),
“p_instance”: $v(‘pInstance’),
“p_arg_names”: [“P4_DAY”],  /* List of your search parameters */
“p_arg_values”: [$v(“P4_DAY”)] /* $v-List of your  search parameters */
},
function success(data) {

try {
chart.setData(data.toString());
chart.write(‘chartDiv’);
}
catch(err) {
if(console && console.log) {
console.log(“Fehler: “+err);
console.log(err.message);
}
}
});
}

Kategorien:APEX 4.0, Charting

Einen leeren Apex Report ausblenden

September 19, 2014 1 Kommentar

Über Apex-Conditions lassen sich in vielen Fällen Page Regionen dynamisch aus- und einblenden. Ein Fall für den es leider keine Condition gibt, sind leere Reports. Über einen einfachen CSS-Trick lässt sich das beheben:

Unter Region-Definition des Apex- Reports eintragen:

  • “Static ID” : myReportRegion

Unter Report-Attributes des Apex-Reports eintragen:

  • “When No Data Found Message”:
  #myReportRegion {
     display:none;
  }

Kategorien:CSS, Reports

Integration von Jasper Reports in APEX-Listener mit Sun-Glassfish V3

Eine detaillierte Beschreibung zur Konfiguration einer Oracle Datasource in Glassfish finden Sie hier.

http://technology.amis.nl/blog/1276/configuring-a-oracle-datasource-on-glassfish

 

Hier nun eine Kurzbeschreibung, wie man Jaspar Reports in Glassfish integriert.

  1. Oracle Jar-File für JDBC-Driver in lib-Verzeichnis der jeweiligen Glassfish-Domain kopieren.
  2. Glassfish-Server rebooten
  3. Glassfish Admin-Website aufrufen und anmelden
  4. Connection-Pool anlegen
  5. Ping ausführen um die Verbindung zu testen.
  6. JDBC Resource anlegen

 

Kategorien:Allgemein

Interactive Reports als Modal Window in Oracle Apex

In Oracle Apex 4.2.5 Theme 25 „Blue Responsive“ gibt es ein Problem, wenn man versucht Interactive Reports als Modal Fenster einzubinden.

In diesem Fall scheint der „Actions“-Button ohne Funktion zu sein. Das Problem lässt sich einfach beheben, in dem man folgende CSS-Zeile in den Header der Apex page hinterlegt.

 

#apexir_ACTIONSMENU,#apexir_FORMAT_MENU,#apexir_ROWS_PER_PAGE_MENU {
  z-index: 1000;
}

 

Hier ein Demo (login als demo/demo):

https://apex.oracle.com/pls/apex/f?p=demo_jens_marre:zindex_issue

Kategorien:Allgemein

Eine Popup-Tree-Auswahl mit Oracle Apex erstellen

Juli 10, 2014 1 Kommentar

Anforderung

  • Eine platzsparende Tree-Auswahl in einem Oracle Apex-Report oder einer Oracle Apex Form. Die Tree-Auswahl soll erst bei Bedarf geöffnet werden.
  • Dabei soll die gesamte Funktionalität von dem Standard Apex-Tree erhalten bleiben. Insbesondere soll es möglich durch den Tree zu navigieren.
  • Der ausgewählte Wert soll immer eingeblendet werden.

Die Recherche nach einem Apex-Plugin für Trees brachte einige Anregungen aber nicht das gewünschte Ergebnis. Hier stelle ich ein Beispiel vor, wie man die Anforderungen größtenteils mit den Bordmitteln von Oracle Apex implementieren kann

  • über eine Tree-Region
  • die als Modal-Fenster angezeigt wird
  • Ein Display-Item zeigt, den aktuell ausgewählten Tree-Anzeigewert an und triggert das Modal-Fenster
  • Ein Hidden-Item enthält den aktuell ausgewählten internen Tree-Wert
  • Eine Dynamic Action für Clicks auf die Tree-Elemente (durch einen JQuery selector), setzt den internen und den angezeigten Tree-Wert neu und schließt das Modal-Fenster.

 

Demo

https://apex.oracle.com/pls/apex/f?p=DEMO_JENS_MARRE:TREE_POPUP

(Anmelden mit Username „demo“ / Passwort „demo“)

 

Hier sind die Schritte für den Apex Builder im Detail:

  • Eine Apex HTML-Region anlegen: „Choose from tree“
  • Ein Text Item „Display Only“ für den ausgewählten Tree-Anzeigewert anlegen
    • Name: P1_SELECTED_EMP_NAME
    • Type: Display Only
    • Default: „(Choose)“
  • Ein Hidden-Item anlegen, um den ausgewählten internen Tree-Wert zu hinterlegen
    • Name: P1_SELECTED_EMP_ID
    • not protected
  • Eine Apex Computation anlegen, um den angezeigten aus dem internen Tree-Wert eingangs zu ermitteln.
    • For item: P1_SELECTED_EMP_NAME
    • Type: SQL Query

SELECT ENAME

FROM EMP

WHERE EMPNO = to_number(:P1_SELECTED_EMP_ID);

  • Eine Apex Tree-Subregion von „Choose from tree“ anlegen
    • Type: „Tree“
    • Name: „Employees“
    • Region Template: „Modal Region“
    • Parent Region: „Choose from tree“
    • Static ID: „treeRegion“ (diese wird benötigt, um das Apex Modal-Fenster zu öffnen und zu schließen)
    • Tree Template: (what you like)
    • Table: EMP
    • ID EMPNO
    • *Parent ID : MGR
    • *Node Text : ENAME
    • *Start With MGR
    • *Start Tree: Value is Null
    • Order Sibling By: ENAME
    • Selected Node Page Item: P1_SELECTED_EMP_ID
  • Einen Triggerlink auf P1_SELECTED_EMP_NAME mit einer Apex Dynamic Action mittels JQuery wrappen. Eine Apex Dynamic Action „on Page load“ anlegen
    • Name: WRAP_OPEN_MODAL_TRIGGER
    • True Action: Execute Java Script Code

    $(“#P1_SELECTED_EMP_NAME”).css({“color”: “#3467a9″,”text-decoration”: “underline”}).wrap(“<a id=’modalTrigger’></a>”);

    $(“#modalTrigger”).attr(“href”,”#”).click( function() {

    openModal(“treeRegion”);

    });

  • Eine Apex Dynamic Action anlegen, als JQuery-Selector für Clicks auf Tree-Elemente
    • Name: SELECT_FROM_TREE
    • Event: Click
    • Selection Type: jQuery Selector
    • jQuery Selector: div.tree li > a
    • True – Action:
    • Action: Execute JavaScript Code
    • Fire on Page Load: no
    • Code:

    var selectedId = $(this.triggeringElement).parents(“li:first”).attr(“id”);

    var selectedName = $(this.triggeringElement).text();

    $(“#P1_SELECTED_EMP_NAME”).text(selectedName);

    closeModal(“treeModal”);

    $s(“P1_SELECTED_EMP_ID”, selectedId);

 

Ich verwende hier die „$s“-Funktion, anstatt der JQuery-Funktion „$“, damit der vollständige Funktionsumfang des Oracle Apex JS-Frameworks erhalten bleibt: z.B. um onchange Dynamic Actions auf  P1_SELECTED_EMP_ID zu triggern.

 

 

Kategorien:Allgemein
Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.