Seiten

Dienstag, 11. Mai 2010

BioScripter - Script Generator für Pipettierroboter v1

Im Rahmen seiner Doktorarbeit arbeitet Robert Schäwe, Wissenschaftler am UFZ, mit dem Pipettierroboter BioMek 2000.
Der Roboter muß in diesem Zusammenhang große Mengen von Probentöpfchen mit Bakterien verschiedenster Kulturen befüllen. Um ein möglichst genaues Versuchsergebnis zu erhalten, ist es erforderlich, die Proben in einem sehr kurzem Zeitraum zusammenzustellen.

Projektposter

Projektposter

Da die Standardsoftware des Roboters entsprechend optimierte Funktionen nicht bereithält, wurde Fachinformatiker-Azubi Andreas Menge mit der Erstellung eines neuen Programms beauftragt. Mit diesem werden nun die optimerten Pipettierschritte errechnet und an den Roboter gesandt.

Der BioMek beim Abarbeiten der optimierten Pipettierschritte.

Das Pipettierschema, aus dem hervorgeht in welchen Mengen welche Kulturen in die jeweiligen Probentöpfchen kommen, wird dabei als Excel-Arbeitsmappe bereitgestellt. Diese Datei wird vom BioScripter importiert und die Befüllungsmatrix durch einen Algorithmus umgewandelt, der sowohl die gewünschte Geschwindigkeit erzielt wie auch auch Kreuzkontamination verhindert.

Projektpräsentation

Projektpräsentation

Pressemitteilung in der Leipziger Volkszeitung

Pressemitteilung in der Leipziger Volkszeitung vom 21.05.2010

Aus Zeitgründen wurde das C#-Programm für diesen speziellen Versuch entwickelt und lässt momentan keine weiteren Anwendungen zu. Deshalb wird das Programm zur Zeit weiterentwickelt. Dabei wird vor allem Punkto Benutzerfreundlichkeit und Flexibilität einiges verbessert werden.

Dienstag, 20. April 2010

SVG: Im <OBJECT>-Tag anklickbar machen (Teil 2)

Hier noch eine weitere Variante, welche praktikabler bei Funktionsaufrufen ist. Auf die Größenangabe über den DIV-Selektor wurde hier verzichtet, weiterhin wird als Parent-Element der <SPAN>-Tag verwendet, was ein horizontales Aneinanderreihen der Grafiken erleichtert.
Die hier beschriebene Methode (ohne style-Attribut) ist von der Struktur her, etwas sauberer.

Beispiel

http://www.ufz.de/
<span class="svglink" style="width: 167px; height: 77px;">
  <a href="javascript:alert('Test')" style="width: 167px; height: 77px;"></a>
  <object data="logo.svg" type="image/svg+xml" width="167" height="77">
    <param name="src" value="logo.svg" />
    <param name="wmode" value="transparent" />
    <img src="logo.jpg" alt="http://www.ufz.de/" width="167" height="77" />
  </object>      
</span>

Die CSS-Klasse svglink

.svglink {
  position: relative;
}
span.svglink  {
  display: inline-block;
}
/* IE7-Hack, gleicht das Aussehen etwas an */
*+html .svglink  {
  padding: 0 0.25em 0.25em 0;
}  
.svglink a, .svglink object  {
  position: absolute;
  width: inherit;
  height: inherit;
}  
.svglink a  {
  /* Die (beiden!) Eigenschaften sind wichtig,
  da im IE die SVG sonst nicht anklickbar ist. */ 
  background-image: url(1x1transp.gif);
  background-color: transparent;
  z-index: 2;
}
.svglink object  {
  z-index: 1;
}

Der Unterschied zu der hier beschriebenen Methode, ist die Zuweisung von display: inline-block; bei der Verwendung des <SPAN>-Tags.
Es kann parallel mit dem <DIV>-Tag gearbeitet werden.

Download Beispiel komplettes Beispiel

Flash: Audio-Datei in Webseite einbetten

Nachfolgend beschreibe ich Schritt für Schritt das Erzeugen und Einbetten einer Audiodatei als Flash-Objekt in eine Webseite.

  1. Die Audiodatei am besten im WAV-Format z.B. mit einem Handy aufzeichnen (wenn möglich gleich als MP3 speichern).
  2. Liegt bereits eine MP3-Datei vor, kann dieser Punkt übersprungen werden. Jetzt sollte ein geeigneter MP3-Encoder gefunden werden, hier kann man z.B. den WAV to MP3 Converter benutzen (Herstellerseite).
    Die Bedienung des Tools ist einfach, die gewünschten Dateien über Add hinzufügen und dann Start klicken.
  3. Verringern Sie die Bit- und Samplerate (max.160kbs/44100Hz) wie im Screenshot dargestellt, sonst kann es später Probleme beim Import nach Flash geben.

Erforderliche Einstellungen im MP3-Encoder

  1. Ist die MP3-Datei erzeugt geht es im Adobe Flash, mit der im unten angehängten Download enthaltenen FLA-Datei, weiter. Öffnen Sie die FLA-Datei und wählen Datei->Importieren->In Bibliothek importieren....
    Anschließend sollte Ihre MP3 in der Bibliothek (Bibliothek evtl. mit STRG + L oder Fenster->Bibliothek öffnen) sichtbar sein. Im Kontextmenü (rechte Maustaste) der Datei wählen Sie Eigenschaften und nehmen die im Screenshot dargestellten Einstellungen vor (sind die gezeigten Einstellungen nicht sichtbar klicken Sie auf die Schaltfläche Erweitert).
var soundToLoad:Sound = new Sound();
startSound_btn.onRelease = function() {
  var song = songName_txt.text;
  soundToLoad.attachSound("mp3");
  soundToLoad.start(0,1);
};

In der FLA-Datei integriertes Actionscript, welches die MP3-Datei beim Klick
auf das Symbol abspielt.

Aktivieren Sie die Checkbox Export für Actionscript
und tragen Sie unter Bezeichner "mp3" ein.
  1. Sollte die endgültige Soundqualität nicht ausreichen, können Sie im oben gezeigten Dialogfeld unter dem Dropdown Komprimierung->MP3 weitere Einstellungen vornehmen.
  2. Anschließend wird das Projekt mit den Standardeinstellungen über Datei->Veröffentlichen veröffentlicht, dabei sollte am Speicherort der FLA-Datei eine SWF- und eine HTML-Datei entstehen.
  3. Jetzt muss die SWF-Datei auf den Webserver hochgeladen werden. Am UFZ benutzt man dazu das Bild- und Filearchiv im administrativen Intranetbereich. Man lädt hier die SWF-Datei hoch und erhält eine ID (z.B. 123456).
  4. Jetzt muss man die hochgeladene Datei noch in der HTML-Datei referenzieren. Entweder die beim Veröffentlichen entstandene HTML-Datei verwenden oder den unten stehenden Quelltext (am UFZ wird für die Dateireferenz das Makro getfiler verwendet).

HTML-Quelltext zur Verwendung auf der eigenen Webseite

Achten Sie bitte darauf, dass die SWF-Datei zweimal referenziert werden muss, hier mit Hilfe des Makroaufrufs [§fgetfiler_123456§] (UFZ-Beispiel, ansonsten ohne das Makro z.B. http://www.foo.com/data/sample123456.swf).

Symbol anklicken um den Sound zu hören

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="37" height="41" id="erste_sahne" align="middle">
  <param name="allowScriptAccess" value="sameDomain" />
  <param name="movie" value="[§fgetfiler_123456§]" />
  <param name="quality" value="high" />
  <param name="bgcolor" value="#ffffff" />
  <embed src="[§fgetfiler_123456§]" quality="high" bgcolor="#ffffff" width="37" height="41" name="erste_sahne" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
Download Beispiel 3 komplettes Beispiel

Donnerstag, 15. April 2010

HTML/Javascript: Framegröße an Inhalte anpassen

Das folgende Beispiel kann genutzt werden, wenn man die Framegröße automatisch an wechselnde Inhaltgrößen anpassen möchte. Ein Anwendungsfall wären die UFZ-Adventskalender, welche sich als Popup öffnen und Kalender unterschiedlicher Größe zeigen. Frames sind hier deshalb eingesetzt weil sich das Menü zum Wechseln der Kalender in einem solchen befindet (siehe Screenshots).

Download komplettes Beispiel komplettes Beispiel

Dienstag, 13. April 2010

PHP: File Upload

<?php
$message = "";

if(isset($_FILES["file_name"])) 
{
  $upload_dir = "/www/data/";
  $file_name  = $_FILES['file_name']['name'];
  $file_temp  = $_FILES['file_name']['tmp_name'];
  
  move_uploaded_file($file_temp,
                     $upload_dir.$file_name);

  $message = "

$file_name ($file_size Byte) nach $upload_dir hochgeladen!

"; } $html = <<< HTML <form action="{$_SERVER['PHP_SELF']}" enctype="multipart/form-data" method="post"> <input type="file" name="file_name" /> <input type="submit" value="Kopieren" /> </form> HTML; echo $html.$message; ?>

Freitag, 9. April 2010

SVG: Im <OBJECT>-Tag anklickbar machen (Teil 1)

Wer eine SVG wie im Post beschrieben einbettet, wird feststellen das man die Grafik nicht wie gewohnt verlinken kann (Beispiel 1). Auch die Verwendung eines onklick-Events führt nicht zum Erfolg (Beispiel 2).

Beispiel 1 (funktioniert nicht)

http://www.ufz.de/
<a href="javascript:alert('Test')">
  <object data="logo.svg" type="image/svg+xml" width="167" height="77">
    <param name="src" value="logo.svg" />
    <param name="wmode" value="transparent" />
    <img src="logo.jpg" alt="http://www.ufz.de/" width="167" height="77" />
  </object>
</a>

Beispiel 2 (funktioniert nicht)

http://www.ufz.de/
<object data="logo.svg" onlick="alert('Test')" type="image/svg+xml" width="167" height="77">
  <param name="src" value="logo.svg" />
  <param name="wmode" value="transparent" />
  <img src="logo.jpg" alt="http://www.ufz.de/" width="167" height="77" />
</object>

Abhilfe schafft hier das Schließen des Links (</a>) vor dem OBJECT-Tag und etwas CSS.

Beispiel 3 (funktioniert)

<div class="svglink" id="svg1">
  <a href="javascript:alert('Test')"></a>
  <object data="logo.svg" type="image/svg+xml" width="167" height="77">
    <param name="src" value="logo.svg" />
    <param name="wmode" value="transparent" />
    <img src="logo.jpg" alt="http://www.ufz.de/" width="167" height="77" />
  </object>
</div>

Der Parameter wmode ist hier nicht optional, sonst ist die Grafik im IE nicht anklickbar.

Die CSS-Klasse svglink

.svglink {
  position: relative;
}  
.svglink a, .svglink object  {
  position: absolute;
  width: inherit;
  height: inherit;
}  
.svglink a  {
  /* Die (beiden!) Eigenschaften sind wichtig,
  da im IE die SVG sonst nicht anklickbar ist. */ 
  background-image: url(1x1transp.gif);
  background-color: transparent;
  z-index: 2;
}
.svglink object  {
  z-index: 1;
}

Bei dem als Hintergrund verwendeten Bitmap, handelt es sich um eine 1 Pixel große GIF-Datei mit eingeschalteter Transparenz (sie ist auch Teil des unten angehängten Downloads).

Weitere CSS-Eigenschaften

Die Größe der Grafik sollte in em angegeben werden, um ein korrektes Skalieren zu ermöglichen. Deshalb ist hier ein weiterer DIV-Selektor notwendig, dieser korrigiert gleichzeitig das Fehlen der inherit CSS-Eigenschaft im IE6 und IE7.

/* IE7 kennt kein inherit,
daher der zusätzliche Selektor */
#svg1, #svg1 a  {  
  width: 10.4375em;  /* 167px / 16 = 10,4375em */  
  height: 4.8125em;  /*  77px / 16 =  4,8125em */ 
}
Download Beispiel 3 komplettes Beispiel

Mittwoch, 7. April 2010

SVG: Crossbrowser kompatibel & XHTML valide referenzieren

Alle Standardbrowser können mehr oder weniger mit SVG-Grafiken umgehen. Der IE, im Moment nur über das Adobe SVG-Plugin (Adobe SVG-Viewer 3.03). Ab Version 9 des IE ist hier Besserung in Sicht und damit haben SVG-Grafiken eine gute Chance, sich weiter zu verbreiten.

http://www.ufz.de/ SVG-Grafik
<object data="logo.svg" type="image/svg+xml" width="167" height="77">
  <param name="src" value="logo.svg" />
  <param name="wmode" value="transparent" />
  <img src="logo.jpg" alt="http://www.ufz.de/" width="167" height="77" />
</object>

Der erste eingebettete param-Tag dient den IE's als Referenz auf die Datei. Der Internet Explorer kann hier keine Dateien per HTTP referenzieren!? Deshalb wird die Beispieldatei hier auch nicht angezeigt. Das sollte aber in der Praxis nicht weiter stören.
Der Parameter wmode ist optional, gleicht aber das Verhalten der SVG's im IE, an dass, der anderen Browser an.
Der eingebettete img-Tag wird benutzt wenn der Browser keine SVG anzeigen kann.

Dienstag, 6. April 2010

Excel/VBA: Letze Zeile mit Wert ermitteln

Der Methode Cells wird hier der zu untersuchende Bereich übergeben: Rows.Count = 65536 und die Spalte 1 = A
MsgBox(ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row)

Freitag, 19. März 2010

Oracle: DROP TABLE OR SEQUENCE IF EXIST als Prozedur

CREATE OR REPLACE
PROCEDURE drop_object
(
ObjName IN VARCHAR2
) IS
counter  NUMBER := 0;
to_drp   VARCHAR2(200) := UPPER(ObjName);
drp_stmt VARCHAR2(200) := NULL;

BEGIN

  SELECT COUNT(*)
  INTO counter
  FROM user_tables
  WHERE table_name = to_drp;
  IF counter = 1
  THEN drp_stmt := 'Drop Table ' || to_drp;
  EXECUTE IMMEDIATE drp_stmt;
  END IF;

  SELECT COUNT(*) INTO counter
  FROM user_sequences
  WHERE sequence_name = to_drp;
  IF counter = 1 THEN
    drp_stmt := 'DROP SEQUENCE ' || to_drp;
    EXECUTE IMMEDIATE drp_stmt;
  END IF;

END DROP_OBJECT;
/

Der Aufruf der Prozedur könnte z.B. so erfolgen:

CALL drop_object('t_test');
CREATE TABLE t_test
(
  test_id NUMBER, uid_text VARCHAR2(80)
)

Oracle: LDAP ID als Primärschlüssel

CALL drop_object('t_test');
CREATE TABLE t_test (test_id NUMBER, uid_text VARCHAR2(80));

CREATE OR REPLACE TRIGGER insert_lid
BEFORE INSERT ON t_test FOR EACH ROW

DECLARE
l_ldap_host  VARCHAR2(256) := 'hera.leipzig.ufz.de';
l_ldap_port  VARCHAR2(256) := '389';
l_ldap_base  VARCHAR2(256) := 'dc=ufz,dc=de';
l_retval     PLS_INTEGER; 
l_session    DBMS_LDAP.session;
l_attrs      DBMS_LDAP.string_collection;
l_message    DBMS_LDAP.message;
l_entry      DBMS_LDAP.message;
l_vals       DBMS_LDAP.string_collection;

BEGIN    
l_session := DBMS_LDAP.init
(
  hostname => l_ldap_host,
  portnum  => l_ldap_port
);

l_retval := DBMS_LDAP.simple_bind_s
(
  ld     => l_session,
  dn     => NULL,
  passwd => NULL
);
                                      
l_attrs(0) := 'uidNumber'; 
l_retval := DBMS_LDAP.search_s
(
  ld       => l_session, 
  base     => l_ldap_base, 
  scope    => DBMS_LDAP.SCOPE_SUBTREE,
  filter   => '(&
                 (nsrole=*roleself*)
                 (objectClass=ufzperson)
                 (uid=' || :new.uid_text || ')
               )',
  attrs    => l_attrs,
  attronly => 0,
  res      => l_message
);   

IF DBMS_LDAP.count_entries
(
  ld => l_session,
  msg => l_message
) = 1
THEN
  l_entry := DBMS_LDAP.first_entry
  (
    ld  => l_session,
    msg => l_message
  );
                                                                                 
  l_vals := DBMS_LDAP.get_values
  (
    ld        => l_session,
    ldapentry => l_entry,
    attr      => l_attrs(0)
  );
END IF;                                     
  
DBMS_OUTPUT.PUT_LINE
(
  l_attrs(0) || ' = ' || l_vals(0)
);
  
l_retval := DBMS_LDAP.unbind_s
(
  ld => l_session
);
  
:new.test_id := l_vals(0);

END;
/

Der Trigger könnte z.B. so ausgelöst werden:

INSERT INTO t_test (uid_text)
VALUES ('dutzend');

Oracle: Per Trigger Website in CLOB einlesen

Die Tabelle für das Beispiel

CREATE TABLE t_pages
(
  pages_id NUMBER,
  url VARCHAR2(80),
  page CLOB
);

-- Sequenz für das Increment der ID
CREATE SEQUENCE t_pages_autoinc_seq
START WITH 1
INCREMENT BY 1
NOCACHE
NOMAXVALUE;

-- Trigger zum Aufruf der Sequenz
CREATE OR REPLACE TRIGGER t_pages_autoinc_trigger
BEFORE INSERT ON t_pages
FOR EACH ROW
BEGIN
  SELECT t_pages_autoinc_seq.NEXTVAL INTO :NEW.pages_id FROM DUAL; 
END;
/

Der Trigger löst folgendes aus:
Wird ein Datensatz mit einer gültigen URL eingefügt, wird die Seite aufgerufen und der HTML-Code der Seite in den CLOB geschrieben.

CREATE OR REPLACE TRIGGER insert_page
BEFORE INSERT ON t_pages
FOR EACH ROW

DECLARE 
  req  UTL_HTTP.req;
  resp UTL_HTTP.resp;
  page CLOB;

BEGIN    
  -- Verbindung aufbauen
  req := UTL_HTTP.begin_request(:new.url);
  -- Als Mozilla ausgeben
  UTL_HTTP.set_header(req, 'User-Agent', 'Mozilla/4.0');       

  resp := UTL_HTTP.get_response(req);    
  UTL_HTTP.read_text(resp, page, 50000);             
  UTL_HTTP.end_response(resp);    
  :new.page := page;  
   
EXCEPTION
  WHEN
    UTL_HTTP.end_of_body
  THEN
    UTL_HTTP.end_response(resp);
END;
/

Beispiel zum Aufrufen des Ganzen

INSERT INTO t_pages (url)
VALUES ('http://www.ufz.de/');

Oracle: LDAP Connection mi dem DBMS_LDAP Package

Im folgenden wird das DBMS_LDAP-Package genutzt um auf einen LDAP-Server anonym zuzugreifen und den Wert eines bestimmten Attributes zu lesen.
Konkret ist das hier die LDAP-ID (uidNumber) einer bestimmten Person (ufzperson).
Die ID der Person soll später als Primärschlüssel in einer Oracle-Tabelle genutzt werden.

DECLARE
l_ldap_host VARCHAR2(256) := 'hera.leipzig.ufz.de';
l_ldap_port VARCHAR2(256) := '389';  -- varchar?
l_ldap_base VARCHAR2(256) := 'dc=ufz,dc=de';

l_retval  PLS_INTEGER; 
l_session DBMS_LDAP.session;
l_attrs   DBMS_LDAP.string_collection;
l_message DBMS_LDAP.message;
l_entry   DBMS_LDAP.message;
l_vals    DBMS_LDAP.string_collection;  
    
BEGIN
l_session := DBMS_LDAP.init
(
  hostname => l_ldap_host,
  portnum  => l_ldap_port
);

-- anonym anmelden
l_retval := DBMS_LDAP.simple_bind_s
(
  ld     => l_session,
  dn     => NULL,
  passwd => NULL
);

-- Attribut welches gesucht wird                                      
l_attrs(0) := 'uidNumber'; 

-- Suche ausführen
l_retval := DBMS_LDAP.search_s
(
  ld       => l_session, 
  base     => l_ldap_base, 
  scope    => DBMS_LDAP.SCOPE_SUBTREE,
  filter   => '(&(nsrole=*roleself*)(objectClass=ufzperson)(uid=dutzend))',
  attrs    => l_attrs,
  attronly => 0,
  res      => l_message);  
 
-- Wenn genau ein Eintrag gefunden wurde
IF DBMS_LDAP.count_entries(ld => l_session, msg => l_message) = 1
THEN
  -- ersten Eintrag festlegen
  l_entry := DBMS_LDAP.first_entry
  (
    ld  => l_session,
    msg => l_message
  );
  
  -- Wert des Eintrages holen                                         
  l_vals := DBMS_LDAP.get_values
  (
    ld        => l_session,
    ldapentry => l_entry,
    attr      => l_attrs(0)
  );
END IF;                                     

-- Attribut und Wert ausgeben  
DBMS_OUTPUT.PUT_LINE(l_attrs(0) || ' = ' || l_vals(0));

-- Verbindung beenden  
l_retval := DBMS_LDAP.unbind_s(ld => l_session);
END;
/

Dienstag, 16. März 2010

Linux/Solaris: Dateiname in Textdateien einfügen

Um den jeweiligen Dateinamen in die 2. Zeile aller Textdateien einzufügen, die in einem Verzeichnis liegen, kann man folgende Befehlszeile im verwenden:

for i in *.*; do sed -i "2i\\$i" $i; done

Freitag, 12. März 2010

Linux/Solaris: Text in Dateien einfügen

  1. Terminal öffnen
  2. in den Ordner mit den Dateien wechseln (cd)
  3. sed -i "1i\Text in Zeile 1" *.mop
  4. sed -i "2i\Text in Zeile 2" *.mop

Donnerstag, 11. März 2010

Windows 7: Test auf Unterstützung des TRIM-Befehls

fsutil behavior query disabledeletenotify

Die Meldung DisableDeleteNotify = 0 bedeutet in diesem Fall die Aktierung von Trim, eine DisableDeleteNotify = 1 würde seine Deaktivierung signalisieren.

Dienstag, 9. März 2010

COMA: Linkicons und Glossarlinks setzen

Um z.B. in AJAX-Responses nachträglich die Glossarlinks und Linkicons zu setzen, kann man nachfolgende COMA-Funktion nutzen:
$html = "HTML mit Glossarbegriffen und Hyperlinks";
$root_pid = 28;
$html = linkicon_glossar_parser($html, $root_pid); 
Der Parameter $root_pid entscheidet über das Setzen der Glossarlinks (Knoten 1 JA, Knoten 9 NEIN ?).

Zu ersetzende Linkicons müssen in einem DIV mit der ID content stehen (nicht valide da ID dann doppelt)!

COMA: Makroparser aufrufen

Um z.B. Makros in AJAX-Responses zu parsen, kann man nachfolgende COMA-Funktion nutzen:
$html = "HTML mit einem Makro (§fgetpic_9908§)";
$html = ersetze_funktionstemplates
        (
          $conn, $sprache, $code, $html,
          $baum, $adm, $su, $pub
        );

Montag, 8. März 2010

USB Stick ohne Floppy bootfähig machen

Man braucht für diese Methode eigentlich eine startfähige Diskette im Laufwerk, als Ersatz kann man Virtual Floppy Drive verwenden.
  1. ZIP-Datei an beliebiger Stelle entpacken
  2. VFDWIN.EXE als Administrator starten
  3. unter Driver auf Start klicken
  4. bei Drive 0 oder Drive 1 mittels Change den Laufwerksbuchstaben auf A setzen
  5. Open und dann Create klicken (Image File bleibt leer)
  6. im Explorer den Arbeitsplatz öffnen
  7. Rechtsklick auf 3½-Diskette A - Formatieren - MS-DOS Startdiskette erstellen

Systemdateien auf den Stick übertragen

  1. HP USB Disk Storage Format Tool installieren
  2. USB Stick einstecken
  3. HP Tool starten
  4. Bei Device den Stick wählen
  5. Häkchen bei Create DOS Startup Diskusing DOS System Files located at A:\ machen

Mittwoch, 3. März 2010

COMA: Cron-Job einrichten

Als erstes erstellt man das Makro, welches per Cron aufgerufen werden soll.
Danach sucht man sich in der Datenbank die Tabelle W_CJ.
In die Spalte CJ_FKT trägt man den Namen des Makros ein.
Der Eintrag in die Spalte CJ_EXE besteht aus 4 Ziffern, die ersten beiden geben die volle Stunde an, in der das Makro gestartet wird (00 - 23).
Die beiden letzten stehen für den Tag im Monat an dem man das Makro ausführen möchte (01 - 31), 00 steht dabei für täglich.
In der Spalte CJ_LAST_EXE wird das Datum gesetzt an dem der Job zum letzten Mal ausgeführt wurde.

Sonntag, 28. Februar 2010

Oracle: Autoincrement Workarounds

Da ORACLE den z.B. aus MySQL bekannten Datentyp auto_increment nicht kennt, nachfolgend zwei Möglichkeiten das auch in Oracle zu ermöglichen.

Tabelle für die Beispiele

create table teach_clients
(
  id_clients number not null,
  vorname varchar2(255),
  zuname varchar2(255) not null,
  geburtsdatum date,
  constraint teach_clients_pk primary key
  (
    id_clients
  )
  enable
)

Unterabfrage mit Aggregatfunktion

insert into teach_clients
values
(
  (
    select
      case
        when max(id_clients) >= 1
        then to_char(max(id_clients) + 1)
        else to_char(1)
      end
      from teach_clients
  ),
  'René',
  'Tuchscherer',
  '10.02.1963'
)

Trigger und Sequenz

Man erzeugt zwei zusätzliche Datenbankobjekte, eine Sequence und einen Trigger. Die Sequenz erzeugt die einzusetzenden Werte, der before-insert Trigger sorgt dafür, dass der neue Wert als erstes in der neuen Zeile landet.
-- die Sequenz erzeugen
create sequence id_clients_seq
start with 1
increment by 1
nomaxvalue;

-- den Trigger erzeugen 
create trigger id_clients_trigger
before insert on teach_clients
for each row
begin
select id_clients_seq.nextval
into :new.id_client
from dual;
end; 
Man kann statt start with 1 auch eine andere Zahl einsetzen, mit der begonnen werden soll. Das increment by 1 kann man eigentlich weglassen, weil es die Default-Einstellung ist. Der Parameter nomaxvalue sagt der Sequence, das sie für immer und ewig zu inkrementieren hat und nicht an irgendeinem Wert ein Reset machen soll.

Es kann übrigens durchaus sein, dass Zahlen "übersprungen" werden, weil sie von Oracle im Cache gehalten werden, um die Eindeutigkeit zu sichern. Wenn man also lückenlos aufsteigende Nummern haben muss, wäre dieser Ansatz nicht ausreichend.

Mittwoch, 20. Januar 2010

COMA: Angemeldeten User ermitteln

SELECT a.lid, b.login_name FROM web.wsess a, web.wlogin b WHERE sid = $sess_id AND a.lid = b.lid

Oracle: Password ändern

ALTER USER name IDENTIFIED BY "password";

COMA: Bild dynamisch erzeugen

Hier als Makro umgesetzt, welches so aufgerufen wird:
[§fcreatePic§]

function createPic()  {

  if(isset($_REQUEST['getpic']) &&
    Header("Content-type: image/jpeg");
    $width = "300";
    $height = "50";
    // neues Bild erzeugen
    $image = ImageCreateTrueColor($width, $height);
    // Farben definieren
    $col1 = ImageColorAllocate($image, 255, 255, 200);
    $col2 = ImageColorAllocate($image, 0, 0, 0);
    // Hintergrund mit Farbe füllen
    ImageFill($image, 0, 0, $col1);
    // Text dazu
    ImageString ($image, 5, 20, 20,
                 "Coded bei FIAE", $col2);
    // Grafik ausgeben
    ImageJPEG($image);
    // Speicher wieder frei geben
    ImageDestroy($image);
  }
  else  {
    return "<img src=\"{$_SERVER["REQUEST_URI"]}&getpic\">";
  }
}

Dienstag, 19. Januar 2010

COMA: Oracle-Blob als Image-Stream

Hier als Makro umgesetzt, welches z.B. so aufgerufen wird:
[§fpicStream_13404§]
Als Parameter wird die gewünschte Bild-ID übergeben.

function picStream($conn, $param)  {

  if(isset($_REQUEST['getpic']) &&
     is_numeric($param))  {

    $sql = "select pics_blob from web.wpics
            where pics_id=$param";
    $stmt = OCIparse($conn, $sql) ;
    OCIExecute($stmt,OCI_DEFAULT) ;
    $check = OCIFetchInto($stmt, $row, OCI_ASSOC);
    if($check == 1)
      echo $row["PICS_BLOB"]->load(); 
  }
  else  {
    return "<img src=\"{$_SERVER["REQUEST_URI"]}&getpic\">";
  }
}

Donnerstag, 14. Januar 2010

COMA: LDAP per PHP durchsuchen

function search_user_ldap($string)  {

  $filter =
  "(&
     // aktiviert, deaktiviert, alle
     (nsrole=*roleself*)
     (objectclass=ufzperson)
     // Attribut nach dem wir suchen (sn=Surname)
     (sn=".$string."*)
  )";
  // Attribute die geliefert werden sollen
  $attr = array("sn");
  // Menge der Suchergebnisse (0=unbegrenzt)
  $count = 0;
  // Ergebnisarray
  $search = array();
  
  if(cldap_Search("", &$search, $filter, $attr, $count))  {

    $search = cldap_DeleteCount($search, 1);

    foreach($search as $val)  {
      $ret[] = $val[$attr[0]][0];
    }
  }
  return $ret;
}

COMA: Oracle-Tabellen zwischen Instanzen kopieren

Mit den nachfolgenden SQL-Anweisungen lassen sich mit einem Tool wie dem SQLDeveloper, Tabellen zwischen zwei verschiedenen Oracle-Instanzen kopieren, gültige Verbindungen zu den Instanzen vorrausgesetzt. Ausgangspunkt im SQLDeveloper ist jeweils die Zielinstanz.

Beispiel 1: von SERVICE nach INTERNET

create table WEB.WTR_UFZ
as select * from WEB.WTR_UFZ@testsystem

Beispiel 2: von INTERNET nach SERVICE

create table WEB.WTR_UFZ
as select * from WEB.WTR_UFZ@internet

Dienstag, 12. Januar 2010

Javascript: Objektorientierung und Drag and Drop (Teil 2)

Vererbung

Im zweiten Teil geht es um die Vererbung, ich habe u.a. in den Kommentaren den Begriff Klassen verwendet (natürlich gibt es in Javascript keine Klassen in dem Sinne), da ich aber versucht habe Strukturen nachzubilden, wie sie aus Java bzw. C# bekannt sind, erklärt sich das Ganze so besser.

Ob eine Verkettung der drei definierten Klassen möglich ist, gilt es noch auszutesten!

Javascript im HEAD (u.a. Objektinstanzierung):
window.onload = Init;

function Init()  {
  // neues Objekt der Subklasse instanzieren
  var dragObj = new DragById("dragable_1");
  // weiterere moegliche Instanzierung
  //var dragObj = new DragInFrame(316, 92);  
  
  // Events öffentlichen Methoden aus
  // dem instanziertem Objekt zuweisen
  document.getElementById("dragpanel").onmousedown =
    dragObj.startDrag;
  document.getElementById("dragpanel").onmousemove =
    dragObj.Drag;
  document.getElementById("dragpanel").onmouseup =
    dragObj.endDrag; 
}
Javascript im HEAD (die Basisklasse):
function DragObj()  {
  // Variable zur Rückgabe
  // von öffentlichen Methoden (Pattern)
  var that = {};
  // Objektvariablen
  this.movie       = null;
  this.startX      = 0;
  this.startY      = 0;
  this.start_drag  = false;

  // öffentliche Methoden
  that.startDrag = function(e)  {
    // Browserweiche
    if(!e)  {  // IE
      e = window.event;
      this.movie = e.srcElement;
    }
    else
      this.movie = e.target;
     
    this.startY =
      e.screenY - parseInt(this.movie.style.top);
    this.startX =
      e.screenX - parseInt(this.movie.style.left);
  
  this.start_drag = true;
  }
  
  that.Drag = function(e)  {
    if(this.start_drag)  {
      if(!e)
        e = window.event;

      var newX = e.screenX - this.startX;
      var newY = e.screenY - this.startY;
    
      this.movie.style.left = newX + "px";   
      this.movie.style.top  = newY + "px";
   
      document.title = "X: " + newX + " Y: " + newY;  
    } 
  }
  
  that.endDrag = function()  {
    this.start_drag = false;
  }
    
  return that;
}
Javascript im HEAD (die Subklassen):
function DragById(objId)  { 
  // Konstruktor der Basisklasse aufrufen 
  this.constructor();
  // öffentliche Methode der Basisklasse überschreiben
  this.startDrag = function(e)  {
    if(document.getElementById(objId))  {
      this.movie = document.getElementById(objId);
        
      this.startY =
        e.screenY - parseInt(this.movie.style.top);
      this.startX =
        e.screenX - parseInt(this.movie.style.left);
  
      this.start_drag = true;
    }
  }    
}
// Initialisierung der Vererbung
DragById.prototype = new DragObj();

// weitere abgeleitete Klasse definieren
function DragInFrame(frameWidth, frameHeight)  { 
  // Konstruktor der Basisklasse aufrufen 
  this.constructor();
  // öffentliche Methode der Basisklasse überschreiben
  this.Drag = function(e)  {
    if(this.start_drag)  {
      if(!e)
        e = window.event;
  
      var newX = e.screenX - this.startX;
      var newY = e.screenY - this.startY;
    
      if(newX > 0 && newX < frameWidth)
        this.movie.style.left = newX + "px";   
      if(newY > 0 && newY < frameHeight)
        this.movie.style.top  = newY + "px";
   
      document.title = "X: " + newX + " Y: " + newY;  
    } 
  }
}
// Initialisierung der Vererbung
DragInFrame.prototype = new DragObj();
HTML im BODY:
<img src="html.gif"
style="position:absolute; top:0; left:0;"
onmousedown="return false;"
onmousemove="return false;"
width="129" height="108" id="dragable_1" />
Quelltext

Freitag, 8. Januar 2010

AJAX: HTML-Element für Autovervollständigung

Nachdem ich nach einigem Suchen kein stabiles Element für eine AJAX-Autocomplete Funktion gefunden habe, hier mein Mix aus einem Text- und Listenfeld.

Zum Test einfach etwas eingeben (ohne Datenanbindung).

var charCode = 0;      // Tastencode aus beiden Steuerelementen
var optText  = null;   // Textfeld
var optList  = null;   // Listenfeld
var topOpt   = false;  // Hilfsvariable zum Steuern des Uebergangs
var oldText  = "";     // Vorwert aus Textbox merken

function Init(obj)  {
  if(optList)  // Falls noch andere Liste offen
    //CloseList(false);  // IE hängt sich auf
    optList.style.display = 'none';
    
  optText = obj;
  optList = GetSelect(optText);  // SELECT-Element ermitteln
  // geerbte Breite des Eingabefeldes an SELECT-Element weitergebn
  optList.style.width = optText.offsetWidth + "px";
  // Variable charCode setzen
  document.onkeydown = GetKeyCode;
}
function OpenList()  {  
  oldText = optText.value;  // Wert für Abbruch merken

  if(charCode == 27)  {  // ESC
     optList.style.display = 'none';  // Liste wieder zu machen
     return;
  }
  if(charCode == 13)  {  // ESC
     optList.style.display = 'none';  // Liste wieder zu machen
     // Event valuefixed auslösen
     return;
  }  

  if(optText.value.length > 0)  {  // ist etwas im Textfeld
    topOpt = true;  // Pfeiltaste nach oben soll Textfeld aktivieren
     
    with(optList)  {
      // bei diesen Tasten Liste anzeigen
      if(
          charCode ==   8 ||  // Backspace
          charCode ==  40 ||  // Pfeil nach unten
          charCode ==  46 ||  // Delete
         (charCode >=  48 && charCode <= 57) ||  // 0-9
         (charCode >=  65 && charCode <= 90) ||  // a-z
          charCode == 109 ||  // - 
          charCode == 190 ||  // .
          charCode ==  59 ||  // ü
          charCode == 192 ||  // ö          
          charCode == 219 ||  // ß
          charCode == 222     // ä
        )  {
      style.display = 'block';  // Liste anzeigen
      selectedIndex = -1;       // nichts auswaehlen
    }
    }
  }
  else
    CloseList(false);  // wurde z.B. alles geloescht, Liste schliessen
  
  // Übergang zur Liste bei...  
  if(charCode == 40 &&  // ... Pfeiltaste nach unten
     optList.style.display == 'block')  {  // und Liste offen
     
    optText.value = optList.options[0].value;  // Textbox aktualisieren
    optList.focus();                // Liste aktivieren
    optList.selectedIndex = 0;      // ersten Eintrag auswählen 
  }
}
function CloseList(setOpt)  { 
  if(setOpt && (optList.options.length != optList.selectedIndex + 1))  {
    optText.value = optList.value;  // Listenwert ins Textfeld
    // Event valuefixed auslösen
  }
  else  {
    optText.value = oldText;
    oldText = null;
  }  
  optText.focus();  // Textfeld aktivieren
  optList.style.display = 'none';  // Liste ausschalten
}
function CancelSelect()  {
  optList.style.display = 'none';  // Liste ausschalten 
  optText.value = oldText;  // gemerkten Text einsetzen
  optText.focus();  // Textfeld aktivieren
}
function CtrlList()  {

  if(charCode == 13)  // Enter
     CloseList(true);
  if(charCode == 27)  // ESC
     CancelSelect();        
  if((charCode == 38 || charCode == 40) &&  // Pfeiltaste oben unten
     optList.options.length != optList.selectedIndex + 1)  // nicht letzte Option
     optText.value = optList.value; 
      
  if(charCode != 38)  // nicht Pfeiltaste nach oben
    topOpt = false;
        
  // Pfeiltaste nach oben und oberster Eintrag ausgewaehlt  
  if(charCode == 38 && optList.selectedIndex == 0)  {
    if(topOpt)  {  // beim ersten Mal noch nichts machen
      optList.style.display = 'none';  // Liste ausschalten
      optText.focus();  // Textfeld aktivieren 
    }
    else
      topOpt = true;  // um beim zweiten Pfeiltaste nach oben      
  }                   // Steuerelement zu wechseln
}
function GetKeyCode(e) {
  if (!e)
    e = window.event;
  charCode = e.keyCode;
}
function GetSelect(obj)  {
  var count = 0;
  do  {    
    var elem = obj.nextSibling;
    obj = elem;
    
    count++;
    if(count > 3) break;
  } while(elem.type != "select-one")
  
  return elem;
}
function FixValue()  {
  // TODO: Event valuefixed auslösen
  // aktives Element oder Tastencodes prüfen
}
CSS im HEAD:
input.autocomplete  {
 width: inherit;
}
input.autocomplete  {
  display: block;
}
select.autocomplete  {
  display: none;
  position: absolute;
}
#autocomplete1  {
 float: left;
 width: 200px;
}
#autocomplete2  {
   float: left;
 width: 200px;
}
.lastopt  {
  background-color: lightgray;
}
HTML im BODY:
<div id="autocomplete1">
  <input type="text" name="autocomplete2" class="autocomplete" onclick="this.focus();" onfocus="Init(this)" onkeyup="OpenList();" onblur="FixValue();" tabindex="2" />
  <select size="4" class="autocomplete" onclick="CloseList(true);" onkeyup="CtrlList();">
    <option value="Peter">Peter</option>
    <option value="Peters">Peters</option>
    <option value="Petersen">Petersen</option>
    <option value="Petersohn">Petersohn</option>
    <option class="lastopt" title="Schließen">⇑</option>
  </select>
</div>
Download komplettes Beispiel komplettes Beispiel

COMA: AJAX-Response ohne Content

So könnte man z.B. in einem Makro einen Response auslösen.
if(isset($_GET['ajax']))  {
  echo utf8_encode("Hallo AJAX!");
  die();  // ohne die() wird der komplette Kontent 
}         // mit zurückgegeben