In this article, I will shortly give an overview how you can edit existing file from the WebContent folder (Don’t miss the first article on this topic).
First, let’s create a view to display the design elements of the WebContent folder. To do this, I have an old school LotusScript Agent which updates the selection formula of a view (Some details about this technique can be found here).
Sub Initialize
Dim session As New NotesSession
Dim doc As NotesDocument
Dim db As NotesDatabase
Dim view As NotesView
Set db = session.Currentdatabase
Set view = db.Getview("DesignView")
view.SelectionFormula = |@Contains($FlagsExt; "w")|
Set doc = db.GetDocumentByUNID(view.UniversalID)
Delete view
doc.ReplaceItemValue "$FormulaClass", "7FFF"
doc.Sign
doc.Save True, False
End Sub
The agent has to run once to change the view’s selection criteria. In this example the view has the name „DesignView“. After that, we can add a single column to the view to display the files and their names:
Now lets build a simple XPage named Files.xsp to select the file you want to edit:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
<xp:viewPanel
rows="30"
id="viewPanel1"
var="rowEntry">
<xp:this.facets>
<xp:pager
partialRefresh="true"
layout="Previous Group Next"
xp:key="headerPager"
id="pager1">
</xp:pager>
</xp:this.facets>
<xp:this.data>
<xp:dominoView
var="viewDesign"
viewName="DesignView">
</xp:dominoView>
</xp:this.data>
<xp:viewColumn
columnName="$FileNames"
id="viewColumnFileNames"
displayAs="link"
>
<xp:this.pageUrl>
<![CDATA[#{javascript:"/Editor.xsp?filename=" +
rowEntry.getColumnValues().get(0)}]]>
</xp:this.pageUrl>
</xp:viewColumn>
</xp:viewPanel>
<xp:br />
</xp:view>
The Files.xsp lists all files as links and opens them via the Editor.xsp XPage:
This is the Editor.xsp:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
FileName:
<xp:inputText
id="inputTextFileName"
value="#{fileBean.fileName}"
defaultValue="#{param.filename}"
disabled="true" />
<xp:br />
FileData:
<xp:inputTextarea
id="inputTextarea1"
value="#{fileBean.fileData}"
rows="40"
cols="80" />
<xp:br />
<xp:button
value="Load"
id="buttonLoad">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
action="#{fileBean.loadData}">
</xp:eventHandler>
</xp:button>
<xp:button
value="Save"
id="buttonSave">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="complete"
action="#{fileBean.saveData}">
</xp:eventHandler>
</xp:button>
</xp:view>
It uses a simple managed bean…
package ch.hasselba.napi;
import java.io.Serializable;
public class FileDataBean implements Serializable {
private static final long serialVersionUID = 1L;
private String fileName;
private String fileData;
private String dbPath;
private String dbServer;
public String getDbPath() {
return dbPath;
}
public void setDbPath(String dbPath) {
this.dbPath = dbPath;
}
public String getDbServer() {
return dbServer;
}
public void setDbServer(String dbServer) {
this.dbServer = dbServer;
}
public void setFileData(String fileData) {
this.fileData = fileData;
}
public String getFileData() {
return fileData;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileName() {
return fileName;
}
public void loadData() {
this.fileData = NAPIUtils.loadFile(this.dbServer, this.dbPath, this.fileName);
}
public void saveData() {
NAPIUtils.saveFile(this.dbServer, this.dbPath, this.fileName, this.fileData);
}
}
… which is initialized with the properties defined in the faces-config.xml. There you can find the database server and the database path:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config>
<managed-bean>
<managed-bean-name>fileBean</managed-bean-name>
<managed-bean-class>ch.hasselba.napi.FileDataBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
<managed-property>
<property-name>dbPath</property-name>
<value>NAPI.nsf</value>
</managed-property>
<managed-property>
<property-name>dbServer</property-name>
<value>DEV01/Hasselba/CH</value>
</managed-property>
</managed-bean>
</faces-config>
And last but not least, the required NAPIUtil class:
package ch.hasselba.napi;
import java.io.InputStream;
import com.ibm.designer.domino.napi.NotesAPIException;
import com.ibm.designer.domino.napi.NotesDatabase;
import com.ibm.designer.domino.napi.NotesNote;
import com.ibm.designer.domino.napi.NotesObject;
import com.ibm.designer.domino.napi.NotesSession;
import com.ibm.designer.domino.napi.design.FileAccess;
public class NAPIUtils {
/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @return the file data as String
*/
static public String loadFile(final String serverName, final String dbPath,
final String fileName) {
NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;
try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// get Filedate and return String
InputStream is = FileAccess.readFileContentAsInputStream(nNote);
return convertStreamToString(is);
} catch (NotesAPIException e) {
e.printStackTrace();
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}
return fileName;
}
/**
* loads a given WebContent file and returns the result as String
*
* @param serverName
* the server to use
* @param dbPath
* the database path
* @param fileName
* the file to load
* @param fileData
* the data of the file
*/
static public void saveFile(final String serverName, final String dbPath,
final String fileName, final String fileData) {
NotesSession nSession = null;
NotesDatabase nDatabase = null;
NotesNote nNote = null;
try {
nSession = new NotesSession();
// open database
nDatabase = nSession.getDatabaseByPath(serverName + "!!" + dbPath);
nDatabase.open();
// load existing data
nNote = FileAccess.getFileByPath(nDatabase, fileName);
// store them to note
FileAccess.saveData(nNote, fileName, fileData.getBytes());
} catch (NotesAPIException e) {
e.printStackTrace();
} finally {
// recycle NAPI objects
recycleNAPIObject(nNote, nDatabase, nSession);
}
}
/**
* converts an input stream to a string
*
* @param is
* the input stream to convert
* @return String
*/
static String convertStreamToString(java.io.InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
/**
* recycleNAPIObject helper method for recycling NAPI objects
*
* @param nObjects
* the NAPI objects to recycle
*/
static void recycleNAPIObject(NotesObject... nObjects) {
for (NotesObject nObject : nObjects) {
if (nObject != null) {
try {
(nObject).recycle();
} catch (NotesAPIException ne) {
}
}
}
}
}
If the class looks like this…
…just hover one of the entries and select „Fix project setup“.
Then, you can choose the missed bundle:
Build the project, and open one of the files by clicking a link in the Files.xsp. Here is an example for the file WEB-INF/faces-config.xml:
Now you can click the „Load“ button to read the content of the file.
You can edit the file now and save it to the NSF.
If you got to package explorer (Hit F9 for refresh) you can see the changes:
Hi Sven, I thought your approach would save my problem. I an app I want to create presentation files based on a template. I thought a good place to store the template would be somewhere in the webcontent folder of the NSF. So I added the lwpd.domino.napi.jar to the build path but when I run my code I get the log message:
2016-09-27 21:09:46 HTTP JVM: com.ibm.designer.domino.napi.NotesAPIException: Error while opening a collection
2016-09-27 21:09:46 HTTP JVM: This function is inappropriate for file system directories.
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.NotesDatabase.NOpenCollection(Native Method)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.NotesDatabase.openCollection(NotesDatabase.java:650)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.domino.napi.design.FileAccess.getFileByPath(FileAccess.java:69)
2016-09-27 21:09:46 HTTP JVM: at ch.hasselba.napi.NAPIUtils.loadFile(NAPIUtils.java:39)
2016-09-27 21:09:46 HTTP JVM: at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2016-09-27 21:09:46 HTTP JVM: at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
2016-09-27 21:09:46 HTTP JVM: at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
2016-09-27 21:09:46 HTTP JVM: at java.lang.reflect.Method.invoke(Method.java:611)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.JavaAccessObject.call(JavaAccessObject.java:321)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.FBSObject.call(FBSObject.java:161)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTCall.interpret(ASTCall.java:197)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTVariableDecl.interpret(ASTVariableDecl.java:82)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject._executeFunction(FunctionObject.java:261)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject.executeFunction(FunctionObject.java:185)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.std.FunctionObject.call(FunctionObject.java:171)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.types.FBSObject.call(FBSObject.java:161)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTCall.interpret(ASTCall.java:197)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTProgram.interpret(ASTProgram.java:119)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.ASTTree.ASTProgram.interpretEx(ASTProgram.java:139)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression._interpretExpression(JSExpression.java:435)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.access$1(JSExpression.java:424)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression$2.run(JSExpression.java:414)
2016-09-27 21:09:46 HTTP JVM: at java.security.AccessController.doPrivileged(AccessController.java:362)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.interpretExpression(JSExpression.java:410)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.evaluateValue(JSExpression.java:251)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.jscript.JSExpression.evaluateValue(JSExpression.java:234)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.javascript.JavaScriptInterpreter.interpret(JavaScriptInterpreter.java:222)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.binding.javascript.JavaScriptMethodBinding.invoke(JavaScriptMethodBinding.java:111)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.component.UIViewRootEx.invokePhaseMethodBinding(UIViewRootEx.java:1735)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.invokePhaseMethodBinding(FacesControllerImpl.java:450)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.access$0(FacesControllerImpl.java:444)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl$ViewPhaseListener.beforePhase(FacesControllerImpl.java:533)
2016-09-27 21:09:46 HTTP JVM: at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:197)
2016-09-27 21:09:46 HTTP JVM: at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:120)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.controller.FacesControllerImpl.render(FacesControllerImpl.java:270)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServlet.serviceView(FacesServlet.java:261)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServletEx.serviceView(FacesServletEx.java:157)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServlet.service(FacesServlet.java:160)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.FacesServletEx.service(FacesServletEx.java:138)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.xsp.webapp.DesignerFacesServlet.service(DesignerFacesServlet.java:103)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:576)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1335)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:853)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:796)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:565)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1319)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:662)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:482)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:357)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:313)
2016-09-27 21:09:46 HTTP JVM: at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)
when I then check the name of the file it gives me the correct name so I think it still looks good.
var template:String = toolbox.loadFile(„“,database.getFilePath(),“template.pptx“);
print(template);
if I then try a open a file inputstream it breaks:
var inputstream:FileInputStream = new FileInputStream(template);
btw I am using SSJS (forced to do so).
do you have any idea what I have overlooked?
hello, this article saved my life indirectly 🙂
thank you very much
I always have to fix the project after almost every build or restart of DDE. Is there a way to permantly fix this issue??