# 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`