As detailed in Section 6.1 - The Menu Page, the Menu Page in ERES provides a portal that allows users to automatically publish reports and charts that have been deployed in Organizer. Although a standard format for the Menu Page is provided with ERES, users can completely customize this presentation to fit with their existing web content. All of the Menu Page formatting and functions are written on the client using the Java Standard Tag Library, HTML, and JavaScript while the core functionality (processing login, retrieving reports, charts, and schedule/archive information) can be accessed using the JSP beans provided. These features allow web developers to easily create their own reporting portals without developing any server-side code.
This chapter describes how the Menu Page works in the standard format. There is also a more simplified example available in the ERES installation under <ERESInstallDir>/help/examples/menu/exampleMenuJSTL.jsp
. Note that if you do wish to modify the standard Menu Page, it is recommended that you create your own JSP files. The standard files described in this chapter will be modified during an upgrade installation of ERES.
The standard format menu page consists of the following JSP pages and Java beans:
MenuArchiveSecurity.jsp MenuError.jsp MenuPage.jsp RunArchive.jsp RunArchiveCHT.jsp RunReport.jsp RunReportCHT.jsp RunSchedule.jsp ShowParameters.jsp MenuScheduleParam.class MenuRunSchedule.class MenuRunReport.class MenuRunChart.class MenuRunArchiveCHT.class MenuRunArchive.class MenuReportSecurity.class MenuMainPage.class MenuLogin.class MenuArchiveSecurity.class ItemScheduleJob.class ItemNode.class ItemArchiveJob.class FolderNode.class
In the ERES Start-up page, when you click on the View Published Files option, you will see the menu page which lists all the reports, charts, and files to which you have access privileges. The page is displayed by MenuPage.jsp
. MenuPage.jsp
uses the Java bean MenuMainPage.class
.
The relationship among the JSP pages and Java beans is as follows:
In all of the JSP pages, except MenuError.jsp
there is a code scriptlet similar to the following:
<% WebLogin wl = (WebLogin) session.getAttribute("eres-web-login"); String redirect = MenuPageBean.getLoginPage(); if (wl == null || wl.getUser() == null) { response.sendRedirect(redirect+"?errorMsg=Not+Logged+In"); } else if (!wl.canAccess(WebLogin.MENU)) { response.sendRedirect(redirect+"?errorMsg=Permission+Denied.+You+must+have+Menu+priviledge+to+access+this+page."); } else { MenuPageBean.processRequest(request, application); %>
This is trying to ensure that you are properly logged in. If not, you will be directed to a login page. Otherwise, some initialization code will be run when the method processRequest(request,application)
is called. Each bean has a method by this name for initializing the bean.
MenuPage.jsp
retrieves the items (reports, charts, files) that you have right to access and displays them in the tabular format as shown in the following figure:
Items are grouped under folders, as you see in the Organizer. However, in the menu page, folder hierarchy is not displayed. If you have nested folders and you want to display them as such, you can refer to exampleMenuJSTL.jsp
under the <ERESInstallDir>/help/examples/menu
directory.
In the following block of tags, the first forEach
starts the for loop to get the folders. The second one retrieves the individual items in the folder.
<c:set var="rowColor" value="innertablewhite" /> <c:forEach items="${MenuPageBean.folderList}" var="current"> <c:set var="firstTime" value="true" /> <c:forEach items="${current.items}" var="curItem" > <tr> <c:if test="${firstTime}" > <td rowspan=<c:out value="${current.rowCount}" /> valign="top" class="innertablewhite"> <table width=100% cellspacing=0 cellpadding=0 border=0 class="innertablewhite"> <tr> <td width=20><img src="Web_Component/MENU/FolderOpen.gif"></td> <td valign="middle" class="text"><c:out value="${current.nodeName}" /> </td> </tr> </table> </td> <c:set var="firstTime" value="false" /> </c:if> ... ...
The firstTime
variable is true
if the current item is the first one in the folder. rowspan
is set to total item count for the folder. rowColor
is set to alternate values to achieve dual color.
The following code sets up the link to run the report, chart, or simply display the referenced file:
<c:set var="archiveObj" value="${curItem.archiveJob}" /> <!---------test for archive object -------> <c:choose> <c:when test="${curItem.archiveJobAvailable}"> <:c:set var="argv" > .jsp?NODEID=<c:out value="${current.nodeID}" />&TABLEID=<c:out value="${curItem.rowID}"/>&ARCHID=<c:out value="${archiveObj.id}" /> </c:set> </c:when> <c:otherwise> <c:set var="argv" > .jsp?NODEID=<c:out value="${current.nodeID}" />&TABLEID=<c:out value="${curItem.rowID}" /> </c:set> </c:otherwise> </c:choose> <c:choose> <c:when test="${curItem.chart || curItem.report}"> <td width=20 align="center"><a href=# onClick="O('RunReport<c:if test= "${curItem.chart}">CHT</c:if><c:out value="${argv}"/>','RunReport',350,370); return false;"> <img src="Web_Component/MENU/RunReport.gif" border=0 alt="Run"></a></td> </c:when> <c:otherwise> <td width=20 align="center"><a href=# onClick="O('<c:out value="${curItem.documentURL}" />','RunReport',700,450); return false;"> <img src="Web_Component/MENU/RunReport.gif" border=0 alt="Run"></a></td> </c:otherwise> </c:choose>
Depending on whether the item is a chart, report, a file, RunReportCHT.jsp
, RunReport.jsp
or just the file URL will be invoked “onClick”. The argv
variable is used to construct the request objects for the RunReport.jsp
and RunReportCHT.jsp
. If the report or chart has archive job turned on, the archive job's ID will be included. In such case, when you run the report or chart, you have the option to save the report or chart as archive.
If the item has a scheduled job, the following code will be executed:
<c:set var="scheduledRptURL" > MenuReportSecurity<c:out value="${argv}" escapeXml="false"/>&SCHEDID=<c:out value="${curItem.scheduleJob.id}" />&SCHEDPARAMSETCOUNT=<c:out value="${curItem.scheduleJob.paramSetCount}" /> </c:set> <c:choose> <c:when test="${curItem.scheduleJob.chart && curItem.scheduleJob.paramSetCount==0}" > <c:set var="width" value="${750}" /> <c:set var="length" value="${450}" /> <c:set var="scheduledRptURL" > <c:out value="${MenuPageBean.contextPath}" />/FileReader?SCHEDID= <c:out value="${curItem.scheduleJob.id}" /> </c:set> </c:when> <c:otherwise> <c:if test="${curItem.scheduleJob.chart}"> <c:set var="scheduledRptURL"> RunSchedule<c:out value="${argv}" />&SCHEDID=<c:out value="${curItem.scheduleJob.id}" />&SCHEDPARAMSETCOUNT=<c:out value="${curItem.scheduleJob.paramSetCount}" /> </c:set> </c:if> </c:otherwise > </c:choose> <td width=20 align="center"><a href=# onClick="O('<c:out value="${scheduledRptURL}" />','RunSchedule',<c:out value="${width}" />,<c:out value="${length}" />); return false;">< <img src="Web_Component/MENU/ViewSchedule.png" alt="View Latest (Scheduled)Version" border=0></a></td>
The variable scheduleRptURL
will be set to the correct value (link) based on whether the item is a chart with parameters, report with parameters or just report or chart without parameters. As such, “onClick”, if the item is a chart without parameters, the FileReader servlet will be run; if the item is a chart with parameters, RunSchedule.jsp
will be run; if the item is a report, action will be directed to MenuReportSecurity.jsp
. Web_Component/MENU/ViewSchedule.png
is the schedule object icon.
If the item has an archive job, the following code will be executed:
<c:choose> <c:when test="${archiveObj.chart}" > <c:set var="archiveURL" > RunArchiveCHT.jsp?ARCHIVEID=<c:out value="${archiveObj.id}" /> </c:set> </c:when> <c:otherwise> <c:set var="archiveURL" > MenuArchiveSecurity.jsp?ARCHIVEID=<c:out value="${archiveObj.id}" /> </c:set> </c:otherwise> </c:choose> <td width=20 align="center"><a href=# onClick="O('<c:out value="${archiveURL}" />', 'RunArchive', 350, 370); return false;"< <img src="Web_Component/MENU/ViewArchive.gif" alt="View Archived Version" border=0></a></td> </c:when> <c:otherwise> <td width=20 align="center"></td> </c:otherwise> </c:choose>
The variable archiveURL
will be set to the correct value (link) based on whether the item is a chart or report. As such, “onClick”, RunArchiveCHT.jsp
will be run if it is a chart; if the item is a report, MenuArchiveSecurity.jsp
will be run. Web_Component/MENU/ViewArchive.gif
is the icon for an archive object.
The remaining (right-most) three columns show the name of the file, description if any, and the date it was last modified. The JSTL tags used are mainly <c:if test...> </c:if>
tags and the <c:choose> ... </c:choose>
tags. They are quite self-explanatory.
Lastly, the following code completes the dual/alternate color scheme for the table:
<c:choose> <c:when test="${rowColor=='innertablewhite'}" > <c:set var="rowColor" value="innertablegrey" /> </c:when> <c:otherwise> <c:set var="rowColor" value="innertablewhite" /> </c:otherwise> </c:choose>
For each report and chart in the main menu page, users will have the option to run that chart or report on-demand as discussed in the previous section. If the item is a report, then RunReport.jsp
will be opened. If the item is a chart then RunReportCHT.jsp
will be opened.
RunReport.jsp
which uses Java bean MenuRunReport
, is invoked when you click the run report/chart icon. The following window will be shown:
In the following block of tags, <c:when test="${menuRunReport.paramReport}" >
checks to see if the report has run-time parameters. If so, the parameter prompts will be displayed by the tag <c:out value="${menuRunReport.paramTable}" escapeXml="false" />
.
<c:choose> <c:when test="${menuRunReport.paramReport}" > <c:set target="${menuRunReport}" property="languageEncodedText" value="Report Parameter List:" /> <tr> <td> <center> <table width=90% cellspacing=1 cellpadding=0 class="outertable"> <tr> <td> <table width=100% cellspacing=0 cellpadding=1 border=0 class="innertablegrey"> <tr> <td colspan=2 class="textbold"> <c:out value="${menuRunReport.languageEncodedText}" /> </td> </tr> <tr><td> <c:out value="${menuRunReport.paramTable}" escapeXml="false" /> </td></tr> </table> </td> </tr> </table> </center></td> </tr> <tr> <td> </tr> </c:when> <c:otherwise> <tr><td> </tr></td> </c:otherwise> </c:choose>
The tag, <c:set target="${menuRunReport}" property="languageEncodedText" value="Report Parameter List:" />
basically allows you to display the content in value
in the language you are working with. You can do this with scriplet as well, i.e. <%= MenuRunReport.getEncodeText( "Report Parameter List")%>
.
If there is an active archive job, you will see the save generated report to archive checkbox as shown in the screen shot. The following code generates the option in the window:
<c:set target="${menuRunReport}" property="languageEncodedText" value="Save Report:" /> <tr> <td> <center> <table width=90% cellspacing=1 cellpadding=0 class="outertable"> <tr> <td> <table width=100% cellspacing=0 cellpadding=1 border=0 class="innertablegrey"> <tr> <td colspan=2 class="textbold"> <c:out value="${menuRunReport.languageEncodedText}" /></td> </tr> <c:set target="${menuRunReport}" property="languageEncodedText" value="Save generated report to archive" /> <tr> <td valign="middle" width=10> <input type=checkbox name="BACKUPTOARCHIVE"></td> <td valign="middle" class="text"><c:out value="${menuRunReport.languageEncodedText}" /></td> </tr> </table> </td> </tr> </table> </center> </td> </tr> <tr> <td> </tr>
The export options are controlled by HTML tags. The remaining hidden input fields for the forms involve three getter methods in the Java bean MenuRunReport
. They are saveLocation, securityLevel
and archID
. These are required values in the request object for the servlet, returned by <c:out value="${menuRunReport.reportURL}" />
, for the form action. The servlet that generates the report is ReportURLReader
.
If the item to run is a chart, RunReportCHT.jsp
will be invoked. The code is very similar to RunReport.jsp
.
Because reports can have different security levels (appearance properties) for different users, the menu page needs to check if the user, who may have overall access privileges to the report, can see the version of the report that is being generated by the schedule.
MenuReportSecurity.jsp
uses the Java bean MenuReportSecurity.class
. This JSP is run when you click on the schedule job icon of a report. The method of interest is the getOnload()
method. If you do not have privilege to see the report, you will be directed to the page schedulePermission.html
. Otherwise, if the report has no parameters, it will just display the report in its exported format. If the report has parameters, RunSchedule.jsp
will be loaded. There is no JSLP code in MenuReportSecurity.jsp
. The getter method (<%= menuRunSched.getFileURL() %>
) used in the following Java script just gets the scheduled export file:
<script language="JavaScript">
function runSchedule() {
window.open(<%= menuRunSched.getFileURL() %>, 'RunningSchedule', 'top=20, left=20, toolbar=no, directories=no, location=no, status=no, menubar=no, resizable=yes, scrollbars=yes, width=750, height=450');
myTimer = setTimeout("window.close();", 1000);
}
As mentioned earlier, RunSchedule.jsp
is invoked if the scheduled item requested is report with parameters, as well as a chart with parameters. You will see the following window displayed by the JSP:
RunSchedule.jsp
uses the Java bean MenuRunSchedule.class
. The drop-down list box allows you to view the parameters in a given parameter set, as well as get the scheduled report output of the selected parameter set. The list box is constructed with the following forEach
loop tags:
<td align="right" valign="middle" width=65%> <select name="PARAMSET" class="search"> <c:forEach begin="0" end="${param.SCHEDPARAMSETCOUNT-1}" var="curIndex" > <c:choose> <c:when test="${curIndex ==0}" > <option value="<c:out value="${curIndex}" />" selected<Parameter Set 1</option> </c:when> <c:otherwise> <option value="<c:out value="${curIndex}" />">Parameter Set <c:out value="${curIndex+1}"/> </option> </c:otherwise> </c:choose> </c:forEach> </select> <INPUT TYPE="hidden" NAME="SCHEDID" VALUE="<c:out value="${param.SCHEDID}" />"> </td>
Note | |
---|---|
Here we use the begin/end version of the |
If you click on the
button, you will see the following window that shows the parameter sets parameter name/value pairs:The following Java script invokes ShowParameters.jsp
to create this window:
function viewParamSet() { paramOption = document.showschedule.PARAMSET.value; windowLocation = "ShowParameters.jsp?SCHEDID=<c:out value="${param.SCHEDID}" /><c:forEach items="${menuRunSchedule.paramNames}" var="currentName"><c:out value="&PARAMNAME=" escapeXml="false"/><c:out value="${currentName}" /></c:forEach<&PARAMSET=" + paramOption; window.open(windowLocation, "ShowParameters", "top=30, left=30, toolbar=no, directories=no, location=no, status=no, menubar=no, resizable=yes, scrollbars=yes, width=200, height=180"); }
Note | |
---|---|
|
ShowParameters.jsp
uses the Java bean MenuScheduleParam.class
. The following forEach
loop displays the parameter set values:
<c:forEach items="${menuScheduleParam.paramNameValue}" var="curItem" > <tr> <td class="text"> <c:out value="${curItem}" /></td> </tr> </c:forEach>
If you click on the archive icon of a chart, RunArchiveCHT.jsp
will be invoked. The Java bean used is MenuRunArchiveCHT.class
. A window similar to the following screen shot will pop up. The parameter set selection list box will be displayed or hidden depending on whether the chart has parameters or not.
The parameter set selection code is similar to that in RunSchedule.jsp
and will be not repeated here. The code that generates the archive selection box is shown below:
<c:set var="ct" value="${menuRunArchiveCHT.archiveFilesCount}" /> <c:forEach begin="0" end="${ct-1}" var="curIndex" > <OPTION value="<c:out value="${menuRunArchiveCHT.reportName[curIndex]}" />" <c:if test="${curIndex == 0}" >selected</c:if> ><c:out value="${menuRunArchiveCHT.archiveDate[curIndex]}" /> </OPTION> </c:forEach> </select></td>
As for parameter set selection, the forEach
tag syntax here uses begin/end to allow for indexing. Based on the archive date you select, the corresponding archive chart will be sent to the servlet, RPT_Generator to render the chart.
If you select to view the archive of a report, MenuArchiveSecurity.jsp
will be invoked. It uses the Java bean MenuArchiveSecurity.class
. The line of code of interest is <%= menuRunArch.getOnLoad() %>
. The bean checks to see if you are of the same security level as the security level saved in the archive. You will be allowed to retrieve the archive on the following conditions: (1) you have no security level assigned to you and(2) you have the same security level as the one saved in the archived file. So, you will be directed to ArchivePermission.html
, or run RunArchive.jsp
. RunArchive.jsp
uses the Java bean MenuRunArchive.class
. You will notice that the code in RunArchive.jsp
is very similar to that in RunArchiveCHT.jsp
. The only difference is the export format options and that you need to supply a security level in the request object for the servlet, LookupServlet, to render the report. The code to get and pass security level to the request object is as follows:
<c:if test="${menuRunArchive.securityLevel !=null && menuRunArchive.securityLevel != ''}"> <tr> <td> <INPUT TYPE="hidden" NAME="SecurityLevel" VALUE="<c:out value="${menuRunArchive.securityLevel}" />"> </td> </tr> </c:if>
The following section details all of the getter methods that are available in the Menu Java Bean classes:
The following getter methods are available in the class MenuMainPage
:
//returns the path "/ERES/servlet" where ERES is the ERES install directory (without "). public String getContextPath() //get the array of folder nodes public ArrayList getFolderList() // if user not loggin yet, return "onload=\"top.location='Menu_Login.jsp'\", otherwise return "". public String getOnLoad() //get the text "Welcome " + user name public String getMenuTitle() //for international language encoding,these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str)
The following getter methods are available in the class FolderNode
:
//get array of items in current menu folder node public ArrayList getItems() //get the name of current folder node public String getNodeName() // get ID of current folder node public int getNodeID() // get number of rows in the current folder node public int getRowCount() //get the tabs, one for each level down in the node hierarchy. You can use this to display folder nodes in staggered fashion public String getBufferTab()
The following getter methods are available in the class ItemNode
:
//returns true if current item is a chart public boolean isChart() //returns true if current item is a report public boolean isReport() //returns true if current item has scheduled job public boolean isScheduleJobAvailable() //returns true if current item has archive job public boolean isArchiveJobAvailable() //get the schedule object of the current item public ItemScheduleJob getScheduleJob() //get the archive object of the current item public ItemArchiveJob getArchiveJob() //get current item's name public String getDocumentName() //get current item's url public String getDocumentURL() //get current item's file path public String getDocumentPath() //get current item's description public String getDocumentDescription() //get the row ID of current item public int getRowID() //get security level of current item public String getSecurityLevel() //get the last modified date of current item public String getLastModified()
The following getter methods are available in the class ItemScheduleJob
:
//get the end date of item's schedule job public Date getEndDate() //get the start date of item's schedule job public Date getStartDate() //get the ID of the schedule job for current item public int getId() //get name of schedule job public String getJobName() // get the number of parameter set for this schedule job public int getParamSetCount() //get the next export time public Date getNextExportTime() //returns true if item is a chart public boolean isChart() //returns true if item is a report public boolean isReport() //returns location of report file public String getReportLocation() //return location of chart file public String getChartLocation()
The following getter methods are available in the class ItemArchiveJob
:
//returns true if archive item is a chart public boolean isChart() //returns true if archive item is a report public boolean isReport() //get the number of parameter set for this archive job public int getParamSetCount() //get ID of archive job public int getId() //get the name of the archive job public String getJobName()
The following getter methods are available in the class MenuRunReport
:
//if user not loggin yet, returns the string: "onload=\"top.location='Menu_Login.jsp'\ //use it to force users to login //otherwise return "". public String getOnLoad() //returns the string: "Run " + name of item public String getMenuTitle() // get the path of servlet ReportURLReader to generate report public String getReportURL() //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText() //get the parameter prompts for the report/chart public String getParamTable() //get security level of report public String getSecurityLevel() //get the archive object ID public int getArchID () //returns true if item has parameters public boolean isParamReport () //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str)
The following getter methods are available in the class MenuRunChart
:
//if user not loggin yet, returns the string: "onload=\"top.location='Menu_Login.jsp'\ //use it to force users to login //otherwise return "". public String getOnLoad() //returns the string: "Run " + name of item public String getMenuTitle() // returns servlet path for RPT_Generator to generate chart public String getChartURL() // get parameter prompts for chart public String getParamTable() //URL for chart file public String getDocumentURL() //archive ID public int getArchID() //returns true if chart has parameters public boolean isParamChart() //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str)
The following getter methods are available in the class MenuRunSchedule
:
// if user did not login, return "onload=\"top.location='Menu_Login.jsp'\"" // otherwise return "" public String getOnLoad() //REPORT TITLE -- get name of item public String getMenuTitle() //get names of parameters of report public String[] getParamNames() //GENERATE FILE READER URL public String getFileReader() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str) //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText()
The following getter methods are available in the class MenuReportSecurity
:
//if user has permission to view item and item has no parameters returns "onload=\"runSchedule()\"" //else if user does not have permission to view item returns //"onload=\"window.location='Web_Component/MENU/SchedulePermission.html' //else if item has parameters returns "onload=\"window.location='RunSchedule.jsp?" + queryString +"' //else returns "" public String getOnLoad() //get url of scheduled export file public String getFileURL() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str)
The following getter methods are available in the class MenuRunArchive
:
//if user did not login, return "onload=\"top.location='Menu_Login.jsp'\"" //otherwise return "" public String getOnLoad() //REPORT TITLE - returns "Report: " + archive object's name public String getMenuTitle() //get names of parameter names of report public String[] getParamNames() //get url of servlet ReportURLReader to generate report public String getReportURL() //get name of report public ArrayList getReportName() //get date of archive file public ArrayList getArchiveDate() //get number of archive files for current item public int getArchiveFilesCount() //get number of parameter sets public int getParamSetCount() //get security level of report public String getSecurityLevel() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str) //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText()
The following getter methods are available in the class MenuRunArchiveCHT
:
//if user did not login, return "onload=\"top.location='Menu_Login.jsp'\"" //otherwise return "" public String getOnLoad() //returns "Chart: " + archive object name public String getMenuTitle() //Get url for the servlet RPT_Generator to generate chart public String getChartURL() //get the names of archived files public ArrayList getReportName() //get the archive dates of the archive items public ArrayList getArchiveDate() //get the archive file count public int getArchiveFilesCount() //get the number of parameter sets public int getParamSetCount() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str) //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText()
The following getter methods are available in the class MenuArchiveSecurity
:
//if user has different security level as that save in the repor:t returns //"onload=\"window.location='Web_Component/MENU/ArchivePermission.html' //otherwise returns "onload=\"window.location='RunArchive.j:sp?" + queryString +"' public String getOnLoad() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str)
The following getter methods are available in the class MenuScheduleParam
:
//if user did not login, return "onload=\"top.location='Menu_Login.jsp'\"" //otherwise return "" public String getOnLoad() //get "Parameter Set " + (paramIndex+1) public String getName() //get parameter name/value pairs for selected parameter set public ArrayList getParamNameValue() //for international language encoding. Text will be translated to language in the language translation file. public String getEncodeText(String str) //for international language encoding //these two methods can be used in JSTL tag, instead of getEncodeText() public void setLanguageEncodedText(String text) public String getLanguageEncodedText()