Index: chrome/common/extensions/docs/templates/articles/app_codelab_filesystem.html |
diff --git a/chrome/common/extensions/docs/templates/articles/app_codelab_filesystem.html b/chrome/common/extensions/docs/templates/articles/app_codelab_filesystem.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..483b4299ec0affa6c8d16f153dab6d8767b31343 |
--- /dev/null |
+++ b/chrome/common/extensions/docs/templates/articles/app_codelab_filesystem.html |
@@ -0,0 +1,271 @@ |
+<h1 id="export-to-filesystem"> |
+ <span class="h1-step">Step 6:</span> |
+ Export Todos to the Filesystem |
+</h1> |
+ |
+<p class="note"> |
+ <strong>Want to start fresh from here?</strong> |
+ Find the previous step's code in the <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step5</strong></em>. |
+</p> |
+ |
+<p>In this step, you will learn:</p> |
+ |
+<ul> |
+ <li>How to get a reference to a file in the external filesystem.</li> |
+ <li>How to write to the filesystem.</li> |
+</ul> |
+ |
+<p> |
+ <em>Estimated time to complete this step: 20 minutes.</em> |
+ <br> |
+ To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page ↓</a>. |
+</p> |
+ |
+<h2 id="export-todos">Export todos</h2> |
+ |
+<p>This step adds an export button to the app. |
+When clicked, the current todo items are saved to a text file selected by the user. |
+If the file exists, it's replaced. Otherwise, a new file gets created.</p> |
+ |
+<h3 id="update-permissions">Update permissions</h3> |
+ |
+<p>File system permissions can be requested as a string for read-only access, or an Object with additional properties. For example:</p> |
+ |
+<pre> |
+// Read only |
+"permissions": [<b>"fileSystem"</b>] |
+ |
+// Read and write |
+"permissions": [<b>{"fileSystem": ["write"]}</b>] |
+ |
+// Read, write, autocomplate previous input, and select folder directories instead of files |
+"permissions": [<b>{"fileSystem": ["write", "retainEntries", "directory"]}</b>] |
+</pre> |
+ |
+<p>You need read and write access. |
+In <em>manifest.json</em>, request the <code>{fileSystem: [ "write" ] }</code> permission:</p> |
+ |
+<pre data-filename="manifest.json"> |
+"permissions": ["storage", "alarms", "notifications", "webview", |
+ "<all_urls>"<b>, { "fileSystem": ["write"] }</b> ], |
+</pre> |
+ |
+ |
+<h3 id="update-html-view">Update HTML view</h3> |
+ |
+<p>In <em>index.html</em>, add an <b>Export to disk</b> button and |
+a <code>div</code> where the app shows a status message:</p> |
+ |
+<pre data-filename="index.html"> |
+<footer id="info"> |
+ <button id="toggleAlarm">Activate alarm</button> |
+ <b><button id="exportToDisk">Export to disk</button></b> |
+ <b><div id="status"></div></b> |
+ ... |
+</footer> |
+</pre> |
+ |
+<p>Also in <em>index.html</em>, load the <em>export.js</em> script:</p> |
+ |
+<pre data-filename="index.html"> |
+... |
+<script src="js/alarms.js"></script> |
+<b><script src="js/export.js"></script></b> |
+</pre> |
+ |
+<h3 id="create-js">Create export script</h3> |
+ |
+<p>Create a new JavaScript file named <em>export.js</em> using the code below. |
+Save it in the <em>js</em> folder.</p> |
+ |
+<pre data-filename="export.js"> |
+(function() { |
+ |
+ var dbName = 'todos-vanillajs'; |
+ |
+ var savedFileEntry, fileDisplayPath; |
+ |
+ function getTodosAsText(callback) { |
+ } |
+ |
+ function exportToFileEntry(fileEntry) { |
+ } |
+ |
+ function doExportToDisk() { |
+ } |
+ |
+ document.getElementById('exportToDisk').addEventListener('click', doExportToDisk); |
+ |
+})(); |
+</pre> |
+ |
+<p>Right now, <em>export.js</em> only contains a click listener on the <strong>Export to disk</strong> button and stubs for <code>getTodosAsText()</code>, <code>exportToFileEntry</code>, and <code>doExportToDisk()</code>.</p> |
+ |
+<h3 id="get-todos-as-text">Get todo items as text</h3> |
+ |
+<p>Update <code>getTodosAsText()</code> so that it reads todos from <code>chrome.storage.local</code> and generates a textual representation of them:</p> |
+ |
+<pre data-filename="export.js"> |
+function getTodosAsText(callback) { |
+<b> chrome.storage.local.get(dbName, function(storedData) { |
+ var text = ''; |
+ |
+ if ( storedData[dbName].todos ) { |
+ storedData[dbName].todos.forEach(function(todo) { |
+ text += '- '; |
+ if ( todo.completed ) { |
+ text += '[DONE] '; |
+ } |
+ text += todo.title; |
+ text += '\n'; |
+ }, ''); |
+ } |
+ |
+ callback(text); |
+ |
+ }.bind(this));</b> |
+} |
+</pre> |
+ |
+<h3 id="choose-file">Choose a file</h3> |
+ |
+<p>Update <code>doExportToDisk()</code> with <code><a href="/apps/fileSystem#method-chooseEntry">chrome.fileSystem.chooseEntry()</a></code> to allow the user to choose a file:</p> |
+ |
+<pre data-filename="export.js"> |
+function doExportToDisk() { |
+ |
+<b> if (savedFileEntry) { |
+ |
+ exportToFileEntry(savedFileEntry); |
+ |
+ } else { |
+ |
+ chrome.fileSystem.chooseEntry( { |
+ type: 'saveFile', |
+ suggestedName: 'todos.txt', |
+ accepts: [ { description: 'Text files (*.txt)', |
+ extensions: ['txt']} ], |
+ acceptsAllTypes: true |
+ }, exportToFileEntry); |
+ |
+ }</b> |
+} |
+</pre> |
+ |
+<p>The first parameter of <code>chrome.fileSystem.chooseEntry()</code> is an object of options. The second parameter is a callback method.</p> |
+ |
+<p>If there's already a saved <code>FileEntry</code>, |
+use that instead when calling <code>exportToFileEntry()</code>. |
+File references exist for the lifetime of the object representing the <code>FileEntry</code>. |
+This example ties <code>FileEntry</code> to the app window so the JavaScript code can write |
+to the selected file without any user interaction as long as the app window remains open.</p> |
+ |
+<h3 id="use-fileentry">Use FileEntry to write todos items to disk</h3> |
+ |
+<p>Update <code>exportToFileEntry()</code> to save the todos as text via the <code>FileEntry</code> Web API:</p> |
+ |
+<pre data-filename="export.js"> |
+function exportToFileEntry(fileEntry) { |
+<b> savedFileEntry = fileEntry; |
+ |
+ var status = document.getElementById('status'); |
+ |
+ // Use this to get a file path appropriate for displaying |
+ chrome.fileSystem.getDisplayPath(fileEntry, function(path) { |
+ fileDisplayPath = path; |
+ status.innerText = 'Exporting to '+path; |
+ }); |
+ |
+ getTodosAsText( function(contents) { |
+ |
+ fileEntry.createWriter(function(fileWriter) { |
+ |
+ var truncated = false; |
+ var blob = new Blob([contents]); |
+ |
+ fileWriter.onwriteend = function(e) { |
+ if (!truncated) { |
+ truncated = true; |
+ // You need to explicitly set the file size to truncate |
+ // any content that might have been there before |
+ this.truncate(blob.size); |
+ return; |
+ } |
+ status.innerText = 'Export to '+fileDisplayPath+' completed'; |
+ }; |
+ |
+ fileWriter.onerror = function(e) { |
+ status.innerText = 'Export failed: '+e.toString(); |
+ }; |
+ |
+ fileWriter.write(blob); |
+ |
+ }); |
+ });</b> |
+} |
+</pre> |
+ |
+<p><code><a href="/apps/fileSystem#method-getDisplayPath">chrome.fileSystem.getDisplayPath()</a></code> gets a displayable file path that outputs to the status <code>div</code>.</p> |
+ |
+<p>Use <code>fileEntry.createWriter()</code> to create a <code>FileWriter</code> object. <code>fileWriter.write()</code> can then write a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a> to the filesystem. Use <code>fileWriter.onwriteend()</code> and <code>fileWriter.onerror()</code> to update the status <code>div</code>.</p> |
+ |
+<!-- <code>fileWriter.truncate()</code> --> |
+ |
+<p>For more information about <code>FileEntry</code>, read <a href="http://www.html5rocks.com/en/tutorials/file/filesystem/"><em>Exploring the FileSystem APIs</em></a> on HTML5Rocks, or refer to the <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/FileEntry">FileEntry docs</a></code> on MDN.</p> |
+ |
+<h3 id="persistance">Persist FileEntry objects</h3> |
+ |
+<p><strong>Advanced</strong>: <code>FileEntry</code> objects cannot be persisted indefinitely. Your app needs to ask the user to choose a file every time the app is launched. |
+If your app was forced to restart due to a runtime crash or update, |
+<a href="/apps/fileSystem#method-restoreEntry">restoreEntry()</a> |
+is an option to restore a <code>FileEntry</code>.</p> |
+ |
+<p>If you wish, experiment by saving the ID returned by |
+<a href="/apps/fileSystem.html#method-retainEntry">retainEntry()</a> |
+and restoring it on app restart. (Hint: Add a listener to the <code>onRestarted</code> event |
+in the background page.)</p> |
+ |
+<h2 id="launch">Launch your finished Todo app</h2> |
+ |
+<p>You are done Step 6! Reload your app and add some todos. Click <b>Export to disk</b> to export your todos to a .txt file.</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/step6-completed.png" alt="The Todo app with exported todos"/> |
+</figure> |
+ |
+<h2 id="recap">For more information</h2> |
+ |
+<p>For more detailed information about some of the APIs introduced in this step, refer to:</p> |
+ |
+<ul> |
+ <li> |
+ <a href="/apps/app_storage#filesystem" title="Read 'Using the Chrome Filesystem API' in the Chrome developer docs">Using the Chrome Filesystem API</a> |
+ <a href="#export-todos" class="anchor-link-icon" title="This feature mentioned in 'Export todos'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a> |
+ <a href="#update-permissions" class="anchor-link-icon" title="This feature mentioned in 'Update permissions'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/storage#method-StorageArea-get" title="Read 'chrome.storage.local.get()' in the Chrome developer docs">chrome.storage.local.get()</a> |
+ <a href="#get-todos-as-text" class="anchor-link-icon" title="This feature mentioned in 'Get todo items as text'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/fileSystem#method-chooseEntry" title="Read 'chrome.fileSystem.chooseEntry()' in the Chrome developer docs">chrome.fileSystem.chooseEntry()</a> |
+ <a href="#choose-file" class="anchor-link-icon" title="This feature mentioned in 'Choose a file'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/fileSystem#method-getDisplayPath" title="Read 'chrome.fileSystem.getDisplayPath()' in the Chrome developer docs">chrome.fileSystem.getDisplayPath()</a> |
+ <a href="#use-fileentry" class="anchor-link-icon" title="This feature mentioned in 'Use FileEntry to write todos items to disk'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/fileSystem#method-restoreEntry" title="Read 'chrome.fileSystem.restoreEntry()' in the Chrome developer docs">chrome.fileSystem.restoreEntry()</a> |
+ <a href="#persistance" class="anchor-link-icon" title="This feature mentioned in 'Persist FileEntry objects'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/fileSystem#method-retainEntry" title="Read 'chrome.fileSystem.retainEntry()' in the Chrome developer docs">chrome.fileSystem.retainEntry()</a> |
+ <a href="#persistance" class="anchor-link-icon" title="This feature mentioned in 'Persist FileEntry objects'">↑</a> |
+ </li> |
+</ul> |
+ |
+<p>Ready to continue onto the next step? Go to <a href="app_codelab_publish.html">Step 7 - Publish your app »</a></p> |