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..d82037ab2b94e77a4fb13002c17af0da6484a70a |
--- /dev/null |
+++ b/chrome/common/extensions/docs/templates/articles/app_codelab_filesystem.html |
@@ -0,0 +1,270 @@ |
+<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>In this step, we will add an export button to the app. When clicked, the current |
+todo items will be saved to a text file selected by the user. If the file |
+exists, it will be replaced. Otherwise, a new file will be 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>For our purposes, we need read and write access. In <strong><em>manifest.json</em></strong>, 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 <strong><em>index.html</em></strong>, add an <b>Export to disk</b> button and |
+a <code>div</code> where we will show 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 we will create next:</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 <strong><em>export.js</em></strong> using the code below. Save it in the <strong><em>js</em></strong> |
+ 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 we already have a saved <code>FileEntry</code>, we'll use that instead when we call <code>exportToFileEntry()</code>. |
+File references will exist for the lifetime of the object representing the <code>FileEntry</code>. |
+In our example, we tied <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> is used to get a displayable file path that we'll output 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. This means |
+that your app will need to ask the user to choose a file every time the app is |
+launched. However, <a href="/apps/fileSystem#method-restoreEntry">restoreEntry()</a> is an |
+option to |
+restore a <code>FileEntry</code> if your app was forced to restart due to a runtime crash or update.</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">Recap APIs referenced in this step</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> |