From 38e4b6d7e973bc99811139066a7a047b4bd76a4c Mon Sep 17 00:00:00 2001 From: Bernhard Posselt Date: Sat, 13 Apr 2013 17:28:07 +0200 Subject: implement opml import, fix #23 --- js/app/controllers/settingscontroller.coffee | 12 +- js/app/services/bl/folderbl.coffee | 30 ++- js/app/services/opmlparser.coffee | 4 +- js/app/services/services.coffee | 5 - js/public/app.js | 75 ++++++-- js/tests/controllers/settingscontrollerSpec.coffee | 22 ++- js/tests/services/bl/folderblSpec.coffee | 210 +++++++++++++++++++++ templates/part.settings.php | 7 + 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 . 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 . 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 . ### -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 . (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 . __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 . 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 . (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 . return OPMLParser; })(); - return OPMLParser; + return new OPMLParser(); }); }).call(this); @@ -2523,12 +2574,6 @@ License along with this library. If not, see . } ]); - 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 . ### -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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + ' + + @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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + ' + + @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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + ' + + @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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + + + ' + + @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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + + + ' + + @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 = ' + + + + + NewsBlur Feeds + + + 2013-03-14 16:44:01.356965 + + + 2013-03-14 16:44:01.356965 + + + + + + ' + + @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 @@