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