Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Export bookmarks #6652

Merged
merged 4 commits into from
Feb 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions app/browser/bookmarksExporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

const path = require('path')
const moment = require('moment')
const fs = require('fs')
const electron = require('electron')
const dialog = electron.dialog
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const getSetting = require('../../js/settings').getSetting
const settings = require('../../js/constants/settings')
const siteTags = require('../../js/constants/siteTags')
const siteUtil = require('../../js/state/siteUtil')
const isWindows = process.platform === 'win32'
const indentLength = 2
const indentType = ' '

function showDialog (sites) {
const focusedWindow = BrowserWindow.getFocusedWindow()
const fileName = moment().format('DD_MM_YYYY') + '.html'
const defaultPath = path.join(getSetting(settings.DEFAULT_DOWNLOAD_SAVE_PATH) || app.getPath('downloads'), fileName)
let personal = []
let other = []

dialog.showSaveDialog(focusedWindow, {
defaultPath: defaultPath,
filters: [{
name: 'HTML',
extensions: ['html']
}]
}, (fileName) => {
if (fileName) {
personal = createBookmarkArray(sites)
other = createBookmarkArray(sites, -1, false)
fs.writeFileSync(fileName, createBookmarkHTML(personal, other))
}
})
}

function createBookmarkArray (sites, parentFolderId, first = true, depth = 1) {
const filteredBookmarks = parentFolderId
? sites.filter((site) => site.get('parentFolderId') === parentFolderId)
: sites.filter((site) => !site.get('parentFolderId'))
let payload = []
let title
let indentFirst = indentType.repeat(depth * indentLength)
let indentNext = (!first) ? indentFirst : indentType.repeat((depth + 1) * indentLength)

if (first) payload.push(`${indentFirst}<DL><p>`)

filteredBookmarks.forEach((site) => {
if (site.get('tags').includes(siteTags.BOOKMARK) && site.get('location')) {
title = site.get('customTitle') || site.get('title') || site.get('location')
payload.push(`${indentNext}<DT><A HREF="${site.get('location')}">${title}</A>`)
} else if (siteUtil.isFolder(site)) {
const folderId = site.get('folderId')
const submenuItems = sites.filter((bookmark) => bookmark.get('parentFolderId') === folderId)

if (submenuItems.count() > 0) {
title = site.get('customTitle') || site.get('title')
payload.push(`${indentNext}<DT><H3>${title}</H3>`)
payload = payload.concat(createBookmarkArray(sites, folderId, true, (depth + 1)))
}
}
})

if (first) payload.push(`${indentFirst}</DL><p>`)

return payload
}

function createBookmarkHTML (personal, other) {
const breakTag = (isWindows) ? '\r\n' : '\n'
const title = 'Bookmarks'

return `<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file. It will be read and overwritten. DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>${title}</TITLE>
<H1>${title}</H1>
<DL><p>
<DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Bar</H3>
${personal.join(breakTag)}
${other.join(breakTag)}
</DL><p>`
}

module.exports = {
createBookmarkArray,
createBookmarkHTML,
showDialog
}
3 changes: 2 additions & 1 deletion app/browser/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,8 @@ const createBookmarksSubmenu = () => {
CommonMenu.bookmarksManagerMenuItem(),
CommonMenu.bookmarksToolbarMenuItem(),
CommonMenu.separatorMenuItem,
CommonMenu.importBrowserDataMenuItem()
CommonMenu.importBrowserDataMenuItem(),
CommonMenu.exportBookmarksMenuItem()
]

const bookmarks = menuUtil.createBookmarkTemplateItems(appStore.getState().get('sites'))
Expand Down
13 changes: 13 additions & 0 deletions app/common/commonMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,19 @@ module.exports.importBrowserDataMenuItem = () => {
}
}

module.exports.exportBookmarksMenuItem = () => {
return {
label: locale.translation('exportBookmarks'),
click: function (item, focusedWindow) {
if (process.type === 'browser') {
process.emit(messages.EXPORT_BOOKMARKS)
} else {
electron.ipcRenderer.send(messages.EXPORT_BOOKMARKS)
}
}
}
}

module.exports.submitFeedbackMenuItem = () => {
return {
label: locale.translation('submitFeedback'),
Expand Down
1 change: 1 addition & 0 deletions app/extensions/brave/locales/en-US/bookmarks.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ importBrowserData.title=Import Browser Data
allFolders=All Folders
addBookmarkFolder.title=Add Folder
addBookmark.title=Add Bookmark
exportBookmarks.title=Export bookmarks
1 change: 1 addition & 0 deletions app/extensions/brave/locales/en-US/menu.properties
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ preferences=Preferences…
settings=Settings…
bookmarksManager=Bookmarks Manager…
importBrowserData=Import Browser Data…
exportBookmarks=Export Bookmarks…
submitFeedback=Submit Feedback…
bookmarksToolbar=Bookmarks Toolbar
bravery=Shields
Expand Down
9 changes: 9 additions & 0 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const contentSettings = require('../js/state/contentSettings')
const privacy = require('../js/state/privacy')
const async = require('async')
const settings = require('../js/constants/settings')
const BookmarksExporter = require('./browser/bookmarksExporter')

app.commandLine.appendSwitch('enable-features', 'BlockSmallPluginContent,PreferHtmlOverPlugins')

Expand Down Expand Up @@ -725,6 +726,10 @@ app.on('ready', () => {
Importer.init()
})

ipcMain.on(messages.EXPORT_BOOKMARKS, () => {
BookmarksExporter.showDialog(AppStore.getState().get('sites'))
})

// This loads package.json into an object
// TODO: Seems like this can be done with app.getVersion() insteand?
PackageLoader.load((err, pack) => {
Expand All @@ -747,6 +752,10 @@ app.on('ready', () => {
process.on(messages.IMPORT_BROWSER_DATA_NOW, () => {
Importer.init()
})

process.on(messages.EXPORT_BOOKMARKS, () => {
BookmarksExporter.showDialog(AppStore.getState().get('sites'))
})
})
ready = true
})
Expand Down
1 change: 1 addition & 0 deletions app/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ var rendererIdentifiers = function () {
'settings',
'bookmarksManager',
'importBrowserData',
'exportBookmarks',
'submitFeedback',
'bookmarksToolbar',
'bravery',
Expand Down
2 changes: 1 addition & 1 deletion img/toolbar/add_BM_folder_btn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion img/toolbar/add_bookmark_btn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions img/toolbar/bookmarks_export.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions img/toolbar/bookmarks_import.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions js/about/aboutActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ const aboutActions = {
ipc.send(messages.IMPORT_BROWSER_DATA_NOW)
},

/**
* Export bookmarks
*/
exportBookmarks: function () {
ipc.send(messages.EXPORT_BOOKMARKS)
},

createWallet: function () {
ipc.send(messages.LEDGER_CREATE_WALLET)
},
Expand Down
7 changes: 6 additions & 1 deletion js/about/bookmarks.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ class AboutBookmarks extends React.Component {
this.onChangeSearch = this.onChangeSearch.bind(this)
this.onClearSearchText = this.onClearSearchText.bind(this)
this.importBrowserData = this.importBrowserData.bind(this)
this.exportBookmarks = this.exportBookmarks.bind(this)
this.addBookmarkFolder = this.addBookmarkFolder.bind(this)
this.onClick = this.onClick.bind(this)
this.clearSelection = this.clearSelection.bind(this)
Expand Down Expand Up @@ -428,6 +429,9 @@ class AboutBookmarks extends React.Component {
importBrowserData () {
aboutActions.importBrowserDataNow()
}
exportBookmarks () {
aboutActions.exportBookmarks()
}
addBookmarkFolder () {
const newFolder = Immutable.fromJS({
parentFolderId: this.state.selectedFolderId,
Expand All @@ -447,6 +451,8 @@ class AboutBookmarks extends React.Component {
<div data-l10n-id='bookmarkManager' className='sectionTitle' />
<div className='headerActions'>
<div className='searchWrapper'>
<span data-l10n-id='importBrowserData' className='importBrowserData' onClick={this.importBrowserData} />
<span data-l10n-id='exportBookmarks' className='exportBookmarks' onClick={this.exportBookmarks} />
<input type='text' className='searchInput' ref='bookmarkSearch' id='bookmarkSearch' value={this.state.search} onChange={this.onChangeSearch} data-l10n-id='bookmarkSearch' />
{
this.state.search
Expand All @@ -461,7 +467,6 @@ class AboutBookmarks extends React.Component {
<div className='folderView'>
<div className='columnHeader'>
<span data-l10n-id='folders' />
<span data-l10n-id='importBrowserData' className='fa fa-download importBrowserData' onClick={this.importBrowserData} />
<span data-l10n-id='addBookmarkFolder' className='addBookmarkFolder' onClick={this.addBookmarkFolder} />
</div>
<BookmarkFolderList
Expand Down
1 change: 1 addition & 0 deletions js/constants/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ const messages = {
ABOUT_COMPONENT_INITIALIZED: _,
CLEAR_BROWSING_DATA_NOW: _,
IMPORT_BROWSER_DATA_NOW: _,
EXPORT_BOOKMARKS: _,
IMPORTER_LIST: _,
// Autofill
AUTOFILL_SET_ADDRESS: _,
Expand Down
57 changes: 35 additions & 22 deletions less/about/bookmarks.less
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,6 @@
padding-left: 10px;
-webkit-user-select: none;
cursor: default;

.importBrowserData {
color: @buttonColor;
float: right;
font-size: 20px;
margin: 0;

&:hover {
color: #000;
}
}
}

> .bookmarkFolderList {
Expand Down Expand Up @@ -148,27 +137,51 @@
padding-left: 12px;
}

.addBookmarkFolder {
.addBookmarkFolder,
.addBookmark {
background-color: @buttonColor;
-webkit-mask-image: url('../../img/toolbar/add_BM_folder_btn.svg');
-webkit-mask-repeat: no-repeat;
margin-right: 7px;
width: 24px;
height: 26px;
float: right;
margin-right: 7px;
}
.addBookmarkFolder:hover {
background-color: #000;

.addBookmarkFolder {
-webkit-mask-image: url('../../img/toolbar/add_BM_folder_btn.svg');
}

.addBookmark {
background-color: @buttonColor;
-webkit-mask-image: url('../../img/toolbar/add_bookmark_btn.svg');
-webkit-mask-repeat: no-repeat;
margin-right: 7px;
width: 20px;
height: 20px;
float: right;
}
.addBookmark:hover {
background-color: #000;

.addBookmark,
.addBookmarkFolder,
.importBrowserData,
.exportBookmarks {
&:hover {
background-color: #000;
}
}

.exportBookmarks,
.importBrowserData {
background-color: @buttonColor;
-webkit-mask-repeat: no-repeat;
width: 43px;
height: 22px;
display: inline-block;
vertical-align: top;
margin: 7px 10px 5px 0;
}


.importBrowserData {
-webkit-mask-image: url('../../img/toolbar/bookmarks_import.svg');
}

.exportBookmarks {
-webkit-mask-image: url('../../img/toolbar/bookmarks_export.svg');
}
31 changes: 31 additions & 0 deletions test/fixtures/bookmarkExport.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file. It will be read and overwritten. DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
<DT><H3 PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Bar</H3>
<DL><p>
<DT><A HREF="https://brave.com/1">Website 1</A>
<DT><H3>folder 1</H3>
<DL><p>
<DT><A HREF="https://brave.com/2">Website 2</A>
<DT><H3>folder 2</H3>
<DL><p>
<DT><A HREF="https://brave.com/3">Website 3</A>
<DT><A HREF="https://brave.com/4">Website 4</A>
</DL><p>
<DT><H3>folder 3</H3>
<DL><p>
<DT><A HREF="https://brave.com/5">Website 5</A>
</DL><p>
</DL><p>
<DT><A HREF="https://brave.com/6">Website 6</A>
</DL><p>
<DT><A HREF="https://brave.com/7">Website 7</A>
<DT><H3>folder 4</H3>
<DL><p>
<DT><A HREF="https://brave.com/8">Website 8</A>
<DT><A HREF="https://brave.com/9">Website 9</A>
</DL><p>
</DL><p>
Loading