# Steps to Add FileManager Integration and Browser Storage to PaperScape Apps
This guide shows how to add FileManager integration, browser storage support, and persistent save location tracking to PaperScape Giraffe apps, using the centralized PaperScape.fileManager API.
## Prerequisites
1. The app must have `toJson()` and `fromJson()` methods for serializing/deserializing state
2. The app must be a Giraffe app loaded via PaperScape
3. PaperScape must be loaded (provides centralized FileManager integration)
## Step 1: Add Storage Location State Variables
In your app's constructor, add these instance variables:
```javascript
self.storagePath = "/YourAppName/default-state.json"; // Default path
self.storageIsBrowser = true; // Default to browser storage
```
## Step 2: Add Path Cleaning Helper Function
Use PaperScape's centralized path cleaning utility:
```javascript
var updateStoragePath = function(path) {
if(typeof path !== "string") {
self.storagePath = "";
return self.storagePath;
}
// Use PaperScape's centralized path cleaning utility
var cleaned = controllerRef && controllerRef.storage ? controllerRef.storage.cleanPath(path) : path.trim();
self.storagePath = cleaned;
return self.storagePath;
};
```
## Step 3: Update toJson() to Include Storage Metadata
Modify your `toJson()` method to include storage location:
```javascript
self.toJson = function() {
var json = {
// ... your existing state properties ...
};
// Include storage location metadata if available
if(self.storagePath && self.storagePath !== DEFAULT_PATH) {
json.storagePath = self.storagePath;
json.storageIsBrowser = self.storageIsBrowser;
}
return json;
};
```
## Step 4: Update fromJson() to Restore Storage Metadata
Modify your `fromJson()` method to restore storage location:
```javascript
self.fromJson = function(jsonData) {
if(!jsonData || typeof jsonData !== "object") {
console.error("Invalid JSON data provided");
return;
}
// ... restore your existing state properties ...
// Restore storage location metadata if present
if(jsonData.storagePath) {
updateStoragePath(jsonData.storagePath);
self.storageIsBrowser = jsonData.storageIsBrowser || false;
console.log("[YourApp] Restored storage location from JSON:", jsonData.storagePath, "isBrowser:", self.storageIsBrowser);
} else {
console.log("[YourApp] No storage location in JSON - file will save to default path unless you use 'Save As'");
}
// ... continue with rendering ...
};
```
## Step 5: Implement saveToFile() Method
Add this method to handle saving to both browser and server storage:
```javascript
self.saveToFile = function(targetPath, isBrowser) {
var path = (typeof targetPath === "string" && targetPath.trim().length) ? targetPath.trim() : self.storagePath;
if(!path || !path.length) {
alert("Please provide a file path to save.");
return;
}
// Use stored value if isBrowser is not explicitly provided
var useBrowser = (typeof isBrowser !== 'undefined') ? isBrowser : self.storageIsBrowser;
// Update stored value when explicitly provided
if(typeof isBrowser !== 'undefined') {
self.storageIsBrowser = isBrowser;
}
updateStoragePath(path);
var snapshot = self.toJson();
var payload = JSON.stringify(snapshot, null, 2);
// Save to browser storage if useBrowser is true
if(useBrowser) {
if(typeof OpenForum !== "undefined" && typeof OpenForum.saveFile === "function") {
var browserPath = 'browser://' + path;
OpenForum.saveFile(browserPath, payload, function(response) {
if(response && response.saved) {
console.log("[YourApp] Saved to browser storage: " + path);
} else {
alert("Failed to save to browser storage: " + path);
}
});
} else {
alert("Browser storage is not available");
}
return;
}
// Save to server
if(typeof OpenForum !== "undefined" && typeof OpenForum.saveFile === "function") {
OpenForum.saveFile(path, payload, function(response) {
if(response && response.result === "ok") {
console.log("[YourApp] Saved to server: " + path);
} else {
alert("Failed to save to " + path);
}
});
}
};
```
## Step 6: Implement loadFromFile() Method
Add this method to handle loading from both browser and server storage:
```javascript
self.loadFromFile = function(targetPath, isBrowser) {
var path = (typeof targetPath === "string" && targetPath.trim().length) ? targetPath.trim() : self.storagePath;
if(!path || !path.length) {
alert("Please provide a file path to load.");
return;
}
// Build the full path with browser:// prefix if needed
var loadPath = path;
if(isBrowser && !path.startsWith('browser://')) {
loadPath = 'browser://' + path;
}
if(typeof OpenForum !== "undefined" && typeof OpenForum.loadJSON === "function") {
OpenForum.loadJSON(loadPath, function(json) {
if(!json) {
alert("No data found at " + path);
return;
}
self.fromJson(json);
// Set storage path AFTER fromJson to ensure it's not overwritten
updateStoragePath(path);
if(typeof isBrowser !== 'undefined') {
self.storageIsBrowser = isBrowser;
}
console.log("[YourApp] Loaded from " + loadPath + " (isBrowser: " + isBrowser + ")");
console.log("[YourApp] Storage location set to:", self.storagePath, "isBrowser:", self.storageIsBrowser);
});
} else {
alert("OpenForum.loadJSON not available");
}
};
```
## Step 7: Add UI Methods for Save As and Open
Use the centralized PaperScape.fileManager API:
```javascript
self.saveModelAs = function() {
// Use centralized PaperScape FileManager
if(controllerRef && controllerRef.fileManager && typeof controllerRef.fileManager.showSave === "function") {
var currentFilename = self.storagePath ? self.storagePath.substring(self.storagePath.lastIndexOf('/') + 1) : 'default.json';
controllerRef.fileManager.showSave(self, currentFilename);
} else {
alert("FileManager is not available");
}
};
self.openModel = function() {
// Use centralized PaperScape FileManager
if(controllerRef && controllerRef.fileManager && typeof controllerRef.fileManager.showLoad === "function") {
controllerRef.fileManager.showLoad(self);
} else {
alert("FileManager is not available");
}
};
```
## Step 8: Add UI Buttons
Add buttons to your app's control panel or UI. These can call the methods directly:
```javascript
html += '';
html += '';
```
Or if using PaperScape dialog system, add them as dialog actions.
## Important Notes
### Path Handling Rules
1. **Never store `browser://` prefix in `self.storagePath`** - it should only contain the path
2. **Always add `browser://` prefix when calling `OpenForum.saveFile()` or `OpenForum.loadFile()` for browser storage**
3. **Use `controllerRef.storage.cleanPath()`** to strip prefixes and clean up paths
4. **Use `isBrowser` flag** to track whether storage is browser or server
### Auto-Save Behavior
If your app has auto-save, make sure it uses the stored `isBrowser` flag:
```javascript
function autoSave() {
// Don't pass isBrowser parameter - let saveToFile use stored value
self.saveToFile(self.storagePath);
}
```
### Testing Checklist
- [ ] "Save As" to browser storage creates file with key like `/FolderName/filename.json`
- [ ] "Save As" to server storage saves to server path
- [ ] After "Save As" to browser, auto-save continues to browser storage
- [ ] After "Save As" to server, auto-save continues to server storage
- [ ] Opening a file from browser storage remembers location for future saves
- [ ] Opening a file from server storage remembers location for future saves
- [ ] Closing and reopening an app preserves the save location via PaperScape state
## Example: Setting Browser Storage as Default
To make your app default to browser storage:
```javascript
var DEFAULT_PATH = "/YourAppName/default-state.json";
// In constructor:
self.storagePath = DEFAULT_PATH;
self.storageIsBrowser = true; // Default to browser storage
```
## Centralized PaperScape.fileManager API
The PaperScape framework provides centralized FileManager integration:
### `PaperScape.fileManager.showSave(appInstance, currentFilename)`
Shows FileManager in save mode. Your app must implement `saveToFile(path, isBrowser)`.
### `PaperScape.fileManager.showLoad(appInstance)`
Shows FileManager in open mode. Your app must implement `loadFromFile(path, isBrowser)`.
### `PaperScape.fileManager.close()`
Closes the FileManager popup.
### `PaperScape.fileManager.isAvailable()`
Returns true if FileManager is available.
### `PaperScape.storage.cleanPath(path)`
Cleans a path by removing `browser://` prefix and extra leading slashes.
### `PaperScape.storage.browserPath(path)`
Builds a browser storage URL by adding `browser://` prefix to a cleaned path.
## Building Your App
After making changes to your Giraffe app source files, rebuild using:
```
http://localhost:8888/OpenForum/AddOn/ServiceBuilder?action=buildJavascript&pageName=/YourAppPage&fileName=script.build.json
```
## Additional Resources
- FileManager API: `/Development/OpenForum/AddOn/FileManager/`
- OpenForum Browser Storage: `/OpenForum/Javascript/Core/open-forum-browser.js`
- PaperScape Core: `/OpenForum/AddOn/PaperScape/paperscape.js` (lines 802-934)
- DependencyModelView Reference: `/OpenForum/AddOn/PaperScape/DependencyModelView/dependency-model-view-core.giraffe.js`