ubuntu-touch-coreapps-reviewers team mailing list archive
-
ubuntu-touch-coreapps-reviewers team
-
Mailing list archive
-
Message #07287
[Merge] lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support into lp:ubuntu-rssreader-app
Joey Chan has proposed merging lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support into lp:ubuntu-rssreader-app.
Commit message:
Add opml support.
User now can import their own opml file into Shorts. No export function yet.
Requested reviews:
Ubuntu Shorts Developers (ubuntu-shorts-dev)
Related bugs:
Bug #1248737 in Ubuntu Shorts App: "Allow to add feeds from the local file system"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1248737
Bug #1449424 in Ubuntu Shorts App: "Allow for importing feeds with *.opml files"
https://bugs.launchpad.net/ubuntu-rssreader-app/+bug/1449424
For more details, see:
https://code.launchpad.net/~qqworini/ubuntu-rssreader-app/reboot-add-opml-support/+merge/282608
Add opml support.
User now can import their own opml file into Shorts. No export function yet.
--
Your team Ubuntu Shorts Developers is requested to review the proposed merge of lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support into lp:ubuntu-rssreader-app.
=== modified file 'shorts/po/com.ubuntu.shorts.pot'
--- shorts/po/com.ubuntu.shorts.pot 2015-12-03 16:01:09 +0000
+++ shorts/po/com.ubuntu.shorts.pot 2016-01-14 15:39:13 +0000
@@ -8,7 +8,7 @@
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-12-03 17:59+0800\n"
+"POT-Creation-Date: 2016-01-14 22:46+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -42,37 +42,59 @@
msgid "Large"
msgstr ""
+#: ../qml/content/ImportFeeds.qml:30
+msgid "Import Feeds"
+msgstr ""
+
+#: ../qml/content/ImportFeeds.qml:42 ../qml/pages/TopicManagement.qml:258
+msgid "Confirm"
+msgstr ""
+
+#: ../qml/content/ImportFeeds.qml:305
+msgid "Please select an opml file"
+msgstr ""
+
+#: ../qml/content/ImportFeeds.qml:310
+msgid "Open"
+msgstr ""
+
+#: ../qml/content/ImportFeeds.qml:293
+msgid ""
+"Attention please, before importing opml file, Shorts only support one opml "
+"structure. <br><br>"
+msgstr ""
+
#: ../qml/nongoogle/AppendNGFeedPage.qml:30 ../qml/pages/AppendFeedPage.qml:31
#: ../qml/shorts-app.qml:255 ../qml/shorts-app.qml:390
#: ../qml/shorts-app.qml:398
msgid "Add feeds"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:105
+#: ../qml/nongoogle/AppendNGFeedPage.qml:94
#: ../qml/pages/AppendFeedPage.qml:137
msgid "Type a keyword or URL"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:162
+#: ../qml/nongoogle/AppendNGFeedPage.qml:149
msgid "Feed Title:"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:170
-#: ../qml/nongoogle/AppendNGFeedPage.qml:185
+#: ../qml/nongoogle/AppendNGFeedPage.qml:157
+#: ../qml/nongoogle/AppendNGFeedPage.qml:172
msgid "No data"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:177
+#: ../qml/nongoogle/AppendNGFeedPage.qml:164
msgid "Feed Description:"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:210
+#: ../qml/nongoogle/AppendNGFeedPage.qml:197
#: ../qml/pages/AppendFeedPage.qml:243 ../qml/pages/CreateTopicPage.qml:38
#: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:481
msgid "Cancel"
msgstr ""
-#: ../qml/nongoogle/AppendNGFeedPage.qml:243
+#: ../qml/nongoogle/AppendNGFeedPage.qml:230
#: ../qml/pages/AppendFeedPage.qml:276
msgid "Next"
msgstr ""
@@ -163,19 +185,19 @@
msgid "Edit Feed"
msgstr ""
-#: ../qml/pages/EditFeedPage.qml:20
+#: ../qml/pages/EditFeedPage.qml:19
msgid "Done"
msgstr ""
-#: ../qml/pages/EditFeedPage.qml:93
+#: ../qml/pages/EditFeedPage.qml:82
msgid "Title: "
msgstr ""
-#: ../qml/pages/EditFeedPage.qml:116
+#: ../qml/pages/EditFeedPage.qml:103
msgid "URL: "
msgstr ""
-#: ../qml/pages/EditFeedPage.qml:134
+#: ../qml/pages/EditFeedPage.qml:120
msgid "Topic: "
msgstr ""
@@ -183,18 +205,31 @@
msgid "Settings"
msgstr ""
-#: ../qml/pages/PageSettings.qml:21
+#: ../qml/pages/PageSettings.qml:38
+msgid ""
+"For those who living in some special regions cannot access Google, the "
+"switch below can disable Google RSS engine, Shorts will directly gets data "
+"from RSS sources."
+msgstr ""
+
+#: ../qml/pages/PageSettings.qml:46
msgid "Use Google Search: "
msgstr ""
+#: ../qml/pages/PageSettings.qml:71
+msgid ""
+"For those users, who want to import their RSS feeds from other sources, "
+"please press the button below."
+msgstr ""
+
+#: ../qml/pages/PageSettings.qml:81
+msgid "Import OMPL"
+msgstr ""
+
#: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:219
msgid "Edit topics"
msgstr ""
-#: ../qml/pages/TopicManagement.qml:258
-msgid "Confirm"
-msgstr ""
-
#: ../qml/pages/TopicManagement.qml:274
msgid "Add Feed"
msgstr ""
=== added file 'shorts/qml/content/ContentPickerDialog.qml'
--- shorts/qml/content/ContentPickerDialog.qml 1970-01-01 00:00:00 +0000
+++ shorts/qml/content/ContentPickerDialog.qml 2016-01-14 15:39:13 +0000
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright 2016 Canonical Ltd.
+ *
+ * This file is part of shorts-app.
+ *
+ * shorts-app is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * shorts-app is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 1.3 as ListItem
+import Ubuntu.Components.Popups 1.3
+
+import Ubuntu.Content 1.1
+
+Page {
+ id: picker
+ visible: false
+
+ property var pickerParent
+
+ ContentPeerPicker {
+ visible: parent.visible
+
+ // Type of handler: Source, Destination, or Share
+ handler: ContentHandler.Source
+ // well know content type
+ contentType: ContentType.All
+
+ onPeerSelected: {
+ peer.selectionType = ContentTransfer.Single;
+ pickerParent.activeTransfer = peer.request();
+ pageStack.pop();
+ }
+
+ onCancelPressed: {
+ pageStack.pop();
+ }
+ }
+}
=== added file 'shorts/qml/content/ImportFeeds.qml'
--- shorts/qml/content/ImportFeeds.qml 1970-01-01 00:00:00 +0000
+++ shorts/qml/content/ImportFeeds.qml 2016-01-14 15:39:13 +0000
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2013, 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+import Ubuntu.Components.ListItems 1.3 as ListItem
+import Ubuntu.Components.Popups 1.3
+
+import QtQuick.XmlListModel 2.0
+import Ubuntu.Content 1.1
+
+import "../utils/databasemodule_v2.js" as DB
+//import "../."
+
+Page {
+ id: importFeeds
+ title: i18n.tr("Import Feeds")
+ flickable: null
+
+ property list<ContentItem> importItems
+ property var activeTransfer
+ property list<ContentPeer> peers
+
+
+ property var headActions: [acConfirmbutton]
+ head.actions: repeaterFeedList.opmlList == undefined ? null : headActions
+ Action {
+ id: acConfirmbutton
+ text: i18n.tr("Confirm")
+ iconName: "tick"
+ onTriggered: {
+ DB.importOPMLobject(repeaterFeedList.opmlList)
+ mainView.reloadViews()
+ pageStack.pop()
+ mainView.refresh()
+ }
+ }
+
+ Component.onCompleted: {
+// timerPush.start()
+ }
+
+ ///////////////////////////////////////////////////////////////////// content hub start here
+ ContentTransferHint {
+ anchors.fill: importFeeds
+ activeTransfer: importFeeds.activeTransfer
+ }
+
+ Connections {
+ target: importFeeds.activeTransfer
+ onStateChanged: {
+ console.log("StateChanged: " + importFeeds.activeTransfer.state);
+ if (importFeeds.activeTransfer.state === ContentTransfer.Charged) {
+ importFeeds.importItems = importFeeds.activeTransfer.items;
+ console.log("importItems[0].url: " + importFeeds.importItems[0].url);
+ opmlParser.opmlPath = importFeeds.importItems[0].url
+ }
+ }
+ }
+
+ Connections {
+ target: ContentHub
+ onImportRequested: {
+ console.log ("Import requested: " + transfer.state);
+// titleItem.text = "Imported items";
+ importFeeds.activeTransfer = transfer;
+ if (importFeeds.activeTransfer.state === ContentTransfer.Charged) {
+ importFeeds.importItems = importFeeds.activeTransfer.items;
+// opmlParser.opmlPath = importFeeds.importItems[0].url
+ }
+ }
+ }
+
+ Timer {
+ id: timerPush
+ interval: 100; running: false; repeat: false; triggeredOnStart: false
+ onTriggered: {
+// pageStack.pop()
+// pageStack.push(Qt.resolvedUrl("./ContentPickerDialog.qml"))
+ pageStack.push(contentPickerDialog)
+ }
+ }
+
+ ////////////////////////////////////// content hub
+ ContentPickerDialog{
+ id: contentPickerDialog
+ pickerParent: importFeeds
+ }
+ ///////////////////////////////////////////////////////////////////// content hub end here
+
+ ///////////////////////////////////////////////////////////////////// opml content start here
+ Flickable {
+ id: scrollArea
+ objectName: "repeaterFeedList_flickable"
+
+ clip: true
+
+ anchors {
+ fill: parent
+ bottomMargin: units.gu(1); topMargin: units.gu(1)
+ }
+
+ contentWidth: width
+ contentHeight: contentItem.childrenRect.height
+
+ Column {
+ anchors {
+ left: parent.left; right: parent.right
+ }
+ height: childrenRect.height
+ spacing: units.gu(1)
+
+ Repeater {
+ id: repeaterFeedList
+
+ model: 0
+ property var opmlList: undefined
+
+ function addOpmlObjs (opmlObjs) {
+ console.log("opmlObjs.length: ", opmlObjs.length)
+ repeaterFeedList.model = opmlObjs.length
+ opmlList = opmlObjs
+ }
+
+ Column {
+ id: columnContent
+ anchors {
+ left: parent.left; right: parent.right
+ }
+ height: liStandardRoot.isHidden ? units.gu(6.5) : childrenRect.height
+ clip:true
+
+ Behavior on height { UbuntuNumberAnimation{} }
+
+ ListItem.Empty {
+ id: liStandardRoot
+
+ property var opmlRoot: repeaterFeedList.opmlList == undefined ?
+ undefined : repeaterFeedList.opmlList[index]
+ property bool isHidden: true
+ property alias isChecked: checkboxRoot.checked
+
+ onIsCheckedChanged: {
+ if (liStandardRoot.isChecked) {
+ liStandardRoot.opmlRoot.isSelected = true
+ }
+ else {
+ liStandardRoot.opmlRoot.isSelected = false
+ }
+ }
+
+ Row {
+ anchors
+ {
+ top: parent.top; bottom: parent.bottom; left: parent.left;
+ leftMargin: units.gu(1); topMargin: units.gu(0.7); bottomMargin: units.gu(1);
+ }
+ spacing: units.gu(2)
+
+ CheckBox {
+ id: checkboxRoot
+ }
+
+ Label{
+ id: labelRootName
+ objectName: "labelTopicName"
+ anchors.verticalCenter: parent.verticalCenter
+ fontSize: "large"
+ text: liStandardRoot.opmlRoot == undefined ? "" : liStandardRoot.opmlRoot.text
+ }
+ }
+
+ Icon{
+ id: imgArrow
+ anchors
+ {
+ right: parent.right; top: parent.top; bottom: parent.bottom;
+ topMargin: units.gu(1.5); bottomMargin: units.gu(1.5); rightMargin: units.gu(2)
+ }
+ name: "go-to"
+
+ Behavior on rotation { UbuntuNumberAnimation{} }
+
+ states: [
+ State {
+ name: "expanded"
+ when: !liStandardRoot.isHidden
+
+ PropertyChanges
+ {
+ target: imgArrow
+ rotation: 90
+ }
+ }
+ ]
+ }
+
+ onClicked:
+ {
+ isHidden = !isHidden
+ }
+ }
+
+ Repeater {
+ id: repeaterFeedsChildren
+ model: liStandardRoot.opmlRoot == undefined ?
+ "" : liStandardRoot.opmlRoot.children
+
+ ListItem.Standard {
+ id: listitemFeed
+ text: opmlFeed.title
+// selected: false
+ visible: !liStandardRoot.isHidden
+// height: visible ? units.gu(6) : 0
+
+ property var opmlFeed: liStandardRoot.opmlRoot == undefined ?
+ undefined : liStandardRoot.opmlRoot.children[index]
+
+ Connections {
+ target: liStandardRoot
+ onIsCheckedChanged: {
+ if (liStandardRoot.isChecked) {
+ listitemFeed.selected = true
+ checkBoxFeed.checked = true
+ listitemFeed.opmlFeed.isSelected = true
+ }
+ else {
+ listitemFeed.selected = false
+ checkBoxFeed.checked = false
+ listitemFeed.opmlFeed.isSelected = false
+ }
+ }
+ }
+
+ CheckBox{
+ id: checkBoxFeed
+ anchors
+ {
+ right: parent.right; rightMargin: units.gu(5)
+ verticalCenter: parent.verticalCenter
+ }
+ enabled: liStandardRoot.isChecked
+// checked: parent.opmlFeed.isSelected
+
+ MouseArea { anchors.fill: parent; onClicked: {} }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ color: "#55000000"
+ visible: !liStandardRoot.isChecked
+ }
+
+ onClicked:
+ {
+// importFeeds.test()
+ if (liStandardRoot.isChecked) {
+ selected = !selected
+ // console.log("xmlUrl: ", opmlFeed.xmlUrl /*JSON.stringify(opmlRoot)*/)
+ listitemFeed.opmlFeed.isSelected = !listitemFeed.opmlFeed.isSelected
+ checkBoxFeed.checked = listitemFeed.opmlFeed.isSelected
+ }
+ }
+ }
+ }
+ }
+ } //repeaterFeedList
+ }
+
+ }
+ ///////////////////////////////////////////////////////////////////// opml content end here
+
+ ///////////////////////////////////////////////////////////////////// import tips start here
+ Label {
+ visible: repeaterFeedList.opmlList == undefined
+ anchors { left: parent.left; right: parent.right; margins: units.gu(3) }
+ anchors { top: parent.top; topMargin: units.gu(6) }
+// horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: i18n.tr("Attention please, before importing opml file, Shorts only support one opml structure. <br><br>
+For example: <br><br>Folder0<br> ---feed0<br> ---feed1<br>Folder1<br> ---feed3<br> ---feed4")
+ }
+
+ Column {
+// width: childrenRect.width
+// height: childrenRect.height
+ anchors.centerIn: parent
+ spacing: units.gu(1)
+ visible: repeaterFeedList.opmlList == undefined
+
+ Label {
+ text: i18n.tr("Please select an opml file")
+ }
+
+ Button {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: i18n.tr("Open")
+
+ onClicked: {
+ pageStack.push(contentPickerDialog)
+ }
+ }
+ }
+ ///////////////////////////////////////////////////////////////////// import tips end here
+
+ ////////////////////////////////////////////////////////////////// Opml Parser
+ OpmlParser {
+ id: opmlParser
+ opmlPath: ""
+
+ onParseFinished: {
+ repeaterFeedList.addOpmlObjs(opml)
+ }
+ }
+}
=== added file 'shorts/qml/content/OpmlParser.qml'
--- shorts/qml/content/OpmlParser.qml 1970-01-01 00:00:00 +0000
+++ shorts/qml/content/OpmlParser.qml 2016-01-14 15:39:13 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013, 2014
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.4
+import Ubuntu.Components 1.3
+//import Ubuntu.Components.ListItems 1.3 as ListItem
+//import Ubuntu.Components.Popups 1.3
+
+import QtQuick.XmlListModel 2.0
+
+import "../utils/databasemodule_v2.js" as DB
+//import "../."
+
+Item {
+ id: opmlParser
+
+ property string opmlPath: ""
+
+ signal parseFinished(var opml)
+
+ XmlListModel {
+ id: modelOpml
+ property string subTitle
+
+ property var opmlObj
+ property int currentRootsIndex: 0
+ property int allRootsCount: 0
+
+ function getAllRootObj() {
+ var rootObjs = new Array
+ // push all root objects to array
+ for (var i=0; i<modelOpml.count; i++) {
+ rootObjs.push(
+ //modelOpml.get(i)
+ {
+ "text": modelOpml.get(i).text
+ ,"title": modelOpml.get(i).title
+ ,"xmlUrl": modelOpml.get(i).xmlUrl
+ ,"htmlUrl": modelOpml.get(i).htmlUrl
+ ,"isSelected": false
+ }
+ )
+ }
+ opmlObj = rootObjs
+ // set first subTitle
+ subTitle = rootObjs[currentRootsIndex].text
+ }
+
+ function getChildObjsFromOneRoot() {
+ var rootObjs = opmlObj
+ var array = new Array
+ for (var i=0; i<modelOpml.count; i++) {
+// rootObjs[currentRootsIndex].children.push(
+ if (modelOpml.get(i).xmlUrl != "") {
+ array.push(
+ {
+ "text": modelOpml.get(i).text
+ ,"title": modelOpml.get(i).title
+ ,"xmlUrl": modelOpml.get(i).xmlUrl
+ ,"htmlUrl": modelOpml.get(i).htmlUrl
+ ,"isSelected": false
+ }
+ )
+ }
+ }
+ rootObjs[currentRootsIndex].children = array
+ opmlObj = rootObjs
+ // if not end of the roots, set next subTitle
+ if (currentRootsIndex < (allRootsCount - 1)) {
+ currentRootsIndex ++
+ subTitle = rootObjs[currentRootsIndex].text
+ }
+ // else send finish signal
+ else {
+ console.log("children objs: ", JSON.stringify(opmlObj))
+ parseFinished(opmlObj)
+ }
+ }
+
+ onStatusChanged: {
+ console.log("model status:", status)
+ if (status == XmlListModel.Ready) {
+ if (!subTitle) {
+ allRootsCount = count
+ // get all root object
+ getAllRootObj()
+ }
+ else {
+ // load children object one by one
+// console.log("children objs: ", JSON.stringify(opmlObj))
+// console.log("model count: ", count)
+ getChildObjsFromOneRoot()
+ }
+ }
+ else if (status == XmlListModel.Error) {
+ console.log("XmlListModel.Error: ", errorString())
+ }
+ }
+
+ query: subTitle ? "/opml/body/outline[@text='" + subTitle + "']/outline": "/opml/body/outline"
+ // query: "/opml/body/outline"
+ source: opmlPath
+
+ XmlRole { name: 'text'; query: '@text/string()' }
+ XmlRole { name: 'title'; query: '@title/string()' }
+ XmlRole { name: 'type'; query: '@type/string()' }
+ XmlRole { name: 'xmlUrl'; query: '@xmlUrl/string()' }
+ XmlRole { name: 'htmlUrl'; query: '@htmlUrl/string()' }
+ }
+}
=== modified file 'shorts/qml/pages/PageSettings.qml'
--- shorts/qml/pages/PageSettings.qml 2016-01-04 11:46:35 +0000
+++ shorts/qml/pages/PageSettings.qml 2016-01-14 15:39:13 +0000
@@ -23,32 +23,29 @@
Column {
anchors {
- top: parent.top; topMargin: units.gu(1)
- left: parent.left; leftMargin: units.gu(1)
- right: parent.right; rightMargin: units.gu(1)
+ top: parent.top; topMargin: units.gu(1.5)
+ left: parent.left; leftMargin: units.gu(0)
+ right: parent.right; rightMargin: units.gu(0)
}
height: childrenRect.height
- spacing: units.gu(0.8)
+// spacing: units.gu(0.8)
///////////////////////////////////////////////////////////////////// Google RSS engine switch start here
Label {
anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- text: i18n.tr("For those who living in some special regions cannot access Google, the switch below can disable Google RSS engine, Shorts will directly get data from RSS sources.")
+ text: i18n.tr("For those who living in some special regions cannot access Google, the switch below can disable Google RSS engine, Shorts will directly gets data from RSS sources.")
}
- Item { width: 10; height: 1 } // just a separator
-
- Item {
- anchors { left: parent.left; right: parent.right; }
- height: childrenRect.height
-
- Label {
- text: i18n.tr("Use Google Search: ")
- }
-
- Switch {
+ Item { width: 10; height: units.gu(1); } // just a separator
+
+ ListItem.ThinDivider{ }
+
+ ListItem.Standard {
+ text: i18n.tr("Use Google Search: ")
+ control:
+ Switch {
id: swUseGfa
anchors.right: parent.right
@@ -60,8 +57,37 @@
}
}
- ListItem.ThinDivider{ }
+ ListItem.Divider { }
///////////////////////////////////////////////////////////////////// Google RSS engine switch end here
+
+
+ ///////////////////////////////////////////////////////////////////// Import OPML functions start here
+ Item { width: 10; height: units.gu(1); } // just a separator
+
+ Label {
+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+ text: i18n.tr("For those users, who want to import their RSS feeds from other sources, please press the button below.")
+ }
+
+ Item { width: 10; height: units.gu(1); } // just a separator
+ ListItem.ThinDivider{ }
+ Item { width: 10; height: units.gu(1); } // just a separator
+
+ Button {
+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: i18n.tr("Import OMPL")
+
+ onClicked: {
+ pageStack.push(Qt.resolvedUrl("../content/ImportFeeds.qml")) ;
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////// Import OPML functions end here
+
}// Column
}
=== modified file 'shorts/qml/utils/databasemodule_v2.js'
--- shorts/qml/utils/databasemodule_v2.js 2015-07-04 08:38:18 +0000
+++ shorts/qml/utils/databasemodule_v2.js 2016-01-14 15:39:13 +0000
@@ -673,6 +673,82 @@
return dbResult;
}
+
+/* import opml file into database ~
+ * currently the opml file will be converted to a js object
+ * the following function parse the js object then insert data to database
+ */
+function importOPMLobject(opml) {
+ console.log("opml: ", JSON.stringify(opml))
+// var dbResult
+ var db = openStdDataBase()
+ db.transaction(function (tx) {
+ for (var i=0; i<opml.length; i++) {
+ if (opml[i].isSelected) {
+ // check if the topic(tag) is exist or not
+ // if true, get its id
+ // if false, insert a new topic(tag)
+ var resTag = tx.executeSql("SELECT * FROM tag WHERE name=?", [opml[i].text])
+ var tagID = -1
+ if (resTag.rows.length > 0) {
+ console.log("Database, importOPMLobject: already exist tag with name: ", opml[i].text, "ID", resTag.rows.item(0).id)
+ tagID = resTag.rows.item(0).id
+ }
+ else {
+ resTag = tx.executeSql('INSERT INTO tag (name) VALUES(?)',
+ [opml[i].text])
+ tagID = resTag.insertId
+ console.log("Database, importOPMLobject: tag INSERT ID: ", resTag.insertId)
+ }
+ // topic(tag) end, tagID is the unique id of a topic
+
+ // insert feed start
+ for (var j=0; j<opml[i].children.length; j++) {
+ if (opml[i].children[j].isSelected) {
+ /* Check uniqueness.
+ */
+ var feedID = -1
+ var feedResult = tx.executeSql("SELECT * FROM feed WHERE source=?", [opml[i].children[j].xmlUrl])
+ if (feedResult.rows.length > 0) {
+ console.log("Database, importOPMLobject: already exist feed with source: ", opml[i].children[j].xmlUrl, "ID", feedResult.rows.item(0).id)
+ feedID = feedResult.rows.item(0).id
+ }
+ else {
+ // insert feed
+ feedResult = tx.executeSql('INSERT INTO feed (title, source) VALUES(?, ?)',
+ [opml[i].children[j].title , opml[i].children[j].xmlUrl])
+ feedID = feedResult.insertId
+ console.log("Database, importOPMLobject: feed INSERT ID: ", feedID)
+ }
+
+ // insert feed_tag
+ /* Check uniqueness.
+ */
+ var feedTagResult = tx.executeSql("SELECT * FROM feed_tag WHERE feed_id=? AND tag_id=? ", [feedID, tagID])
+ if (feedTagResult.rows.length > 0) {
+ console.log("Database, importOPMLobject: already exist feed_tag with source: ", feedID, tagID)
+ }
+ else {
+ feedTagResult = tx.executeSql('INSERT INTO feed_tag (feed_id, tag_id) VALUES(?, ?)',
+ [feedID, tagID])
+ console.log("Database, importOPMLobject: feed_tag INSERT ID: ", feedTagResult.insertId, feedID, tagID)
+ }
+ }
+ }
+ }
+ else {
+ continue
+ }
+
+ }
+
+ }
+ )
+// return dbResult;
+}
+
+
+
/* operations for testing
* include clear and drop operations
* not completed yet
=== modified file 'shorts/shorts.qrc'
--- shorts/shorts.qrc 2015-12-02 17:03:41 +0000
+++ shorts/shorts.qrc 2016-01-14 15:39:13 +0000
@@ -32,10 +32,13 @@
<file>qml/shorts-app.qml</file>
<file>qml/content/SharePage.qml</file>
<file>qml/components/DarkModeShader.qml</file>
- <file>qml/nongoogle/XmlNetwork.qml</file>
- <file>qml/nongoogle/AppendNGFeedPage.qml</file>
- <file>qml/pages/PageSettings.qml</file>
- <file>qml/nongoogle/Positioner.qml</file>
+ <file>qml/nongoogle/XmlNetwork.qml</file>
+ <file>qml/nongoogle/AppendNGFeedPage.qml</file>
+ <file>qml/pages/PageSettings.qml</file>
+ <file>qml/nongoogle/Positioner.qml</file>
+ <file>qml/content/OpmlParser.qml</file>
+ <file>qml/content/ImportFeeds.qml</file>
+ <file>qml/content/ContentPickerDialog.qml</file>
</qresource>
<qresource prefix="/img">
<file>qml/icons/add.svg</file>
Follow ups