Archiv

Autor-Archiv

Reine HTML-Buttons verschwinden in Oracle APEX 5

Bei Migrationen von bestehenden Applikationen auf Oracle APEX 5 ist zu beachten, dass reine HTML-Buttons in APEX 5 nicht mehr unterstützt werden. Im APEX-Builder erscheint nun die Option „#DEFAULT_BUTTON_TEAMPLATE# (invalid)“. Das wird zum Problem, weil der HTML Button zur Laufzeit nicht mehr angezeigt wird.

Vor der Migration sollten die entsprechenden Buttons identifiziert und den Button-Style „Template Based“ inkl. entsprechendem Template erhalten, was sowieso schon immer die besserer Variante war.

Über folgenden Select im APEX-Dictionary können die problematischen Buttons herausgesucht werden:

select *

from APEX_APPLICATION_PAGE_BUTTONS

where BUTTON_TEMPLATE is null;

In den Releasenotes wird beschrieben, dass es ab Oracle APEX 5 nur noch Template Based Buttons gibt. Reine HTML Buttons würden automatisch umgesetzt: Ich vermute, dass das nicht den Application Import einbezieht.

https://docs.oracle.com/cd/E59726_01/doc.50/e39143/toc.htm#HTMRN249

 

Kategorien:APEX 5

Ein generisches Datenmodell für Organigramme

Als IT-Berater waren Volker und ich viele Jahre auch im Bereich Ressourcen- und Personalplanung tätig. Auf diesen Erfahrungen haben wir ein generisches Datenmodell für Organigramme entwickelt, das gerade im Bezug zur Ressourceplanung sehr gut funktioniert.

Im Folgenden stelle ich die grundlegenden technischen Anforderungen für das Datenmodell vor. Die fachlichen Anforderungen dahinter sollten direkt einleuchten, in Einzelfällen weise ich explizit darauf hin.

Ziel ist es ein hierarchisches Organigramm so flexibel wir möglich abzubilden, um die jeweiligen Organisationseinheiten und deren Hierarchien als Grundlage für die Klammerung von Personal, Ressourcen und Berechtigungen zu verwenden.

  • O1: Eine Organisation wird in Organisationseinheiten untergliedert, im Folgenden auch Personengruppen.
  • O2 a: Diese Personengruppen können in beliebig verschachtelten Hierarchien angeordnet sein, müssen es aber nicht
    • z.B. Firma Softcom → Hauptabteilung IT-Services → Abteilung Hosting → Unterabteilung Administration → Gruppe DBA
  • O2 b: Eine Person kann mehreren Gruppenhierarchien angehören. Insbesondere sind auch Querschnittsgruppen erlaubt.
    • z.B. ein abteilungsübergreifendes Projekt
  • O2 c: Dasselbe gilt für Personengruppen

    Die Anforderungen O2 a – O2 c verdeutlichen die fachlichen Hintergründe, lassen sich aber zu einer Anforderung O2 zusammenfassen:

  • O2: Personen und Personengruppen sollen wie „Emailverteiler“ strukturiert sein
    • Eine Person oder eine Personengruppe kann also mehreren Personengruppen angehören.
    • Dies ermöglicht auch hierarchische Strukturen, ist aber wesentlich flexibler.
    • Die Struktur kann über eine einfache Intersection Table von Personengruppen / Personen zu Personengruppen implementiert werden. Es empfiehlt sich Hierarchische Views für diese Struktur anzulegen.
  • R1: Ressourcen werden auch den Organisationseinheiten zugeordnet (in unserem Sprachgebrauch also den Personengruppen).
  • E1: Ein Ereignis kann einer Person zugeordnet sein (z.B. ein „Dienst“).
  • E2: Ein Ereignis kann auch einer Ressource zugeordnet sein.
  • E3: Ein Ereignis hat ein Start- und ein Ende-Zeitpunkt.
  • E4: Ein Ereignis kann ein Mutterereignis besitzen
    • Dadurch kann u.a. eine beliebig detaillierte Projektplanung implementiert werden: Für das Ereignis „IT-Messe“ an den Tagen 1 und 2 werden z.B. die Personen „Meier“ für Tag 1 und 2, „Müller“ für Tag 2 und die Ressourcen „PC“ und „Beamer“ für Tag 1 und 2 geplant.
  • E5: Ein Ereignis besitzt immer einen Ereignistypen
    • Ereignistypen können z.B. Dienstarten in der Personalplanung sein (z.B. Urlaub, Bereitschaft, Arbeitszeit).
  • E6: Ereignistypen werden auch den Organisationseinheiten zugeordnet (in unserem Sprachgebrauch also den Personengruppen). Dabei stehen Ereignistypen übergeordneter Gruppen in der Regel auch den untergeordneten Gruppen zur Verfügung (s.u.).
    • Beispiel: Zur Gruppe DBA gehört die spezielle Dienstart „Bereitschaft DBA“

       

      Die volle Flexibilität zeigt sich erst durch geeigneten Berechtigungen:

  • B1: Lese- und Schreibrechte werden hierarchisch gemäß den Personengruppen vergeben und vererbt
    • Im oberen Beispiel etwa: Schreibrechte für Ereignisse der Hauptabteilung IT-Services beinhalten auch immer Schreibrechte auf die jeweiligen Abteilungen, Unterabteilungen und Gruppen.
    • Die Granularität „Personengruppe“ ist hier wesentlich: So werden Rechte nicht für jede Ressource einzeln festgelegt. Durch die Zuordnung einer neuen Ressource zur Personengruppe, werden automatisch die Berechtigungen der anderen Ressourcen der Gruppe übernommen.
  • B2: Die Anwendergruppen und deren Berechtigungen können analog zu den Organisationseinheiten / Personengruppen strukturiert sein, müssen es aber nicht.
    • Dies muss näher erläutert werden: In der Regel werden die hinterlegten Personen des Organigramms auch Anwender des Systems sein. In der Unterabteilung Administration kann es eine Gruppe „Disponenten“ geben, die die gesamte Personal- und Ressourcenplanung für die ganze Abteilung Hosting durchführt. Den Disponenten können also Planungsrechte für alle Ereignisse der übergeordneten Abteilung Hosting vergeben werden, obwohl sie laut Organigramm nur einer Untergruppe der Abteilung zugeordnet sind.
    • Dadurch lässt sich etwa auch folgendes Konstrukt abbilden: Der Spediteur „Lieferschmidt AG“ hat alle LKWs in die „Lieferschmidt Ressourcen GmbH“ outgesourct. Die Gruppe „Disponenten“ in der Lieferschmidt AG haben trotzdem die Berechtigungen LKWs aus der GmbH für die Touren der AG-Mitarbeiter zu planen.
  • B3 a: Die Berechtigung bestimmte Ereignistypen zuzuweisen wird in der Regel in umgekehrte Richtung zu den Gruppenhierarchien vererbt.
    • Allgemeine Dienstarten wie „Arbeitszeit“, „Urlaub“ oder „Mutterschutz“ können also übergeordnet Firma Softcom zugeordnet werden. Die verschiedenen Disponentengruppen der einzelnen Abteilungen können alle diese Dienstarten vergeben, zusätzlich zu den speziellen Dienstarten den Untergruppen.
  • B3 b: Im Gegensatz dazu kann die Berechtigung bestimmte Ereignistypen zuzuweisen auch auf die direkt zugeordnete Gruppe eingeschränkt werden.
    • Disponenten dieser Personengruppe können also nur die Ereignistypen ihrer Disponenten-Gruppe zuweisen.
  • B4: Wurde ein Ereignis angelegt, ist die Gruppe des Ereignistypen maßgeblich für die Berechtigungen zur weiteren Bearbeitung
    • Hat ein Messeplaner einen Mitarbeiter der Gruppe „DBA“ für das abteilungsübergreifende Projekt „IT-Messe“ mit der Dienstart „Präsentation Messe“ eingeplant, kann nur ein anderer Messeplaner dies ändern. Ein Disponent der Gruppe DBA hat keine Schreibrechte auf den Dienst.
    • Damit solche Messeplaner dadurch keine Schreibrechte auf übergeordnete Dienste wie Urlaub erhalten, erhalten sie nur die eingeschränkten Rechte aus B3 b.
    • Dadurch werden die Berechtigungen auch von den Bewegungsdaten entkoppelt. Kommt es zu einer Neustrukturierung der Organisation, müssen nur die Stammdaten geändert werden, im besten Fall sogar nur die Bezeichnung der Personengruppen und die Hierarchien.

Für eine umfassende Personal- und Ressourcen-Planung gehören natürlich noch eine Vielzahl weiterer Anforderungen. Themen wie „Workflows“, „Schicht-Plan“ oder „Serienereignisse“ habe ich weggelassen. Ziel war es lediglich das Konzept der Personengruppen zu erläutern und wie man es exemplarisch in der Ressourceplanung einsetzen kann, um möglichst generisch Berechtigungen zu vergeben.

Es empfiehlt sich übrigens für den Zweck weitere Views anzulegen, die die Hierarchien auflösen, um z.B. Aspekte wie den folgenden zu kapseln: Für welche Personengruppen hat Mitarbeiter Meier Schreibrechte auf Dienste?

Man sollte darauf achten, dass das hierarchische Konzept mit Personengruppen nicht dazu mißbraucht wird, die relationale Idee auszuhebeln: Letztendlich könnten alle Personenattribute durch Gruppen wie „Frauen“ oder „Männer“ ersetzt werden

Kategorien:Allgemein

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, und in einem späteren Beitrag erläutere ich ein generisches Datenmodell für die Ressourcenplanung.

In diesem Beitrag, erweitere ich das Gantt-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, 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, 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

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