Im SAP System werden Dokumente zu einem Objekt meistens über die "Objekt Services" als "Attachments" abgelegt. In Transaktion IE02 (Ändern Equipment) sieht das zum Beispiel wie folgt aus:
Zahlreiche Dateitypen sind dabei möglich; gebräuchlich sind PDFs, Word- und Excel-Dateien sowie Bilddateien im .jpg oder .png Format.
In diesem Tutorial integrieren wir die Dokumentenanzeige sowie den Upload in eine S10 Anwendung. Unser Startpunkt ist eine Liste von Equipments, die wir uns mit den S10 Utilities, Transaktion /s10/util, generiert haben:
In diese Basisliste bauen wir die Dokumentenanzeige ein. Bei Klick auf eine Equipmentzeile erscheinen einige Details zum jeweiligen Equipment sowie die Anlagenliste:
In der Liste der Dokumente wird bei einem Klick das Dokument entweder extern angezeigt (Word- und Exceldateien) oder inline in die Liste eingebettet (Bilddateien, PDF, Video). Der Browser bietet dabei in der Regel die Möglichkeit, das Dokument nachträglich statt in der Liste als eigenständige Anwendung anzuzeigen. Bei Word und Excel ist zur Zeit in den gängigen Browsern keine eingebettete Anzeige unterstützt.
Hier einige Beispiele:
Die Dokumente können jeweils einzeln auf- und zugeklappt werden. Bei Excel und Word wird die entsprechende Anwendung auf dem mobilen Gerät oder auf dem Desktop geöffnet:
Nun zur Implementierung. Wir nutzen die globale Klasse /s10/attachment, die Teil des S10 Demo-Pakets ist; sie stellt einige generelle Services bereit, um Dokumente zu SAP Objekten zu lesen. Damit wird das ABAP-Programm recht übersichtlich:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
program zzequi00. class equi_detail definition inheriting from /s10/any. public section. * Equipment properties data: equnr type v_equi-equnr, " Equipment Number tplnr type v_equi-tplnr, " Functional Location eqtyp type v_equi-eqtyp, " Equipment type inbdt type v_equi-inbdt, " Start-up Date sernr type v_equi-sernr. " Serial Number * attachments data: attachment type ref to /s10/attachment, attachments type table of ref to /s10/attachment. * database table name constants: dbtablename type string value 'v_equi'. " equipments with functional locations methods: build_attachments importing equnr type equnr exporting attachments type table, on_detail_attachments. endclass. class equi_detail implementation. method build_attachments. /s10/attachment=>list_attachments( exporting instid = equnr typeid = 'EQUI' catid = 'BO' importing attachments = attachments ). * sort attachments s10sort( exporting foldername = 'attachments' columns = 'doctitle' ). endmethod. method on_detail_attachments. create object attachment. attachment->dockey = s10contextinfo( )->getvalue( 'dockey'). endmethod. endclass. class equi_short definition inheriting from /s10/any. public section. * table fields for detail view, plus key fields data: equnr type v_equi-equnr, " Equipment number tplnr type v_equi-tplnr. " Functional location * database table name constants: dbtablename type string value 'v_equi'. " for select endclass. class equi_short implementation. endclass. class equi_manager definition inheriting from /s10/any. public section. * table fields for list view, plus key fields data: search_equnr type string, search_equnr_upper type string, search_tplnr type v_equi-tplnr, search_tplnr_upper type string, search_sernr type v_equi-sernr, " search_sernr_upper type string, search_maxrowcount type string value '100'. data: tabequi type table of ref to equi_short, myequi type ref to equi_detail. methods: list, on_detail_tabequi, * method to build up tabequi build_tabequi importing search_equnr type string search_tplnr type v_equi-tplnr search_sernr type v_equi-sernr search_maxrowcount type string exporting tabequi type table. endclass. class equi_manager implementation. * display list screen method list. s10nextscreen( 'list'). endmethod. * select database values and fill table tabequi method build_tabequi. data: condition type string, join type string, search type string. search_equnr_upper = to_upper( search_equnr ). search_tplnr_upper = to_upper( search_tplnr ). search_sernr_upper = to_upper( search_sernr ). if search_equnr is not initial. search = search_equnr. if join is initial. join = |v_equi |. endif. join = join && | join eqkt on eqkt~EQUNR = v_equi~equnr and eqkt~SPRAS = @sy-langu and upper( eqkt~EQKTX ) like '%| && search_equnr_upper && |%'|. endif. if search_tplnr is not initial. search = search_tplnr. if condition is not initial. condition = condition && | and |. endif. condition = condition && |v_equi~TPLNR EQ '| && search_tplnr && |'|. endif. if search_sernr is not initial. search = search_sernr. if condition is not initial. condition = condition && | and |. endif. condition = condition && |v_equi~SERNR EQ '| && search_sernr && |'|. endif. data: maxrows type i. maxrows = search_maxrowcount. * read data s10databaseselect( exporting dbtablename = join condition = condition maxrows = maxrows distinct = 'X' fieldlist = 'v_equi~equnr,v_equi~tplnr' changing folder = tabequi ). s10sort( foldername = 'tabequi' columns = 'equnr,tplnr' userformat = 'X' ). endmethod. * show details in list (line selection) method on_detail_tabequi. * read current table row. data: tabindex type i. tabindex = s10actionparameter( ). read table tabequi index tabindex assigning field-symbol(<row>). * set table key and read detail attributes create object myequi. myequi->equnr = <row>->equnr. myequi->tplnr = <row>->tplnr. myequi->s10databaseread( keylist = 'equnr,tplnr' ). endmethod. endclass. * main class for this application class main definition inheriting from /s10/any. public section. * manager object data: my_equi_manager type ref to equi_manager. methods: logon. endclass. class main implementation. * logon user method logon. * set S10 license s10setlicense( 'Synactive GmbH demo license number=100 role=s10demo_role maxusers=10 signature=821.126.87.7' ). * create manager object create object my_equi_manager. * start list display my_equi_manager->list( ). endmethod. endclass. |
Zur Erläuterung ist die Verbindung zur HTML-Seite an einigen Stellen erforderlich:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=400"> <link rel='stylesheet' type='text/css' href='../../../style/s10.style.css'> <link rel='stylesheet' type='text/css' href='../../../style/custom.style.css'> <script src='../../synactiveS10/synactiveS10.java.js'></script> <title>Equipments</title> </head> <body style="width: 100%; margin: 0px; padding: 0px;" onload='init();' class="colorscheme9"> <div class="headerarea" style="width: 100%; padding: 10px;"> <!-- title from data dictionary --> <b>Equipments</b> </div> <div class="toolbar"> <button type="button" class="toolbarbutton" onclick="S10Enter();"> Start </button> <button type="button" class="toolbarbutton" style="float: right;" onclick="S10Logoff();"> End </button> </div> <!-- filter --> <div class="tablefilteractive"> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Equipmenttext</label><br> <input type="search" class="input" name="search_equnr" style="width: 140px;"> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Technischer Platz </label> <br> <input type="search" class="input" name="search_tplnr" style="width: 140px;"> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Serialnummer </label> <br> <input type="search" class="input" name="search_sernr" style="width: 140px;"> </div> <br /> <div class="infoblock" style="width: 150px; height: 50px;"> <label class='label'>Maximale Anzahl</label> <br /> <select class="inputselect" name="search_maxrowcount" size="1" style='width: 120px;' onchange="S10Enter();"> <option value="100">100 </option> <option value="400">400 </option> <option value="1000">1000 </option> <option value="4000">4000 </option> <option value="10000">10000 </option> <option value="40000">40000 </option> <option value="100000">100000 </option> </select> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class='label'>Anzahl selektiert</label><br /> <div class='output' style="font-size: 14px; padding-top: 6px; font-weight: bold;" name='tabequi@rowcount'> </div> </div> <div class="processingmessage">Daten wurden selektiert ...</div> </div> <!-- column headers --> <div class="colheaders"> <!-- Equipment --> <div class='colhead colheadup output ' style="width: 100px; max-width: 100px;" name="equnr"> </div> <div class='colhead output' style="width: 260px;" name="equnr@text"> </div> <!-- Functional location --> <div class='colhead output landscape' style="width: 300px;" name="tplnr@text"> </div> <div class="colhead" style="width: 20px; float: right; margin-right: 4px;" onclick="S10FilterTable(this);"> <img src="../../../icons/filter.png" style="width: 18px; height: 18px;"> </div> </div> <!-- list rows --> <form class='table' name='tabequi'> <div class="tablerow" onclick="S10ToggleDetail(this);"> <!-- Equipment --> <div class='outputcelldiv ' style="width: 100px; max-width: 100px;" name="equnr"> </div> <div class='outputcelldiv' style="width: 260px;" name="equnr@text"> </div> <!-- Functional location --> <div class='outputcelldiv landscape' style="width: 300px;" name="tplnr@text"> </div> <div class='tablerequestdetail' style='float: right;'></div> </div> </form> <!-- nothing selected --> <div class="tablenocontent" style="display: block;"> Es wurde nichts selektiert </div> <!-- detail view --> <div id="tabequi_detail" class='tabledetail'> <!-- Equipment type--> <div class="infoblock"> <label class='label output' name="myequi.eqtyp"></label> <br /> <span class='output' name='myequi.eqtyp'></span> <span class='output' name='myequi.eqtyp@text'></span> </div> <!-- In operation from --> <div class="infoblock"> <label class='label output' name="myequi.inbdt"></label> <br /> <span class='output' name='myequi.inbdt'></span> </div> <!-- Serial number --> <div class="infoblock"> <label class='label output' name="myequi.sernr"></label> <br /> <span class='output' name='myequi.sernr'></span> </div> <!-- Attachments --> <div class="infoblock" style="width: 100%; max-width: 800px; height: auto; display: block;"> <label class='label'>Dokumente</label><br /> <form class='table' name='myequi.attachments' style="width: 360px; --landscape-width: 480px;"> <div class="tablerow" onclick="S10ToggleDetail();"> <!-- additional keys --> <div class='outputcelldiv linkkey' name="dockey"></div> <!-- columns --> <div class='outputcelldiv' style="width: 220px; --landscape-width: 320px;" name="doctitle"> </div> <div class='outputcelldiv' style="width: 40px;" name="doctype"> </div> <div class='outputcelldiv' style="width: 80px;" name="docsize_out"> </div> </div> </form> <div class="tablenocontent">Keine Dokumente abgelegt</div> <br /> </div> <!-- Document display area --> <div class='tabledetail' id="myequi.attachments_detail"> <div class="outputhtml" name="myequi.attachment.display_html"> </div> </div> </body> </html> |
Erläuterungen
Die Zeilennummern A... beziehen sich auf das ABAP-Coding, H... auf die HTML-Datei.
Zeile A3
class equi_detail definition inheriting from /s10/any.
Die Klasse "equi_detail" beschreibt die Detailsicht des Equipments, nach Selektion einer Zeile in der Liste. Die Attribute (Zeilen A9 bis A13) werden in HTML ab H155 angezeigt. Wenn Sie weitere Infos zu dem Equipment anzeigen wollen, die in dem Datenbank-View V_EQUI enthalten sind, genügt es, diese in der Klasse noch dazuzunehmen und in der HTML-Datei auszugeben. Das Lesen aller Attribute geschieht durch s10databaseread( ) in Zeile A192.
Zeile A15
* attachments data: attachment type ref to /s10/attachment, attachments type table of ref to /s10/attachment.
Definiert ein attachment-Objekt und die Tabelle der Attachments. Die Ausgabe der Tabelle erfolgt in HTML ab Zeile H181 bis H209. Die Spalten doctitle, doctype und docsize_out sind als Attribute der Klasse /s10/attachment definiert.
Neben den sichtbaren Spalten wird der Dokumentenschlüssel unsichtbar in der Tabelle vermerkt: H191 <div class='outputcelldiv linkkey' name="dockey"></div> Bei Klick auf eine Attachmentzeile wird in ABAP dieser Schlüssel durch s10contextinfo() gelesen:
method on_detail_attachments. create object attachment. attachment->dockey = s10contextinfo( )->getvalue( 'dockey'). endmethod.
Zeile A37
Die Tabelle der Attachments pro Equipment ist als build-Methode realisiert. Zur Impelmentierung wird die Methode "list_attachments" der Klasse /s10/attachment aufgerufen:
/s10/attachment=>list_attachments( exporting instid = equnr typeid = 'EQUI' catid = 'BO' importing attachments = attachments ).
Für andere Objekte müssen Sie hier die richtige "typeid" mitgeben, das ist der "Business Object Type" (Transaktion SWO2). Für eine Bestellanforderung wäre es zum Beispiel "BUS2105". Die Katalog-Id ist immer "BO" für "Business Object".
Zeile A64
class equi_short definition inheriting from /s10/any.
Die Klasse "equi_short" beschreibt die Zeile der Equipemntliste. WIr benötigen hier nur die Equipementnummer und den Technischen Platz. Die entsprechenden Texte werden durch das S10 Framework in der Ausgabe durch die Notation "...@text" ergänzt. In der HTML Datei finden Sie die Ausgabe der Tabelle ab Zeile H125, beginnend mit
<form class='table' name='tabequi'>
Ab Zeile A119
class equi_manager implementation.
Der Rest des Programms ist unabhängig von der Anzeige der Dokumente; er ist durch die S10 Utilities generiert.
Der zentrale Abschnitt in der HTML-Datei ist die Anzeige des Dokuments ab Zeile H211:
<!-- Document display area --> <div class='tabledetail' id="myequi.attachments_detail"> <div class="outputhtml" name="myequi.attachment.display_html"> </div> </div>
Diese Abschnitt wird automatisch hinter die ausgewählte Zeile der Attachment-Liste eingeblendet, sobald der Benutzer auf eine Attachment-Zeile klickt, da
<div class="tablerow" onclick="S10ToggleDetail();">
definiert ist in Zeile H188.
Es wird zunächst die ABAP-Methode
method on_detail_attachments.
create object attachment.
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
endmethod.
des Objekts "myequi" gerufen, da die Tabelle "attachments" im Objekt "myequi" definiert ist. Allerdings passt das Objekt "myequi" im ABAP Programm immer zur letzten ausgewählten Equipmentzeile; es ist ja nur einmal vorhanden. Wenn der Benutzer zunächst einige Equipments aufklappt und dann in einem der aufgeklappten Bereiche ein Attachment anzeigt, nutzt uns die Zeilennummer aus HTML im ABAP Programm nichts, da die aktuelle attachment-Tabelle zu einem anderen Equipment gehören kann. Diese Schwierigkeit entsteht durch die geschachtelte Struktur der Liste, also Liste der Equipments und pro Equipment eine Liste der Attachments.
Aus diesem Grund nutzen wir nicht die Zeilennummer und die aktuelle Tabelle "attachments", sondern greifen durch
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
auf den in der HTML-Zeile versteckten Dokumentenkey zu und setzen ihn in das attachment-Objekt. Da in der HTML-Seite das Attribut "display_html" ausgegeben wird:
<div class="outputhtml" name="myequi.attachment.display_html">
läuft nun die build-Methode des Attributs "display_html" der globalen Klasse "/s10/attachment" ab. Die build-Methode liest das Dokument ein und baut den richtigen HTML-Code zur Anzeige des Objekts auf. In unserer HTML-Datei ist es hier wichtig, mit class="outputhtml"statt mit class="output" zu arbeiten, da andernfalls der HTML-Code angezeigt würde, statt ihn als HTML zu interpretieren.
Unsere Dokumentenanzeige wäre einfacher zu implementieren, wenn wir die Informationen pro Equipment und pro Attachment nicht in die Liste integrieren würden, sonden jeweils in separat aufgerufenen Seiten anzeigen würden, aus denen der Benutzer dann zur Liste zurückgeht. Der Vorteil der gewählten Darstellung ist, dass die einmal aufgeklappten Teile, zum Beispiel zwei Fotos, in der Liste simultan sichtbar bleiben oder zumindest durch Blättern leicht erreichbar sind. Zum Beispiel wären zwei Fotos zu einer Schadensmeldung dann unmittelbar untereinander sichtbar.
Upload von Dokumenten
Nun zum Hochladen von Dokumenten und Abspeichern als Attachment. Das Vorgehen hierzu ist wie folgt:
Schritt 1: In HTML die Auswahl der gewünschten hochzuladenden Datei aufnehmen
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<label class='label'>Titel für neues Dokument</label> <br /> <input class="input" data-locid="attachment_title_input" style="width: 380px;"> <input type="file" data-locid="attachment_input" accept=".jpg,.jpeg,.png,.pdf,.docx,.xlsx,.txt" style="display: none" onchange="upload_attachment(this)"> <br /> <button type="button" class="button" data-locid="attachment_button" style="margin-top: 5px;" onclick="return start_upload_attachment(this)"> Dokument auswählen und hochladen <span class="processingmessage">Upload läuft</span> </button> |
Erläuterungen
Zeilen 4, 8, 15: data-locid=
In unserer Anwendung wird die Upload-Möglichkeit unterhalb der Attachment-Liste innerhalb des Detailbereichs der Tabellenzeile angezeigt. Es kann also, wenn der Benutzer für mehrere Zeilen nacheinander den Detailbereich öffnet, mehrfach erscheinen. Aus diesem Grund können wir zur Identifizierung der HTML-Elemente aus JavaScript nicht das gebräuchliche id= verwenden, da in HTML die vergebene id in der HTML-Seite eindeutig sein muss, um mit document.getElementById() zu funktionieren. Das Attribut data-locid gestattet uns, ein Element innerhalb des geöffneten Detailbereichs mit der S10-Farmework-Funktion S10 ElementByLocid() zu adressieren (siehe JavaScript in Schritt 2), es ist sozusagen eine lokal verwendbare Id.
Zeile 7: <input type=file>
Hierduch wird in HTML die Auswahl einer Datei durch den Benutzer ermöglicht. Mit accept=... geben wir die Dateikennungen an, die bei der Auwahl möglich sein sollen. In Zeile 10 wird das Element mit style="display:none" versteckt, da wir nicht die HTML-Standarddarstellung nutzen wollen, sonden im Anschluss in Zeile 14 einen eigenen Button zur Dateiauswahl vorsehen.
Der oben angegebene HTML-Teil kommt in den Detaibereich der Tabellenzeile. Zusätzlich benötigen wir ausserhalb des Dateilbereichs ein einzelnes verstecktes <input>-Element für die S10 Variable, mit der wir den Dateiinhalt in unser ABAP-Programm transportieren lassen:
<input type="hidden" class="input" name="data_url" id="data_url" />
data: data_url type string.
Schritt 2: Mit Javscript-Mitteln den Inhalt der ausgewählten Datei in eine versteckte S10 Variable übertragen
Fügen Sie folgendes JavaScrpt-Coding in der HTML-Seite ein. Wo es sich befindet, spielt keine Rolle, also in <head> oder <body>. Falls Sie das Hochladen von Dokumenten in mehreren HTML-Seite benötigen, am besten als eigene Datei ablegen und mit <script src="..."> </script> referieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<script> var attachment_reader = null; var attachment_input = null; var attachment_button = null; var attachment_title = ""; var attachment_filename = ""; function start_upload_attachment(f) { attachment_title = S10ElementByLocid(f, 'attachment_title_input').value; if (!attachment_title) { alert('Bitte geben Sie zunächst einen Titel ein'); return; }; attachment_input = S10ElementByLocid(f, 'attachment_input'); attachment_button = S10ElementByLocid(f, 'attachment_button'); // reset value, otherwise onchange // is not triggered for same filename attachment_input.value = ''; attachment_input.click(); return false; }; function upload_attachment(f) { var files = f.files; // anything selected? if (files) { // 1st file (nultiple files not supported) var fileobject = files[0]; attachment_filename = fileobject.name; attachment_reader = new FileReader(); attachment_reader.addEventListener("load", complete_upload_attachment); attachment_reader.readAsDataURL(fileobject); }; } function complete_upload_attachment() { var result = attachment_reader.result; attachment_reader = null; // check file size if (result.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive Demo-System: Bitte nur Dateien < 1MB hochladen"); return; }; // upload content as data url document.getElementById('data_url').value = result; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; </script> |
Erläuterungen
Zeile 9 Funktion "start_upload_attachment"
Die Funktion wird von den Upload-Button aufgerufen. Sie prüft, ob der Benutzer einen Titel eingegeben hat, setzt einige globale Variablen und ruft dann die Dateiauswahl auf, indem das versteckte <input type='file'> Element geklickt wird.
Zeile 32 Funktion "upload_attachment"
Diese Funktion wird aus dem <input type='file'> Element durch onchange= aufgerufen, siehe HTML-Ausschnitt oben Zeile 11, sobald der Benutzer eine Datei ausgewählt hat. WIr besorgen uns das "File object" der ausgewählten Datei und lesen die Datei im Format "Data URL" in Zeile 47 ein. Das Dateilesen führt das Javascript "File Object" asynchron aus. Wir geben deshalb in Zeile 45 davor die JavaScript-Funktion an, die aufgerufen wird, wenn die Datei vollständig eingelesen ist und wir auf den gesamten Inhalt zugreifen können.
Zeile 52 Funktion "complete_upload_attachment"
Diese Funktion wird aufgerufen, sobald die Datei vollständig eingelesen ist. Wir können auf den Inhalt der Datei über die Komponente "result" des JavaScript File Readers zugreifen. In Zeile 59 prüfen wir die Dateigrösse und geben bei zu grosser Datei eine Fehlermeldung aus.
Hinweis: In mobilen Geräten steht bei der Dateiauswahl meist auch die Möglichkeit zur Verfügung, ein Foto zu machen und hochzuladen. Durch die grosse Auflösung heutiger Geräte entstehen dabei unter Umständen sehr grosse jpeg-Dateien. Sie können derartige Bilddateien zunächst intern stärker komprimieren und dann hochladen (canvas-Element in HTML verwenden).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
<script> var attachment_reader = null; var attachment_input = null; var attachment_button = null; var attachment_title = ""; var attachment_filename = ""; function start_upload_attachment(f) { attachment_title = S10ElementByLocid(f, 'attachment_title_input').value; if (!attachment_title) { alert('Bitte zunächst einen Titel eingeben'); return; }; attachment_input = S10ElementByLocid(f, 'attachment_input'); attachment_button = S10ElementByLocid(f, 'attachment_button'); // reset value, otherwise onchange is not triggered for same filename attachment_input.value = ''; attachment_input.click(); return false; }; function upload_attachment(f) { var files = f.files; // anything selected? if (files) { // 1st file (nultiple files not supported) var fileobject = files[0]; attachment_filename = fileobject.name; attachment_reader = new FileReader(); attachment_reader.addEventListener("load", complete_upload_attachment); attachment_reader.readAsDataURL(fileobject); }; } function complete_upload_attachment() { var result = attachment_reader.result; attachment_reader = null; // compress large jpeg images (camera) if (result.startsWith('data:image/jpeg') && result.length > 400 * 1024) { var image = new Image(); image.src = result; image.onload = function () { upload_image(image); }; return; }; // check file size if (result.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive Demo-Systen: Bitte nur Dateien < 1MB hochladen"); return; }; // upload content as data url document.getElementById('data_url').value = result; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; function compressImage(image, compression) { var canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; var ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0); return canvas.toDataURL("image/jpeg", compression); } function upload_image(image) { var data_uri = compressImage(image, 0.7); var k = 0.5; while (k > 0 && data_uri.length > 1024 * 1024) { data_uri = compressImage(image, k); k = k - 0.1; }; // check file size if (data_uri.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive Demo-System: Bitte nur Bilder < 1MB (komprimiert) hochladen"); return; }; // upload content as data url document.getElementById('data_url').value = data_uri; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; </script> |
Den Inhalt der Datei setzen wir in Zeile 66 in die versteckte Variable "data_url". Anschliessen rufen wir in Zeile 68 die ABAP-Funktion "save_attachment" auf, der wir den Dateinamen und, durch ein TAB-Zeichen getrennt, den vom Benutzer eingegebenen TItel mitgeben. Nun kann auf ABAP-Ebene das Abspeichern der Datei als Attachment erfolgen.
Schritt 3: In ABAP den Dateinhalt aus der S10 Variablen übernehmen und mit der Klasse "attachment" des S10 Frameworks als Attachment zum Equipment abspeichern
Ergänzen Sie Ihr ABAP-Programm um folgendes Coding:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
* Variable für Upload-Dateiinhalt public section. data: data_url type string. * In "class equi_manager implementation": method save_attachment. data: parms type string. parms = s10actionparameter( ). data: docfilename type string, doctitle type string, doctype type string. split parms at cl_abap_char_utilities=>horizontal_tab into docfilename doctitle. * doctype: file extension if docfilename ca '.'. data: strs type standard table of string, str type string. split docfilename at '.' into table strs. read table strs index lines( strs ) into str. doctype = to_lower( str ). endif. * current equipment data: rownumber type i. rownumber = s10contextinfo( )->rownumber. read table tabequi index rownumber assigning field-symbol(<equi>). call method /s10/attachment=>create_attachment exporting objtype = 'EQUI' objkey = <equi>->equnr docfilename = docfilename doctitle = doctitle doctype = doctype data_url = data_url. * refresh detail view <equi>->s10detailview = 'X'. * set table key and read detail attributes create object myequi. myequi->equnr = <equi>->equnr. myequi->tplnr = <equi>->tplnr. myequi->s10databaseread( keylist = 'equnr,tplnr' ). * clear data clear: data_url. endmethod. |
Erläuterungen
Zeilen 3 und 4
Die Variable "data_url" in der "public section" der Klasse anlegen.
Zeile 17
Den in JavaScript zusammengesetzten Parameter zerlegen wir wieder in die Bestandteile Dateiname und Titel. Alternativ hätten wir in JavaScript wie beim Dateiinhalt vorgehen können und zwei weitere versteckte <input> Element füllen, die jeweils ABAP Attributen zugeordnet sind.
Zeile 32
Wir benötigen die aktuelle Equipmentnummer, die wir der aktiven Tabellenzeile entnehmen.
Zeile 37
Nun wird das Attachment hinzugefügt. Der Objekttyp, hier "EQUI", und der Aufbau des Schlüssels, hier nur die Equipmentnummer, hängt von dem jeweiligen SAP Objekt ab.
Zeilen 46-53
Wir lesen die Equipmentdetails neu und sorgen dafür, dass der Detailbereich aktualisiert wird (Zeile 47). Damit erscheint das gerade hinzugefügte Attachment nun in der Attachmentliste.