Merge lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support into lp:ubuntu-rssreader-app

Proposed by Joey Chan
Status: Merged
Approved by: Nicholas Skaggs
Approved revision: 418
Merged at revision: 417
Proposed branch: lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support
Merge into: lp:ubuntu-rssreader-app
Diff against target: 856 lines (+681/-38)
7 files modified
shorts/po/com.ubuntu.shorts.pot (+52/-17)
shorts/qml/content/ContentPickerDialog.qml (+52/-0)
shorts/qml/content/ImportFeeds.qml (+328/-0)
shorts/qml/content/OpmlParser.qml (+123/-0)
shorts/qml/pages/PageSettings.qml (+43/-17)
shorts/qml/utils/databasemodule_v2.js (+76/-0)
shorts/shorts.qrc (+7/-4)
To merge this branch: bzr merge lp:~qqworini/ubuntu-rssreader-app/reboot-add-opml-support
Reviewer Review Type Date Requested Status
Jenkins Bot continuous-integration Approve
Ubuntu Shorts Developers Pending
Review via email: mp+282608@code.launchpad.net

Commit message

Add opml support.

User now can import their own opml file into Shorts. No export function yet.

Description of the change

Add opml support.

User now can import their own opml file into Shorts. No export function yet.

To post a comment you must log in.
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Jenkins Bot (ubuntu-core-apps-jenkins-bot) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'shorts/po/com.ubuntu.shorts.pot'
2--- shorts/po/com.ubuntu.shorts.pot 2015-12-03 16:01:09 +0000
3+++ shorts/po/com.ubuntu.shorts.pot 2016-01-14 15:39:13 +0000
4@@ -8,7 +8,7 @@
5 msgstr ""
6 "Project-Id-Version: \n"
7 "Report-Msgid-Bugs-To: \n"
8-"POT-Creation-Date: 2015-12-03 17:59+0800\n"
9+"POT-Creation-Date: 2016-01-14 22:46+0800\n"
10 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Language-Team: LANGUAGE <LL@li.org>\n"
13@@ -42,37 +42,59 @@
14 msgid "Large"
15 msgstr ""
16
17+#: ../qml/content/ImportFeeds.qml:30
18+msgid "Import Feeds"
19+msgstr ""
20+
21+#: ../qml/content/ImportFeeds.qml:42 ../qml/pages/TopicManagement.qml:258
22+msgid "Confirm"
23+msgstr ""
24+
25+#: ../qml/content/ImportFeeds.qml:305
26+msgid "Please select an opml file"
27+msgstr ""
28+
29+#: ../qml/content/ImportFeeds.qml:310
30+msgid "Open"
31+msgstr ""
32+
33+#: ../qml/content/ImportFeeds.qml:293
34+msgid ""
35+"Attention please, before importing opml file, Shorts only support one opml "
36+"structure. <br><br>"
37+msgstr ""
38+
39 #: ../qml/nongoogle/AppendNGFeedPage.qml:30 ../qml/pages/AppendFeedPage.qml:31
40 #: ../qml/shorts-app.qml:255 ../qml/shorts-app.qml:390
41 #: ../qml/shorts-app.qml:398
42 msgid "Add feeds"
43 msgstr ""
44
45-#: ../qml/nongoogle/AppendNGFeedPage.qml:105
46+#: ../qml/nongoogle/AppendNGFeedPage.qml:94
47 #: ../qml/pages/AppendFeedPage.qml:137
48 msgid "Type a keyword or URL"
49 msgstr ""
50
51-#: ../qml/nongoogle/AppendNGFeedPage.qml:162
52+#: ../qml/nongoogle/AppendNGFeedPage.qml:149
53 msgid "Feed Title:"
54 msgstr ""
55
56-#: ../qml/nongoogle/AppendNGFeedPage.qml:170
57-#: ../qml/nongoogle/AppendNGFeedPage.qml:185
58+#: ../qml/nongoogle/AppendNGFeedPage.qml:157
59+#: ../qml/nongoogle/AppendNGFeedPage.qml:172
60 msgid "No data"
61 msgstr ""
62
63-#: ../qml/nongoogle/AppendNGFeedPage.qml:177
64+#: ../qml/nongoogle/AppendNGFeedPage.qml:164
65 msgid "Feed Description:"
66 msgstr ""
67
68-#: ../qml/nongoogle/AppendNGFeedPage.qml:210
69+#: ../qml/nongoogle/AppendNGFeedPage.qml:197
70 #: ../qml/pages/AppendFeedPage.qml:243 ../qml/pages/CreateTopicPage.qml:38
71 #: ../qml/pages/TopicManagement.qml:239 ../qml/shorts-app.qml:481
72 msgid "Cancel"
73 msgstr ""
74
75-#: ../qml/nongoogle/AppendNGFeedPage.qml:243
76+#: ../qml/nongoogle/AppendNGFeedPage.qml:230
77 #: ../qml/pages/AppendFeedPage.qml:276
78 msgid "Next"
79 msgstr ""
80@@ -163,19 +185,19 @@
81 msgid "Edit Feed"
82 msgstr ""
83
84-#: ../qml/pages/EditFeedPage.qml:20
85+#: ../qml/pages/EditFeedPage.qml:19
86 msgid "Done"
87 msgstr ""
88
89-#: ../qml/pages/EditFeedPage.qml:93
90+#: ../qml/pages/EditFeedPage.qml:82
91 msgid "Title: "
92 msgstr ""
93
94-#: ../qml/pages/EditFeedPage.qml:116
95+#: ../qml/pages/EditFeedPage.qml:103
96 msgid "URL: "
97 msgstr ""
98
99-#: ../qml/pages/EditFeedPage.qml:134
100+#: ../qml/pages/EditFeedPage.qml:120
101 msgid "Topic: "
102 msgstr ""
103
104@@ -183,18 +205,31 @@
105 msgid "Settings"
106 msgstr ""
107
108-#: ../qml/pages/PageSettings.qml:21
109+#: ../qml/pages/PageSettings.qml:38
110+msgid ""
111+"For those who living in some special regions cannot access Google, the "
112+"switch below can disable Google RSS engine, Shorts will directly gets data "
113+"from RSS sources."
114+msgstr ""
115+
116+#: ../qml/pages/PageSettings.qml:46
117 msgid "Use Google Search: "
118 msgstr ""
119
120+#: ../qml/pages/PageSettings.qml:71
121+msgid ""
122+"For those users, who want to import their RSS feeds from other sources, "
123+"please press the button below."
124+msgstr ""
125+
126+#: ../qml/pages/PageSettings.qml:81
127+msgid "Import OMPL"
128+msgstr ""
129+
130 #: ../qml/pages/TopicManagement.qml:13 ../qml/shorts-app.qml:219
131 msgid "Edit topics"
132 msgstr ""
133
134-#: ../qml/pages/TopicManagement.qml:258
135-msgid "Confirm"
136-msgstr ""
137-
138 #: ../qml/pages/TopicManagement.qml:274
139 msgid "Add Feed"
140 msgstr ""
141
142=== added file 'shorts/qml/content/ContentPickerDialog.qml'
143--- shorts/qml/content/ContentPickerDialog.qml 1970-01-01 00:00:00 +0000
144+++ shorts/qml/content/ContentPickerDialog.qml 2016-01-14 15:39:13 +0000
145@@ -0,0 +1,52 @@
146+/*
147+ *
148+ * Copyright 2016 Canonical Ltd.
149+ *
150+ * This file is part of shorts-app.
151+ *
152+ * shorts-app is free software; you can redistribute it and/or modify
153+ * it under the terms of the GNU General Public License as published by
154+ * the Free Software Foundation; version 3.
155+ *
156+ * shorts-app is distributed in the hope that it will be useful,
157+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
158+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
159+ * GNU General Public License for more details.
160+ *
161+ * You should have received a copy of the GNU General Public License
162+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
163+ */
164+
165+
166+import QtQuick 2.4
167+import Ubuntu.Components 1.3
168+import Ubuntu.Components.ListItems 1.3 as ListItem
169+import Ubuntu.Components.Popups 1.3
170+
171+import Ubuntu.Content 1.1
172+
173+Page {
174+ id: picker
175+ visible: false
176+
177+ property var pickerParent
178+
179+ ContentPeerPicker {
180+ visible: parent.visible
181+
182+ // Type of handler: Source, Destination, or Share
183+ handler: ContentHandler.Source
184+ // well know content type
185+ contentType: ContentType.All
186+
187+ onPeerSelected: {
188+ peer.selectionType = ContentTransfer.Single;
189+ pickerParent.activeTransfer = peer.request();
190+ pageStack.pop();
191+ }
192+
193+ onCancelPressed: {
194+ pageStack.pop();
195+ }
196+ }
197+}
198
199=== added file 'shorts/qml/content/ImportFeeds.qml'
200--- shorts/qml/content/ImportFeeds.qml 1970-01-01 00:00:00 +0000
201+++ shorts/qml/content/ImportFeeds.qml 2016-01-14 15:39:13 +0000
202@@ -0,0 +1,328 @@
203+/*
204+ * Copyright (C) 2013, 2014
205+ *
206+ * This program is free software; you can redistribute it and/or modify
207+ * it under the terms of the GNU General Public License as published by
208+ * the Free Software Foundation; version 3.
209+ *
210+ * This program is distributed in the hope that it will be useful,
211+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
212+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
213+ * GNU General Public License for more details.
214+ *
215+ * You should have received a copy of the GNU General Public License
216+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
217+ */
218+
219+import QtQuick 2.4
220+import Ubuntu.Components 1.3
221+import Ubuntu.Components.ListItems 1.3 as ListItem
222+import Ubuntu.Components.Popups 1.3
223+
224+import QtQuick.XmlListModel 2.0
225+import Ubuntu.Content 1.1
226+
227+import "../utils/databasemodule_v2.js" as DB
228+//import "../."
229+
230+Page {
231+ id: importFeeds
232+ title: i18n.tr("Import Feeds")
233+ flickable: null
234+
235+ property list<ContentItem> importItems
236+ property var activeTransfer
237+ property list<ContentPeer> peers
238+
239+
240+ property var headActions: [acConfirmbutton]
241+ head.actions: repeaterFeedList.opmlList == undefined ? null : headActions
242+ Action {
243+ id: acConfirmbutton
244+ text: i18n.tr("Confirm")
245+ iconName: "tick"
246+ onTriggered: {
247+ DB.importOPMLobject(repeaterFeedList.opmlList)
248+ mainView.reloadViews()
249+ pageStack.pop()
250+ mainView.refresh()
251+ }
252+ }
253+
254+ Component.onCompleted: {
255+// timerPush.start()
256+ }
257+
258+ ///////////////////////////////////////////////////////////////////// content hub start here
259+ ContentTransferHint {
260+ anchors.fill: importFeeds
261+ activeTransfer: importFeeds.activeTransfer
262+ }
263+
264+ Connections {
265+ target: importFeeds.activeTransfer
266+ onStateChanged: {
267+ console.log("StateChanged: " + importFeeds.activeTransfer.state);
268+ if (importFeeds.activeTransfer.state === ContentTransfer.Charged) {
269+ importFeeds.importItems = importFeeds.activeTransfer.items;
270+ console.log("importItems[0].url: " + importFeeds.importItems[0].url);
271+ opmlParser.opmlPath = importFeeds.importItems[0].url
272+ }
273+ }
274+ }
275+
276+ Connections {
277+ target: ContentHub
278+ onImportRequested: {
279+ console.log ("Import requested: " + transfer.state);
280+// titleItem.text = "Imported items";
281+ importFeeds.activeTransfer = transfer;
282+ if (importFeeds.activeTransfer.state === ContentTransfer.Charged) {
283+ importFeeds.importItems = importFeeds.activeTransfer.items;
284+// opmlParser.opmlPath = importFeeds.importItems[0].url
285+ }
286+ }
287+ }
288+
289+ Timer {
290+ id: timerPush
291+ interval: 100; running: false; repeat: false; triggeredOnStart: false
292+ onTriggered: {
293+// pageStack.pop()
294+// pageStack.push(Qt.resolvedUrl("./ContentPickerDialog.qml"))
295+ pageStack.push(contentPickerDialog)
296+ }
297+ }
298+
299+ ////////////////////////////////////// content hub
300+ ContentPickerDialog{
301+ id: contentPickerDialog
302+ pickerParent: importFeeds
303+ }
304+ ///////////////////////////////////////////////////////////////////// content hub end here
305+
306+ ///////////////////////////////////////////////////////////////////// opml content start here
307+ Flickable {
308+ id: scrollArea
309+ objectName: "repeaterFeedList_flickable"
310+
311+ clip: true
312+
313+ anchors {
314+ fill: parent
315+ bottomMargin: units.gu(1); topMargin: units.gu(1)
316+ }
317+
318+ contentWidth: width
319+ contentHeight: contentItem.childrenRect.height
320+
321+ Column {
322+ anchors {
323+ left: parent.left; right: parent.right
324+ }
325+ height: childrenRect.height
326+ spacing: units.gu(1)
327+
328+ Repeater {
329+ id: repeaterFeedList
330+
331+ model: 0
332+ property var opmlList: undefined
333+
334+ function addOpmlObjs (opmlObjs) {
335+ console.log("opmlObjs.length: ", opmlObjs.length)
336+ repeaterFeedList.model = opmlObjs.length
337+ opmlList = opmlObjs
338+ }
339+
340+ Column {
341+ id: columnContent
342+ anchors {
343+ left: parent.left; right: parent.right
344+ }
345+ height: liStandardRoot.isHidden ? units.gu(6.5) : childrenRect.height
346+ clip:true
347+
348+ Behavior on height { UbuntuNumberAnimation{} }
349+
350+ ListItem.Empty {
351+ id: liStandardRoot
352+
353+ property var opmlRoot: repeaterFeedList.opmlList == undefined ?
354+ undefined : repeaterFeedList.opmlList[index]
355+ property bool isHidden: true
356+ property alias isChecked: checkboxRoot.checked
357+
358+ onIsCheckedChanged: {
359+ if (liStandardRoot.isChecked) {
360+ liStandardRoot.opmlRoot.isSelected = true
361+ }
362+ else {
363+ liStandardRoot.opmlRoot.isSelected = false
364+ }
365+ }
366+
367+ Row {
368+ anchors
369+ {
370+ top: parent.top; bottom: parent.bottom; left: parent.left;
371+ leftMargin: units.gu(1); topMargin: units.gu(0.7); bottomMargin: units.gu(1);
372+ }
373+ spacing: units.gu(2)
374+
375+ CheckBox {
376+ id: checkboxRoot
377+ }
378+
379+ Label{
380+ id: labelRootName
381+ objectName: "labelTopicName"
382+ anchors.verticalCenter: parent.verticalCenter
383+ fontSize: "large"
384+ text: liStandardRoot.opmlRoot == undefined ? "" : liStandardRoot.opmlRoot.text
385+ }
386+ }
387+
388+ Icon{
389+ id: imgArrow
390+ anchors
391+ {
392+ right: parent.right; top: parent.top; bottom: parent.bottom;
393+ topMargin: units.gu(1.5); bottomMargin: units.gu(1.5); rightMargin: units.gu(2)
394+ }
395+ name: "go-to"
396+
397+ Behavior on rotation { UbuntuNumberAnimation{} }
398+
399+ states: [
400+ State {
401+ name: "expanded"
402+ when: !liStandardRoot.isHidden
403+
404+ PropertyChanges
405+ {
406+ target: imgArrow
407+ rotation: 90
408+ }
409+ }
410+ ]
411+ }
412+
413+ onClicked:
414+ {
415+ isHidden = !isHidden
416+ }
417+ }
418+
419+ Repeater {
420+ id: repeaterFeedsChildren
421+ model: liStandardRoot.opmlRoot == undefined ?
422+ "" : liStandardRoot.opmlRoot.children
423+
424+ ListItem.Standard {
425+ id: listitemFeed
426+ text: opmlFeed.title
427+// selected: false
428+ visible: !liStandardRoot.isHidden
429+// height: visible ? units.gu(6) : 0
430+
431+ property var opmlFeed: liStandardRoot.opmlRoot == undefined ?
432+ undefined : liStandardRoot.opmlRoot.children[index]
433+
434+ Connections {
435+ target: liStandardRoot
436+ onIsCheckedChanged: {
437+ if (liStandardRoot.isChecked) {
438+ listitemFeed.selected = true
439+ checkBoxFeed.checked = true
440+ listitemFeed.opmlFeed.isSelected = true
441+ }
442+ else {
443+ listitemFeed.selected = false
444+ checkBoxFeed.checked = false
445+ listitemFeed.opmlFeed.isSelected = false
446+ }
447+ }
448+ }
449+
450+ CheckBox{
451+ id: checkBoxFeed
452+ anchors
453+ {
454+ right: parent.right; rightMargin: units.gu(5)
455+ verticalCenter: parent.verticalCenter
456+ }
457+ enabled: liStandardRoot.isChecked
458+// checked: parent.opmlFeed.isSelected
459+
460+ MouseArea { anchors.fill: parent; onClicked: {} }
461+ }
462+
463+ Rectangle {
464+ anchors.fill: parent
465+ color: "#55000000"
466+ visible: !liStandardRoot.isChecked
467+ }
468+
469+ onClicked:
470+ {
471+// importFeeds.test()
472+ if (liStandardRoot.isChecked) {
473+ selected = !selected
474+ // console.log("xmlUrl: ", opmlFeed.xmlUrl /*JSON.stringify(opmlRoot)*/)
475+ listitemFeed.opmlFeed.isSelected = !listitemFeed.opmlFeed.isSelected
476+ checkBoxFeed.checked = listitemFeed.opmlFeed.isSelected
477+ }
478+ }
479+ }
480+ }
481+ }
482+ } //repeaterFeedList
483+ }
484+
485+ }
486+ ///////////////////////////////////////////////////////////////////// opml content end here
487+
488+ ///////////////////////////////////////////////////////////////////// import tips start here
489+ Label {
490+ visible: repeaterFeedList.opmlList == undefined
491+ anchors { left: parent.left; right: parent.right; margins: units.gu(3) }
492+ anchors { top: parent.top; topMargin: units.gu(6) }
493+// horizontalAlignment: Text.AlignHCenter
494+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
495+ text: i18n.tr("Attention please, before importing opml file, Shorts only support one opml structure. <br><br>
496+For example: <br><br>Folder0<br> ---feed0<br> ---feed1<br>Folder1<br> ---feed3<br> ---feed4")
497+ }
498+
499+ Column {
500+// width: childrenRect.width
501+// height: childrenRect.height
502+ anchors.centerIn: parent
503+ spacing: units.gu(1)
504+ visible: repeaterFeedList.opmlList == undefined
505+
506+ Label {
507+ text: i18n.tr("Please select an opml file")
508+ }
509+
510+ Button {
511+ anchors.horizontalCenter: parent.horizontalCenter
512+ text: i18n.tr("Open")
513+
514+ onClicked: {
515+ pageStack.push(contentPickerDialog)
516+ }
517+ }
518+ }
519+ ///////////////////////////////////////////////////////////////////// import tips end here
520+
521+ ////////////////////////////////////////////////////////////////// Opml Parser
522+ OpmlParser {
523+ id: opmlParser
524+ opmlPath: ""
525+
526+ onParseFinished: {
527+ repeaterFeedList.addOpmlObjs(opml)
528+ }
529+ }
530+}
531
532=== added file 'shorts/qml/content/OpmlParser.qml'
533--- shorts/qml/content/OpmlParser.qml 1970-01-01 00:00:00 +0000
534+++ shorts/qml/content/OpmlParser.qml 2016-01-14 15:39:13 +0000
535@@ -0,0 +1,123 @@
536+/*
537+ * Copyright (C) 2013, 2014
538+ *
539+ * This program is free software; you can redistribute it and/or modify
540+ * it under the terms of the GNU General Public License as published by
541+ * the Free Software Foundation; version 3.
542+ *
543+ * This program is distributed in the hope that it will be useful,
544+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
545+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
546+ * GNU General Public License for more details.
547+ *
548+ * You should have received a copy of the GNU General Public License
549+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
550+ */
551+
552+import QtQuick 2.4
553+import Ubuntu.Components 1.3
554+//import Ubuntu.Components.ListItems 1.3 as ListItem
555+//import Ubuntu.Components.Popups 1.3
556+
557+import QtQuick.XmlListModel 2.0
558+
559+import "../utils/databasemodule_v2.js" as DB
560+//import "../."
561+
562+Item {
563+ id: opmlParser
564+
565+ property string opmlPath: ""
566+
567+ signal parseFinished(var opml)
568+
569+ XmlListModel {
570+ id: modelOpml
571+ property string subTitle
572+
573+ property var opmlObj
574+ property int currentRootsIndex: 0
575+ property int allRootsCount: 0
576+
577+ function getAllRootObj() {
578+ var rootObjs = new Array
579+ // push all root objects to array
580+ for (var i=0; i<modelOpml.count; i++) {
581+ rootObjs.push(
582+ //modelOpml.get(i)
583+ {
584+ "text": modelOpml.get(i).text
585+ ,"title": modelOpml.get(i).title
586+ ,"xmlUrl": modelOpml.get(i).xmlUrl
587+ ,"htmlUrl": modelOpml.get(i).htmlUrl
588+ ,"isSelected": false
589+ }
590+ )
591+ }
592+ opmlObj = rootObjs
593+ // set first subTitle
594+ subTitle = rootObjs[currentRootsIndex].text
595+ }
596+
597+ function getChildObjsFromOneRoot() {
598+ var rootObjs = opmlObj
599+ var array = new Array
600+ for (var i=0; i<modelOpml.count; i++) {
601+// rootObjs[currentRootsIndex].children.push(
602+ if (modelOpml.get(i).xmlUrl != "") {
603+ array.push(
604+ {
605+ "text": modelOpml.get(i).text
606+ ,"title": modelOpml.get(i).title
607+ ,"xmlUrl": modelOpml.get(i).xmlUrl
608+ ,"htmlUrl": modelOpml.get(i).htmlUrl
609+ ,"isSelected": false
610+ }
611+ )
612+ }
613+ }
614+ rootObjs[currentRootsIndex].children = array
615+ opmlObj = rootObjs
616+ // if not end of the roots, set next subTitle
617+ if (currentRootsIndex < (allRootsCount - 1)) {
618+ currentRootsIndex ++
619+ subTitle = rootObjs[currentRootsIndex].text
620+ }
621+ // else send finish signal
622+ else {
623+ console.log("children objs: ", JSON.stringify(opmlObj))
624+ parseFinished(opmlObj)
625+ }
626+ }
627+
628+ onStatusChanged: {
629+ console.log("model status:", status)
630+ if (status == XmlListModel.Ready) {
631+ if (!subTitle) {
632+ allRootsCount = count
633+ // get all root object
634+ getAllRootObj()
635+ }
636+ else {
637+ // load children object one by one
638+// console.log("children objs: ", JSON.stringify(opmlObj))
639+// console.log("model count: ", count)
640+ getChildObjsFromOneRoot()
641+ }
642+ }
643+ else if (status == XmlListModel.Error) {
644+ console.log("XmlListModel.Error: ", errorString())
645+ }
646+ }
647+
648+ query: subTitle ? "/opml/body/outline[@text='" + subTitle + "']/outline": "/opml/body/outline"
649+ // query: "/opml/body/outline"
650+ source: opmlPath
651+
652+ XmlRole { name: 'text'; query: '@text/string()' }
653+ XmlRole { name: 'title'; query: '@title/string()' }
654+ XmlRole { name: 'type'; query: '@type/string()' }
655+ XmlRole { name: 'xmlUrl'; query: '@xmlUrl/string()' }
656+ XmlRole { name: 'htmlUrl'; query: '@htmlUrl/string()' }
657+ }
658+}
659
660=== modified file 'shorts/qml/pages/PageSettings.qml'
661--- shorts/qml/pages/PageSettings.qml 2016-01-04 11:46:35 +0000
662+++ shorts/qml/pages/PageSettings.qml 2016-01-14 15:39:13 +0000
663@@ -23,32 +23,29 @@
664
665 Column {
666 anchors {
667- top: parent.top; topMargin: units.gu(1)
668- left: parent.left; leftMargin: units.gu(1)
669- right: parent.right; rightMargin: units.gu(1)
670+ top: parent.top; topMargin: units.gu(1.5)
671+ left: parent.left; leftMargin: units.gu(0)
672+ right: parent.right; rightMargin: units.gu(0)
673 }
674 height: childrenRect.height
675- spacing: units.gu(0.8)
676+// spacing: units.gu(0.8)
677
678 ///////////////////////////////////////////////////////////////////// Google RSS engine switch start here
679 Label {
680 anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
681 horizontalAlignment: Text.AlignHCenter
682 wrapMode: Text.WrapAtWordBoundaryOrAnywhere
683- 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.")
684+ 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.")
685 }
686
687- Item { width: 10; height: 1 } // just a separator
688-
689- Item {
690- anchors { left: parent.left; right: parent.right; }
691- height: childrenRect.height
692-
693- Label {
694- text: i18n.tr("Use Google Search: ")
695- }
696-
697- Switch {
698+ Item { width: 10; height: units.gu(1); } // just a separator
699+
700+ ListItem.ThinDivider{ }
701+
702+ ListItem.Standard {
703+ text: i18n.tr("Use Google Search: ")
704+ control:
705+ Switch {
706 id: swUseGfa
707 anchors.right: parent.right
708
709@@ -60,8 +57,37 @@
710 }
711 }
712
713- ListItem.ThinDivider{ }
714+ ListItem.Divider { }
715 ///////////////////////////////////////////////////////////////////// Google RSS engine switch end here
716+
717+
718+ ///////////////////////////////////////////////////////////////////// Import OPML functions start here
719+ Item { width: 10; height: units.gu(1); } // just a separator
720+
721+ Label {
722+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
723+ horizontalAlignment: Text.AlignHCenter
724+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
725+ text: i18n.tr("For those users, who want to import their RSS feeds from other sources, please press the button below.")
726+ }
727+
728+ Item { width: 10; height: units.gu(1); } // just a separator
729+ ListItem.ThinDivider{ }
730+ Item { width: 10; height: units.gu(1); } // just a separator
731+
732+ Button {
733+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
734+ anchors.horizontalCenter: parent.horizontalCenter
735+ text: i18n.tr("Import OMPL")
736+
737+ onClicked: {
738+ pageStack.push(Qt.resolvedUrl("../content/ImportFeeds.qml")) ;
739+ }
740+ }
741+
742+
743+ ///////////////////////////////////////////////////////////////////// Import OPML functions end here
744+
745 }// Column
746
747 }
748
749=== modified file 'shorts/qml/utils/databasemodule_v2.js'
750--- shorts/qml/utils/databasemodule_v2.js 2015-07-04 08:38:18 +0000
751+++ shorts/qml/utils/databasemodule_v2.js 2016-01-14 15:39:13 +0000
752@@ -673,6 +673,82 @@
753 return dbResult;
754 }
755
756+
757+/* import opml file into database ~
758+ * currently the opml file will be converted to a js object
759+ * the following function parse the js object then insert data to database
760+ */
761+function importOPMLobject(opml) {
762+ console.log("opml: ", JSON.stringify(opml))
763+// var dbResult
764+ var db = openStdDataBase()
765+ db.transaction(function (tx) {
766+ for (var i=0; i<opml.length; i++) {
767+ if (opml[i].isSelected) {
768+ // check if the topic(tag) is exist or not
769+ // if true, get its id
770+ // if false, insert a new topic(tag)
771+ var resTag = tx.executeSql("SELECT * FROM tag WHERE name=?", [opml[i].text])
772+ var tagID = -1
773+ if (resTag.rows.length > 0) {
774+ console.log("Database, importOPMLobject: already exist tag with name: ", opml[i].text, "ID", resTag.rows.item(0).id)
775+ tagID = resTag.rows.item(0).id
776+ }
777+ else {
778+ resTag = tx.executeSql('INSERT INTO tag (name) VALUES(?)',
779+ [opml[i].text])
780+ tagID = resTag.insertId
781+ console.log("Database, importOPMLobject: tag INSERT ID: ", resTag.insertId)
782+ }
783+ // topic(tag) end, tagID is the unique id of a topic
784+
785+ // insert feed start
786+ for (var j=0; j<opml[i].children.length; j++) {
787+ if (opml[i].children[j].isSelected) {
788+ /* Check uniqueness.
789+ */
790+ var feedID = -1
791+ var feedResult = tx.executeSql("SELECT * FROM feed WHERE source=?", [opml[i].children[j].xmlUrl])
792+ if (feedResult.rows.length > 0) {
793+ console.log("Database, importOPMLobject: already exist feed with source: ", opml[i].children[j].xmlUrl, "ID", feedResult.rows.item(0).id)
794+ feedID = feedResult.rows.item(0).id
795+ }
796+ else {
797+ // insert feed
798+ feedResult = tx.executeSql('INSERT INTO feed (title, source) VALUES(?, ?)',
799+ [opml[i].children[j].title , opml[i].children[j].xmlUrl])
800+ feedID = feedResult.insertId
801+ console.log("Database, importOPMLobject: feed INSERT ID: ", feedID)
802+ }
803+
804+ // insert feed_tag
805+ /* Check uniqueness.
806+ */
807+ var feedTagResult = tx.executeSql("SELECT * FROM feed_tag WHERE feed_id=? AND tag_id=? ", [feedID, tagID])
808+ if (feedTagResult.rows.length > 0) {
809+ console.log("Database, importOPMLobject: already exist feed_tag with source: ", feedID, tagID)
810+ }
811+ else {
812+ feedTagResult = tx.executeSql('INSERT INTO feed_tag (feed_id, tag_id) VALUES(?, ?)',
813+ [feedID, tagID])
814+ console.log("Database, importOPMLobject: feed_tag INSERT ID: ", feedTagResult.insertId, feedID, tagID)
815+ }
816+ }
817+ }
818+ }
819+ else {
820+ continue
821+ }
822+
823+ }
824+
825+ }
826+ )
827+// return dbResult;
828+}
829+
830+
831+
832 /* operations for testing
833 * include clear and drop operations
834 * not completed yet
835
836=== modified file 'shorts/shorts.qrc'
837--- shorts/shorts.qrc 2015-12-02 17:03:41 +0000
838+++ shorts/shorts.qrc 2016-01-14 15:39:13 +0000
839@@ -32,10 +32,13 @@
840 <file>qml/shorts-app.qml</file>
841 <file>qml/content/SharePage.qml</file>
842 <file>qml/components/DarkModeShader.qml</file>
843- <file>qml/nongoogle/XmlNetwork.qml</file>
844- <file>qml/nongoogle/AppendNGFeedPage.qml</file>
845- <file>qml/pages/PageSettings.qml</file>
846- <file>qml/nongoogle/Positioner.qml</file>
847+ <file>qml/nongoogle/XmlNetwork.qml</file>
848+ <file>qml/nongoogle/AppendNGFeedPage.qml</file>
849+ <file>qml/pages/PageSettings.qml</file>
850+ <file>qml/nongoogle/Positioner.qml</file>
851+ <file>qml/content/OpmlParser.qml</file>
852+ <file>qml/content/ImportFeeds.qml</file>
853+ <file>qml/content/ContentPickerDialog.qml</file>
854 </qresource>
855 <qresource prefix="/img">
856 <file>qml/icons/add.svg</file>

Subscribers

People subscribed via source and target branches