summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernhard Posselt <nukeawhale@gmail.com>2013-04-13 17:28:07 +0200
committerBernhard Posselt <nukeawhale@gmail.com>2013-04-13 17:28:38 +0200
commit38e4b6d7e973bc99811139066a7a047b4bd76a4c (patch)
treefb88f63c0a69cc527956715333dddd20d3b94f0b
parent8df86d1991e25dfaef8623e2f4945c67dbc5f318 (diff)
implement opml import, fix #23
-rw-r--r--js/app/controllers/settingscontroller.coffee12
-rw-r--r--js/app/services/bl/folderbl.coffee30
-rw-r--r--js/app/services/opmlparser.coffee4
-rw-r--r--js/app/services/services.coffee5
-rw-r--r--js/public/app.js75
-rw-r--r--js/tests/controllers/settingscontrollerSpec.coffee22
-rw-r--r--js/tests/services/bl/folderblSpec.coffee210
-rw-r--r--templates/part.settings.php7
8 files changed, 333 insertions, 32 deletions
diff --git a/js/app/controllers/settingscontroller.coffee b/js/app/controllers/settingscontroller.coffee
index 07cf7360a..2df604c42 100644
--- a/js/app/controllers/settingscontroller.coffee
+++ b/js/app/controllers/settingscontroller.coffee
@@ -22,11 +22,15 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
angular.module('News').controller 'SettingsController',
-['$scope', 'FeedBl', ($scope, FeedBl) ->
-
- $scope.import = (fileContent) ->
- console.log fileContent
+['$scope', 'FeedBl', 'FolderBl', ($scope, FeedBl, FolderBl) ->
$scope.feedBl = FeedBl
+ $scope.import = (fileContent) =>
+ $scope.error = false
+ try
+ FolderBl.import(fileContent)
+ catch error
+ $scope.error = true
+
] \ No newline at end of file
diff --git a/js/app/services/bl/folderbl.coffee b/js/app/services/bl/folderbl.coffee
index ddb1f88c4..a74ecc42f 100644
--- a/js/app/services/bl/folderbl.coffee
+++ b/js/app/services/bl/folderbl.coffee
@@ -23,14 +23,14 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
angular.module('News').factory 'FolderBl',
['_Bl', 'FolderModel', 'FeedBl', 'Persistence', 'FeedType', 'ActiveFeed',
-'ItemModel', 'ShowAll', '_ExistsError',
+'ItemModel', 'ShowAll', '_ExistsError', 'OPMLParser',
(_Bl, FolderModel, FeedBl, Persistence, FeedType, ActiveFeed,
-ItemModel, ShowAll, _ExistsError)->
+ItemModel, ShowAll, _ExistsError, OPMLParser)->
class FolderBl extends _Bl
constructor: (@_folderModel, @_feedBl, @_showAll, activeFeed,
- persistence, @_feedType, itemModel) ->
+ persistence, @_feedType, itemModel, @_opmlParser) ->
super(activeFeed, persistence, itemModel, @_feedType.Folder)
@@ -123,7 +123,29 @@ ItemModel, ShowAll, _ExistsError)->
@_folderModel.removeByName(folderName)
+ import: (xml) ->
+ opml = @_opmlParser.parseXML(xml)
+
+ @_importElement(opml, 0)
+
+
+ _importElement: (opml, parentFolderId) ->
+ for item in opml.getItems()
+ if item.isFolder()
+ try
+ @create item.getName(), (data) =>
+ @_importElement(item, data.folders[0].id)
+ catch error
+ if error instanceof _ExistsError
+ folder = @_folderModel.getByName(item.getName())
+ @_importElement(item, folder.id)
+ else
+ try
+ @_feedBl.create(item.getUrl(), parentFolderId)
+ catch error
+
+
return new FolderBl(FolderModel, FeedBl, ShowAll, ActiveFeed, Persistence,
- FeedType, ItemModel)
+ FeedType, ItemModel, OPMLParser)
] \ No newline at end of file
diff --git a/js/app/services/opmlparser.coffee b/js/app/services/opmlparser.coffee
index 9586cbaf2..904ff2954 100644
--- a/js/app/services/opmlparser.coffee
+++ b/js/app/services/opmlparser.coffee
@@ -20,7 +20,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
###
-angular.module('News').factory '_OPMLParser', ->
+angular.module('News').factory 'OPMLParser', ->
class Feed
@@ -75,4 +75,4 @@ angular.module('News').factory '_OPMLParser', ->
@_recursivelyParse($outline, folder)
- return OPMLParser
+ return new OPMLParser()
diff --git a/js/app/services/services.coffee b/js/app/services/services.coffee
index 837e2004c..83e76e20e 100644
--- a/js/app/services/services.coffee
+++ b/js/app/services/services.coffee
@@ -99,8 +99,3 @@ FolderModel, FeedModel) ->
]
-# other classes
-angular.module('News').factory 'OPMLParser', ['_OPMLParser', (_OPMLParser) ->
- return new _OPMLParser()
-]
-
diff --git a/js/public/app.js b/js/public/app.js
index 741188cb4..26702cd11 100644
--- a/js/public/app.js
+++ b/js/public/app.js
@@ -455,11 +455,21 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
(function() {
angular.module('News').controller('SettingsController', [
- '$scope', 'FeedBl', function($scope, FeedBl) {
- $scope["import"] = function(fileContent) {
- return console.log(fileContent);
+ '$scope', 'FeedBl', 'FolderBl', function($scope, FeedBl, FolderBl) {
+ var _this = this;
+
+ $scope.feedBl = FeedBl;
+ return $scope["import"] = function(fileContent) {
+ var error;
+
+ $scope.error = false;
+ try {
+ return FolderBl["import"](fileContent);
+ } catch (_error) {
+ error = _error;
+ return $scope.error = true;
+ }
};
- return $scope.feedBl = FeedBl;
}
]);
@@ -839,17 +849,18 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
angular.module('News').factory('FolderBl', [
- '_Bl', 'FolderModel', 'FeedBl', 'Persistence', 'FeedType', 'ActiveFeed', 'ItemModel', 'ShowAll', '_ExistsError', function(_Bl, FolderModel, FeedBl, Persistence, FeedType, ActiveFeed, ItemModel, ShowAll, _ExistsError) {
+ '_Bl', 'FolderModel', 'FeedBl', 'Persistence', 'FeedType', 'ActiveFeed', 'ItemModel', 'ShowAll', '_ExistsError', 'OPMLParser', function(_Bl, FolderModel, FeedBl, Persistence, FeedType, ActiveFeed, ItemModel, ShowAll, _ExistsError, OPMLParser) {
var FolderBl;
FolderBl = (function(_super) {
__extends(FolderBl, _super);
- function FolderBl(_folderModel, _feedBl, _showAll, activeFeed, persistence, _feedType, itemModel) {
+ function FolderBl(_folderModel, _feedBl, _showAll, activeFeed, persistence, _feedType, itemModel, _opmlParser) {
this._folderModel = _folderModel;
this._feedBl = _feedBl;
this._showAll = _showAll;
this._feedType = _feedType;
+ this._opmlParser = _opmlParser;
FolderBl.__super__.constructor.call(this, activeFeed, persistence, itemModel, this._feedType.Folder);
}
@@ -972,10 +983,50 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
return this._folderModel.removeByName(folderName);
};
+ FolderBl.prototype["import"] = function(xml) {
+ var opml;
+
+ opml = this._opmlParser.parseXML(xml);
+ return this._importElement(opml, 0);
+ };
+
+ FolderBl.prototype._importElement = function(opml, parentFolderId) {
+ var error, folder, item, _i, _len, _ref, _results,
+ _this = this;
+
+ _ref = opml.getItems();
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ item = _ref[_i];
+ if (item.isFolder()) {
+ try {
+ _results.push(this.create(item.getName(), function(data) {
+ return _this._importElement(item, data.folders[0].id);
+ }));
+ } catch (_error) {
+ error = _error;
+ if (error instanceof _ExistsError) {
+ folder = this._folderModel.getByName(item.getName());
+ _results.push(this._importElement(item, folder.id));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ } else {
+ try {
+ _results.push(this._feedBl.create(item.getUrl(), parentFolderId));
+ } catch (_error) {
+ error = _error;
+ }
+ }
+ }
+ return _results;
+ };
+
return FolderBl;
})(_Bl);
- return new FolderBl(FolderModel, FeedBl, ShowAll, ActiveFeed, Persistence, FeedType, ItemModel);
+ return new FolderBl(FolderModel, FeedBl, ShowAll, ActiveFeed, Persistence, FeedType, ItemModel, OPMLParser);
}
]);
@@ -1879,7 +1930,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
(function() {
- angular.module('News').factory('_OPMLParser', function() {
+ angular.module('News').factory('OPMLParser', function() {
var Feed, Folder, OPMLParser;
Feed = (function() {
@@ -1964,7 +2015,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
return OPMLParser;
})();
- return OPMLParser;
+ return new OPMLParser();
});
}).call(this);
@@ -2523,12 +2574,6 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
}
]);
- angular.module('News').factory('OPMLParser', [
- '_OPMLParser', function(_OPMLParser) {
- return new _OPMLParser();
- }
- ]);
-
}).call(this);
// Generated by CoffeeScript 1.6.2
diff --git a/js/tests/controllers/settingscontrollerSpec.coffee b/js/tests/controllers/settingscontrollerSpec.coffee
index aa66b6878..cedf5eb44 100644
--- a/js/tests/controllers/settingscontrollerSpec.coffee
+++ b/js/tests/controllers/settingscontrollerSpec.coffee
@@ -21,7 +21,25 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
###
-describe '_SettingsController', ->
+describe 'SettingsController', ->
+ beforeEach module 'News'
- beforeEach module 'News' \ No newline at end of file
+ beforeEach inject ($controller, @FeedBl, @FolderBl) =>
+ @scope = {}
+ @replace =
+ '$scope': @scope
+ @controller = $controller('SettingsController', @replace)
+
+
+ it 'should make FeedBl available', =>
+ expect(@scope.feedBl).toBe(@FeedBl)
+
+
+ it 'should show an error if the xml import failed', =>
+ xml = 'test'
+
+ @scope.import(xml)
+
+ expect(@scope.error).toBe(true)
+ \ No newline at end of file
diff --git a/js/tests/services/bl/folderblSpec.coffee b/js/tests/services/bl/folderblSpec.coffee
index 3ca761f7d..ba7e94de5 100644
--- a/js/tests/services/bl/folderblSpec.coffee
+++ b/js/tests/services/bl/folderblSpec.coffee
@@ -29,6 +29,7 @@ describe 'FolderBl', ->
angular.module('News').factory 'Persistence', =>
@persistence =
createFolder: ->
+ createFeed: ->
beforeEach inject (@FolderBl, @FolderModel, @FeedModel, @ShowAll,
@ActiveFeed, @FeedType, @_ExistsError) =>
@@ -220,3 +221,212 @@ describe 'FolderBl', ->
expect(@FolderModel.getById(3).opened).toBeTruthy()
expect(@persistence.openFolder).toHaveBeenCalledWith(3)
+
+ it 'should not import on empty opml', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).not.toHaveBeenCalled()
+ expect(@persistence.createFeed).not.toHaveBeenCalled()
+
+
+ it 'should import a folder', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+ <outline text="Design" title="Design" />
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).toHaveBeenCalledWith('Design', 0,
+ jasmine.any(Function))
+ expect(@persistence.createFeed).not.toHaveBeenCalled()
+
+
+ it 'should import a feed', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+ <outline htmlUrl="http://worrydream.com/" text=
+ "&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ title="&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ type="rss"
+ version="RSS" xmlUrl="http://worrydream.com/feed.xml"/>
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).not.toHaveBeenCalled()
+ expect(@persistence.createFeed).toHaveBeenCalledWith(
+ 'http://worrydream.com/feed.xml', 0, jasmine.any(Function))
+
+
+ it 'should import nested folders', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFolder.andCallFake (name, parentId, onSuccess) =>
+ data =
+ data:
+ folders: [
+ {id: 3}
+ ]
+ onSuccess(data)
+
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+ <outline text="Design" title="Design">
+ <outline htmlUrl="http://worrydream.com/" text=
+ "&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ title="&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ type="rss"
+ version="RSS" xmlUrl="http://worrydream.com/feed.xml"/>
+ </outline>
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).toHaveBeenCalledWith('Design', 0,
+ jasmine.any(Function))
+ expect(@persistence.createFeed).toHaveBeenCalledWith(
+ 'http://worrydream.com/feed.xml', 3, jasmine.any(Function))
+
+
+ it 'should use an existing folder when importing a folder', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ @FolderModel.add({id: 2, name: 'design'})
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+ <outline text="Design" title="Design">
+ <outline htmlUrl="http://worrydream.com/" text=
+ "&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ title="&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ type="rss"
+ version="RSS" xmlUrl="http://worrydream.com/feed.xml"/>
+ </outline>
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).not.toHaveBeenCalled()
+ expect(@persistence.createFeed).toHaveBeenCalledWith(
+ 'http://worrydream.com/feed.xml', 2, jasmine.any(Function))
+
+
+ it 'should not import a feed if it already exists', =>
+ @persistence.createFolder = jasmine.createSpy('create folder')
+ @persistence.createFeed = jasmine.createSpy('create feed')
+
+ @FeedModel.add({urlHash: hex_md5('http://worrydream.com/feed.xml')})
+
+ xml = '<?xml version="1.0" ?>
+ <opml version="1.1">
+ <!--Generated by NewsBlur - www.newsblur.com-->
+ <head>
+ <title>
+ NewsBlur Feeds
+ </title>
+ <dateCreated>
+ 2013-03-14 16:44:01.356965
+ </dateCreated>
+ <dateModified>
+ 2013-03-14 16:44:01.356965
+ </dateModified>
+ </head>
+ <body>
+ <outline htmlUrl="http://worrydream.com/" text=
+ "&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ title="&lt;div&gt;Bret Victor\'s website&lt;/div&gt;"
+ type="rss"
+ version="RSS" xmlUrl="http://worrydream.com/feed.xml"/>
+ </body>
+ </opml>'
+
+ @FolderBl.import(xml)
+
+ expect(@persistence.createFolder).not.toHaveBeenCalled()
+ expect(@persistence.createFeed).not.toHaveBeenCalled() \ No newline at end of file
diff --git a/templates/part.settings.php b/templates/part.settings.php
index fbe39fffd..ab1876104 100644
--- a/templates/part.settings.php
+++ b/templates/part.settings.php
@@ -11,12 +11,15 @@
<div id="app-settings-content">
<fieldset class="personalblock">
<legend><strong><?php p($l->t('Import / Export OPML')); ?></strong></legend>
+
<input type="file" id="opml-upload" name="import"
oc-read-file="import($fileContent)"/>
<button title="<?php p($l->t('Import')); ?>"
oc-forward-click="{selector:'#opml-upload'}">
<?php p($l->t('Import')); ?>
</button>
+
+
<a title="<?php p($l->t('Export')); ?>" class="button"
href="<?php p(\OCP\Util::linkToRoute('news_export_opml')); ?>"
target="_blank"
@@ -29,6 +32,10 @@
<?php p($l->t('Export')); ?>
</button>
+ <p class="error" ng-show="error">
+ <?php p($l->t('Error when importing: file does not contain valid OPML')); ?>
+ </p>
+
</fieldset>
<fieldset class="personalblock">
<legend><strong><?php p($l->t('Subscribelet')); ?></strong></legend>