helma/modules/tools/Global/helma.sqlshell.js

754 lines
30 KiB
JavaScript
Raw Normal View History

2020-03-16 16:53:52 +01:00
if (!global.helma) {
global.helma = {};
}
helma.sqlshell = {};
/**
* Get the helma datasource with the given name
*/
helma.sqlshell.getDatasource = function(name) {
return app.getDbSource(name);
}
/**
* Get an array of names of all defined data sources suitable for use
* in html.select macro.
*/
helma.sqlshell.getDatasources = function() {
var dbmap = app.getDbProperties();
var sources = [];
for (var i in dbmap) {
var dot = i.indexOf(".");
if (dot > -1 && i.lastIndexOf(".url") == i.length-4) {
var source = i.substring(0, dot);
sources.push([source, source]);
}
}
return sources;
}
helma.sqlshell.getRepositories = function() {
var rep = [];
var repos = app.getRepositories();
for (var i in repos) {
if (repos[i].getClass() == Packages.helma.framework.repository.FileRepository)
rep.push([i, repos[i].name]);
}
return rep;
}
helma.sqlshell.getProtoRepositories = function(protoName) {
var rep = [];
var proto = app.getPrototype(protoName);
if (proto) {
var repos = proto.getRepositories();
for (var i in repos) {
if (repos[i].getClass() == Packages.helma.framework.repository.FileRepository)
rep.push([i, repos[i].name]);
}
}
return rep;
}
/**
* Main action to set the Helma Dbsource, display forms, perform queries.
*/
helma.sqlshell.main = function() {
// If done, end sqlshell session
if (req.data.done) {
helma.invalidate('sqlshell');
if (session.data.sqlShellReturnUrl) {
var targetUrl = session.data.sqlShellReturnUrl;
delete session.data.sqlShellReturnUrl;
} else {
var targetUrl = path.href();
}
res.redirect(targetUrl);
}
// Check if sqlshell is called from the shell tool
if (req.data.introFrom) {
session.data.sqlShellReturnUrl = req.data.introFrom;
if (session.data.shellAuthenticated)
session.data.sqlshellAuthenticated = true;
}
// Check authentication
if (!helma.auth('sqlshell'))
res.abort();
// Handle authenticated requests
res.handlers.html = helma.sqlshell.html;
var param = {};
param.datasource = req.data.datasource;
res.data.fontface = 'Verdana, sans-serif';
/* if (req.data.schema)
session.data.sqlshellSchema = req.data.schema;
if (req.data.datasource)
session.data.sqlshellDatasource = req.data.datasource; */
var dsource = req.data.datasource ?
helma.sqlshell.getDatasource(req.data.datasource) : null;
if (dsource) {
(new helma.sqlshell.Datasource(dsource)).main();
} else {
if (req.data.datasource && req.isPost()) {
param.message = "Sorry, data source " + req.data.datasource +
" is not defined for this application.";
}
res.data.header = "Choose data source";
res.data.datasources = helma.sqlshell.getDatasources();
res.data.body = renderSkinAsString("helma.sqlshell.selectdb", param);
renderSkin("helma.sqlshell.page");
}
}
helma.sqlshell.Datasource = function(datasource) {
this.datasource = datasource;
this.name = datasource.name;
return this;
}
/**
* Get an array of schema names defined in the current database.
*/
helma.sqlshell.Datasource.prototype.getSchemas = function(meta) {
// get available schemas and set up an array for the drop down box:
var schemas = [];
var t = meta.getSchemas();
while (t.next()) {
var s = t.getString(1);
schemas.push([s, s]);
}
return schemas;
}
/**
* Get table names and set up an array for the drop down box
*/
helma.sqlshell.Datasource.prototype.getTables = function(meta, schema) {
var tables = [["", ""]];
var t = meta.getTables (null, schema, "%", null);
while (t.next()) {
var table = t.getString (3);
tables.push([table, table]);
}
return tables;
}
helma.sqlshell.Datasource.prototype.getColumns = function(meta, schema, table) {
var columns = [];
var t = meta.getColumns(null, schema, table, "%");
while (t.next()) {
columns.push(t.getString(4));
}
return columns;
}
helma.sqlshell.Datasource.prototype.getPrototypes = function() {
var protos = [["", ""]];
var protomap = app.getPrototypes();
for (var i in protomap) {
if (protomap[i].lowerCaseName != "global") {
protos.push([protomap[i].name, protomap[i].name]);
}
}
return protos.sort(function(a, b) {return a < b ? -1 : 1;});
}
helma.sqlshell.Datasource.prototype.href = function(name) {
var href = path.href(req.action) + "?datasource=";
href += encode(req.data.datasource);
href += "&schema=";
href += encode(req.data.schema);
href += "&tab=";
href += req.data.tab;
return href;
}
helma.sqlshell.Datasource.prototype.href_macro = function(param) {
return this.href(param.name);
}
helma.sqlshell.colors = {
explore: "#bd9", query: "#db9", map: "#9bd"
}
helma.sqlshell.Datasource.prototype.main = function() {
res.handlers.datasource = this;
if (!req.data.tab) {
req.data.tab = "explore";
}
res.data.tabcolor = helma.sqlshell.colors[req.data.tab];
var param = new Object();
param.action = this.href();
// get connection
var con = this.datasource.getConnection();
// get database meta data
var meta = con.getMetaData();
res.data.datasources = helma.sqlshell.getDatasources();
res.data.schemas = this.getSchemas(meta);
var schema = req.data.schema;
res.data.tables = this.getTables(meta, schema);
if (req.data.action) {
app.data.repositories = helma.sqlshell.getRepositories();
if (req.data.action == "createproto" ) {
var repos = app.repositories[req.data.repository];
var file = new File(repos.directory.toString(), req.data.protoname);
if (file.mkdir()) {
renderSkin(this.getSkin("closePopup"), {
parentUrl: this.href() + "&prototype=" + req.data.protoname,
message: "<p>Created directory " + file + "</p>" +
"<p>Please wait for prototypes to be updated...</p>"
} );
return;
} else {
res.debug("Couldn't create directory: " + file);
res.data.body = renderSkinAsString(this.getSkin("newproto"));
}
} else if (req.data.action == "extras") {
var p = {};
var t = app.getPrototype(req.data.target);
var target = t && t.dbMapping ? t.dbMapping.tableName : null;
p.targetColumns = this.getColumns(meta, schema, target).toSource();
p.localColumns = this.getColumns(meta, schema, req.data.__sqlshell_table__).toSource();
res.data.body = renderSkinAsString(this.getSkin(req.data.action), p);
} else if (req.data.action == "generate") {
if (req.data.create) {
renderSkin(this.getSkin("closePopup"), {
parentUrl: this.href() + "&prototype=" + req.data.__sqlshell_prototype__,
message: "<p>Created type mapping " + file + "</p>" +
"<p>Please wait for prototypes to be updated...</p>"
} );
} else {
var fields = {};
var s = new java.lang.StringBuffer();
for (var i in req.data) {
if (i.indexOf("maptype_") == 0) {
fields[i.substring(8)] = req.data[i];
}
s.append("<input type='hidden' name='").append(i)
.append("' value='").append("" + req.data[i]).append("'>");
}
if (req.data.__sqlshell_create__) {
// res.data.body = renderSkinAsString(this.getSkin("generate"), p);
var repos = app.getPrototype(req.data.__sqlshell_prototype__).repositories;
var resName = "type.properties";
for (var i in repos) {
var resource = repos[i].getResource(resName);
if (resource && resource.exists()) {
if (resource.getClass() == Packages.helma.framework.repository.FileResource) {
var file = new File(resource.getName());
var backup = new File(resource.getName() + ".bak");
if (backup.exists()) {
var n = 1;
do {
backup = new File(resource.getName() + ".bak." + n++)
} while (backup.exists());
}
if (!file.renameTo(backup)) {
res.debug("ERROR: Couldn't create backup for " + resource);
}
} else {
res.debug("WARNING: Couldn't move " + resource);
}
}
}
var file = new File(repos[req.data.__sqlshell_repository__].getResource(resName).getName());
file.open();
file.writeln("# Created by Helma SqlShell at " + new Date());
if (req.data.__sqlshell_extends__)
file.writeln("_extends = " + req.data.__sqlshell_extends__);
if (req.data.__sqlshell_primaryKey__)
file.writeln("_id = " + req.data.__sqlshell_primaryKey__);
if (req.data.__sqlshell_protoColumn__)
file.writeln("_prototype = " + req.data.__sqlshell_protoColumn__);
if (req.data.__sqlshell_nameColumn__)
file.writeln("_name = " + req.data.__sqlshell_nameColumn__);
file.writeln("");
for (var i in fields) {
var propType = parseInt(fields[i]);
var propName = req.data[i];
if (!propName) continue;
file.write(propName);
file.write(" = ");
switch (propType) {
case 0:
file.writeln(req.data[i]);
break;
case 1:
file.writeln("object(" + req.data["target_" + i] + ")");
break;
case 2:
file.writeln("collection(" + req.data["target_" + i] + ")");
break;
case 3:
file.writeln("mountpoint(" + req.data["target_" + i] + ")");
break;
default:
res.debug(i + ": " + fields[i]);
}
for (var m in this.mappingOptions) {
if (this.mappingOptions[m] <= propType && req.data[i + "_" + m]) {
file.write(propName);
file.write(".");
file.write(m.replace("_", "."));
file.write(" = ");
file.writeln(req.data[i + "_" + m]);
}
}
file.writeln("");
}
file.close();
res.data.body = "Successfully created mapping in " + file;
} else {
var p = {};
p.data = s.toString();
res.data.repositories = helma.sqlshell.getProtoRepositories(req.data.__sqlshell_prototype__);
res.data.body = renderSkinAsString(this.getSkin("generate"), p);
}
}
} else {
res.data.body = renderSkinAsString(this.getSkin(req.data.action));
}
} else {
// should we display type info on some table?
if (req.data.tab == "explore") {
param.body = this.explore(meta, schema, param);
} else if (req.data.tab == "query") {
param.body = this.query(con, param);
} else if (req.data.tab == "map") {
param.body = this.map(meta, schema, con, param);
}
// render the inner page skin and then the whole page
res.data.body = renderSkinAsString("helma.sqlshell.main", param);
}
renderSkin("helma.sqlshell.page");
}
helma.sqlshell.Datasource.prototype.explore = function(meta, schema, param) {
res.push();
renderSkin(this.getSkin("explore"), param);
if (req.data.__sqlshell_table__) {
var tableStyle = { table: { "class": "explore" }, td: { "class": "explore" } };
var t = meta.getColumns(null, schema, req.data.__sqlshell_table__, "%");
var writer = new helma.Markup.TableWriter(6, tableStyle);
writer.writeHeader = true;
var columnNames = ["Column Name", "Column Type", "Column Size",
"Nullable", "Default Value", "Extras"];
for (var c in columnNames) {
writer.write(columnNames[c]);
}
while (t.next()) {
writer.write(t.getString(4));
writer.write(t.getString(6));
writer.write(t.getString(7));
writer.write(t.getString(18));
writer.write(t.getString(13));
writer.write(t.getString(12));
}
writer.close();
}
return res.pop();
}
helma.sqlshell.Datasource.prototype.query = function(con, param) {
// some SQL has been submitted - evaluate it
if (req.data.sql) {
var query = req.data.sql.trim();
con.setReadOnly(false);
var stmt = con.createStatement();
var value;
try {
value = stmt.execute(query);
if (!value) {
param.updated = stmt.getUpdateCount();
} else {
var rs = stmt.getResultSet();
var rsmeta = rs.getMetaData();
var ncol = rsmeta.getColumnCount();
res.push();
var tableStyle = { table: { "class": "query" }, td: { "class": "query" } };
var writer = new helma.Markup.TableWriter(ncol, tableStyle);
writer.writeHeader = true;
for (var i=1; i<=ncol; i++) {
writer.write(rsmeta.getColumnName(i));
}
while (rs.next()) {
for (var i=1; i<=ncol; i++) {
writer.write(encode(rs.getString(i)));
}
}
writer.close();
param.resultset = res.pop();
}
} catch (error) {
param.message = "Error: " + error;
}
}
return renderSkinAsString(this.getSkin("query"), param);
}
helma.sqlshell.Datasource.prototype.map = function(meta, schema, con, param) {
// for (var i in req.data) res.debug(i);
res.push();
res.data.prototypes = this.getPrototypes();
var proto = app.getPrototype(req.data.__sqlshell_prototype__);
if (proto) {
var tableStyle = { table: { "class": "map" }, td: { "class": "map" } };
var dbmap = proto.getDbMapping();
if (!req.data.__sqlshell_table__ ||
req.data.__sqlshell_prototype__ != req.data.previousProto) {
req.data.__sqlshell_table__ = dbmap.tableName;
}
param.tableSelect = renderSkinAsString(createSkin('Map to table \
<% html.select name="__sqlshell_table__" options="response.tables" \
onchange="document.forms.tab.submit();"%>'));
}
renderSkin(this.getSkin("map"), param);
if (proto) {
var maptypes = ["Primitive", "Reference", "Collection", "Mountpoint"];
var tableStyle = { table: { "class": "map" }, td: { "class": "map" } };
if (req.data.__sqlshell_table__) {
var primKey = "";
try {
var k = meta.getPrimaryKeys(null, schema, req.data.__sqlshell_table__);
if (k.next()) {
primKey = k.getString(4);
}
if (k.next()) {
helma.Markup.p({"class": "error"}, "Table has composed primary key!");
}
} catch (error) {
helma.Markup.p({"class": "error"}, "Error retrieving primary key: " + error);
}
var t = meta.getColumns(null, schema, req.data.__sqlshell_table__, "%");
var columns = [];
res.data.columns = [["", ""]];
while (t.next()) {
var colname = t.getString(4);
columns.push(colname);
res.data.columns.push([colname, colname]);
}
var writer = new helma.Markup.TableWriter(2, tableStyle);
writer.write("Extends ");
var ext = dbmap.getExtends() || app.getPrototype("hopobject").name;
writer.write(helma.Markup.Select({name: "__sqlshell_extends__"}, res.data.prototypes, ext));
writer.write("Primary key column ");
writer.write(helma.Markup.Select({name: "__sqlshell_primaryKey__"}, res.data.columns, primKey));
writer.write("Prototype column ");
writer.write(helma.Markup.Select({name: "__sqlshell_protoColumn__"}, res.data.columns, dbmap.prototypeField));
writer.write("Name column ");
writer.write(helma.Markup.Select({name: "__sqlshell_nameColumn__"}, res.data.columns, dbmap.nameField));
writer.close();
tableStyle = { table: { "class": "map", id: "maptable" }, td: { "class": "map" } };
writer = new helma.Markup.TableWriter(5, tableStyle);
writer.writeHeader = true;
var headers = ["Column Name", "Property Name", "Mapping",
"Target Prototype", "Extras"];
for (var c in headers) {
writer.write(headers[c]);
}
for (var col in columns) {
var colname = columns[col];
// if (colname == primKey) continue;
var rel = dbmap.columnNameToRelation(colname);
var value = rel && rel.propName ? rel.propName : this.toCamelCase(colname);
var type = rel ? rel.refType : 0;
var targetDisplay = type > 0 ? '': ' style="display: none;"';
var target = rel && rel.targetType ? rel.targetType.typeName : "";
writer.write(colname);
writer.write('<input name="' + colname + '" value="' + value +'">');
writer.write(helma.Markup.Select({name: "maptype_" + colname,
onchange: "toggleEditor(this)"}, maptypes, type));
writer.write('<div id="refedit_' + colname + '"' + targetDisplay +'>' +
helma.Markup.Select({name: "target_" + colname}, res.data.prototypes, target) + '</div>');
var buffer = new java.lang.StringBuffer();
var config = rel ? wrapJavaMap(rel.config) : {};
for (var i in this.mappingOptions) {
// var name = i.replace('_', '.');
var name = colname + "_" + i;
buffer.append(helma.Markup.Hidden({id: name, name: name, value: config[i] }).toString());
}
buffer.append(helma.Markup.A({href: this.href() + "&action=extras&col=" + colname,
id:"extralink_" + colname, style: type > 0 ? '': 'display: none;',
onclick: "openExtraEditor(this.href, '" + colname + "'); return false;"},
"edit").toString());
writer.write(buffer);
/* writer.write(helma.Markup.A({href: this.href() + "&action=extras&col=" + colname,
id:"extralink_" + colname, style: type > 0 ? '': 'display: none;',
onclick: "openPopup(this.href, 'extras'); return false;"},
"edit")); */
}
var props = dbmap.getPropertyNames();
var collectionCount = 0;
for (var p in props) {
var rel = dbmap.propertyToRelation(props[p]);
if (rel.refType < 1 || (rel.dbField && rel.dbField != primKey)) {
continue;
}
var propName = rel.propName;
var target = rel.targetType ? rel.targetType.typeName : "";
var type = rel.refType;
if (type == 2 && !rel.dbField) {
// helma does not separate between collections and mountpoints internally
type = 3;
}
var colname = "collection_" + (collectionCount++);
writer.write("");
writer.write('<input name="' + colname + '" value="' + propName +'">');
writer.write(helma.Markup.Select({name: "maptype_" + colname,
onchange: "toggleEditor(this)"}, maptypes, type));
writer.write('<div id="refedit_' + colname + '">' +
helma.Markup.Select({name: "target_" + colname}, res.data.prototypes, target) + '</div>');
var buffer = new java.lang.StringBuffer();
var config = wrapJavaMap(rel.config);
for (var i in this.mappingOptions) {
// var name = i.replace('_', '.');
var name = colname + "_" + i;
buffer.append(helma.Markup.Hidden({id: name, name: name, value: config[i] }).toString());
}
buffer.append(helma.Markup.A({href: this.href() + "&action=extras&col=" + colname,
id:"extralink_" + colname,
onclick: "openExtraEditor(this.href, '" + colname + "'); return false;"},
"edit").toString());
writer.write(buffer);
}
writer.close();
// FIXME: MAJOR HACK **********************************
res.writeln('<script type="text/javascript">');
res.writeln('var colcount = ' + collectionCount + ';');
res.write('var rowtemplate = \'<td class="map"></td><td class="map"><input name="$$$" value=""></td>');
res.write('<td class="map">')
helma.Markup.select({name: "maptype_$$$",
onchange: "toggleEditor(this)"}, maptypes, 2);
res.write('</td><td class="map">')
res.write('<div id="refedit_$$$">' +
helma.Markup.Select({name: "target_$$$"}, res.data.prototypes, target) + '</div>');
res.write('</td><td class="map">')
for (var i in this.mappingOptions) {
// var name = i.replace('_', '.');
var name = "$$$_" + i;
helma.Markup.hidden({id: name, name: name, value: "" });
}
helma.Markup.a({href: this.href() + "&action=extras&col=$$$",
id:"extralink_$$$",
onclick: "openExtraEditor(this.href, \\'$$$\\'); return false;"},
"edit");
res.write('</td>');
res.writeln("';");
res.writeln('</script>');
// END OF MAJOR HACK **********************************
helma.Markup.a({href: "#", onclick:'return appendTableRow("maptable");'}, "Add Collection");
res.write(" ");
helma.Markup.submit({name: "generateMapping",
onclick:"submitFormToPopup(document.forms.tab, '" + this.href() + "&action=generate', 'generate',500,350); return false;",
value: "Generate Mapping"});
}
}
return res.pop();
}
helma.sqlshell.Datasource.prototype.mappingOptions = {
local: 1,
foreign: 1,
order: 2,
accessname: 2,
group: 2,
group_order: 2,
group_prototype: 2,
filter: 2,
filter_additionalTables: 2,
loadmode: 1,
cachemode: 2,
maxsize: 2,
hints: 2,
logicalOperator: 2,
readonly: 2,
"private": 2
}
helma.sqlshell.Datasource.prototype.toCamelCase = function(str) {
var s = str.toLowerCase().split(/[-_]/);
str = s[0];
for (var i=1; i<s.length; i++) {
str += s[i].charAt(0).toUpperCase() + s[i].substring(1);
}
return str;
}
helma.sqlshell.Datasource.prototype.getSkin = function(name) {
switch (name) {
case "query": return createSkin('<div class="activetab">SQL:<br>\
<textarea cols="60" rows="6" wrap="virtual"\
name="sql"><% request.sql encode="form" %></textarea><br>\
<input type="submit" value="Submit"> \
<input type="reset" value="Reset"></div>\
<% param.message prefix="<p style=\'color: red\'>" suffix="</p>" %>\
<% param.updated prefix="<p><span style=\'background-color: yellow\'>"\
prefix="Statement executed, "\
suffix=" row(s) affected</span></p>" %>\
<% param.resultset %>');
case "explore": return createSkin('<div class="activetab">Describe Table \
<% html.select name="__sqlshell_table__" options="response.tables" \
onchange="document.forms.tab.submit();" %>\
<input type="submit" value="Submit"></div>\
<% param.tableheader prefix="<h4>" suffix="</h4>" %>\
<% param.tableinfo %>');
case "map": return createSkin('<div class="activetab">Prototype \
<% html.select name="__sqlshell_prototype__" options="response.prototypes" \
onchange="document.forms.tab.submit();" %>\
<a onclick="openPopup(\'' +this.href() + '&action=newproto\', \'newproto\',500,250); return false;" \
href="?popup=newproto">[new]</a>\
<% param.tableSelect %> \
<input type="hidden" name="previousProto" value="<% request.__sqlshell_prototype__ %>">\
<input type="submit" value="Set">\
<input type="reset" value="Reset">\
</div>');
case "newproto": return createSkin('<form method="POST" action="' + this.href() + '&action=createproto">\
Prototype Name: <br><input name="protoname"><br>\
Create in Repository: <br><% html.select name="repository" options="app.repositories" %><br><br>\
<input type="submit" name="create" value="Create Prototype">\
</form>');
case "extras": return createSkin('<form name="extras">\
<h4>Extra parameters for ' + req.data.col + '</h4>\
<table>\
<script type="text/javascript">\
var localColumns = <% param.localColumns %>;\
var targetColumns = <% param.targetColumns %>;\
extraEditor("' + req.data.col + '", localColumns, targetColumns);\
</script>\
</table>\
<input type="submit" value="Apply" \
onclick="applyExtras(\'' + req.data.col + '\'); return false;">\
</form>');
case "generate": return createSkin('<form method="POST" action="' + this.href() + '&action=generate">\
Create type.properties in Repository: <br><% html.select name="__sqlshell_repository__"\
options="response.repositories" %><br><br>\
<input type="submit" name="__sqlshell_create__" value="Create Type Mapping">\
<% param.data %>\
</form>');
case "closePopup": return createSkin('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\
<!--\
This is an empty page that closes the current popup window and\
optionally refreshes the parent page (passed as parentUrl).\
-->\
<html>\
<head>\
<title>closing window</title>\
<script>\
var parentUrl = "<% param.parentUrl %>";\
function doClose() {\
if (parentUrl)\
opener.location.href = parentUrl;\
window.close();\
}\
setTimeout("doClose()", 150000);\
</script>\
</head>\
<body>\
<% param.message %>\
</body>\
</html>');
default: return createSkin("No skin defined for " + name);
}
}
helma.sqlshell.Datasource.href =
HopObject.prototype[ (app.properties['sqlshellAction'] || 'sqlshell') +'_action' ] = helma.sqlshell.main;
helma.dontEnum('sqlshell');
////////////////////////////////////////////////////////////////////////
// Macro handler for Html tags
////////////////////////////////////////////////////////////////////////
helma.sqlshell.html = {
tablink_macro: function(param) {
var href = req.action + "?datasource=";
href += encode(req.data.datasource);
href += "&schema=";
href += encode(req.data.schema);
href += "&tab=";
href += param.name;
var attr = { href: href, "class": "tab" };
if (req.data.tab == param.name) {
attr["class"] += " activetab";
} else {
attr["class"] += " passivetab";
}
helma.Markup.element("a", attr, param.name);
},
select_macro: function(param) {
if (!param.name) {
throw "dropDown macro requires name attribute";
}
if (!param.options) {
throw "dropDown macro requires options attribute";
}
var opts = param.options.split(".");
if (opts.length != 2) {
throw "options attribute must be of the form 'handler.options'";
}
var handler = this.getHandler(opts[0]);
if (!handler) {
throw "handler '"+opts[0]+" not found - " +
"valid options are (response|request|session|app)";
}
var options = handler[opts[1]];
if (!options) {
throw param.options+" is not defined ";
}
if (options.length == 0) {
return;
}
var attr = {};
for (var i in param) {
if (i != "options" && i != "prefix" && i != "suffix") {
attr[i] = param[i];
}
}
helma.Markup.select(attr, options, req.data[param.name], param.firstoption);
},
getHandler: function (handlerName) {
switch (handlerName) {
case "response":
return res.data;
case "request":
return req.data;
case "session":
return session.data;
case "app":
return app.data;
}
return null;
}
}