diff options
author | Bernhard Posselt <BernhardPosselt@users.noreply.github.com> | 2016-03-25 19:50:33 +0100 |
---|---|---|
committer | Bernhard Posselt <BernhardPosselt@users.noreply.github.com> | 2016-03-25 19:50:33 +0100 |
commit | 84c6f5921e4e5f4c1fe1e27761bbc645549a6e17 (patch) | |
tree | ab4750ec16dfa23e1b36841e2dee89669d02b1d7 | |
parent | f7520a8fe7191475668904f5f8c1816df25e2e5a (diff) | |
parent | 3835a9a87795b331aa9e7ac46e4182e1ec4c9d97 (diff) |
Merge pull request #947 from owncloud/basic-auth
Add basic basic auth support
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | appinfo/database.xml | 12 | ||||
-rw-r--r-- | appinfo/info.xml | 2 | ||||
-rw-r--r-- | controller/feedcontroller.php | 8 | ||||
-rw-r--r-- | css/navigation.css | 12 | ||||
-rw-r--r-- | db/feed.php | 10 | ||||
-rw-r--r-- | fetcher/feedfetcher.php | 21 | ||||
-rw-r--r-- | fetcher/fetcher.php | 9 | ||||
-rw-r--r-- | fetcher/ifeedfetcher.php | 6 | ||||
-rw-r--r-- | fetcher/youtubefetcher.php | 9 | ||||
-rw-r--r-- | js/build/app.min.js | 6 | ||||
-rw-r--r-- | js/controller/NavigationController.js | 6 | ||||
-rw-r--r-- | js/gulpfile.js | 2 | ||||
-rw-r--r-- | js/service/FeedResource.js | 7 | ||||
-rw-r--r-- | js/tests/unit/controller/NavigationControllerSpec.js | 12 | ||||
-rw-r--r-- | js/tests/unit/service/FeedResourceSpec.js | 16 | ||||
-rw-r--r-- | service/feedservice.php | 16 | ||||
-rw-r--r-- | templates/part.navigation.addfeed.php | 20 | ||||
-rw-r--r-- | tests/unit/db/FeedTest.php | 6 | ||||
-rw-r--r-- | tests/unit/service/FeedServiceTest.php | 7 |
20 files changed, 157 insertions, 31 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index a8932b21e..e1446b487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ owncloud-news (7.2.0) * **New dependency**: Bump required PostgreSQL version to 9.4 * **New dependency**: Bump required MySql/MariaDB version to 5.5 * **Bugfix**: Fix bug that would not lowercase non ASCII characters when searching, #944 +* **Enhancement**: Add HTTP basic auth, #938 owncloud-news (7.1.2) * **Enhancement**: Major JavaScript library updates: diff --git a/appinfo/database.xml b/appinfo/database.xml index a831b4211..9b6b36a3d 100644 --- a/appinfo/database.xml +++ b/appinfo/database.xml @@ -154,6 +154,18 @@ <notnull>false</notnull> </field> <field> + <name>basic_auth_user</name> + <type>clob</type> + <default></default> + <notnull>false</notnull> + </field> + <field> + <name>basic_auth_password</name> + <type>clob</type> + <default></default> + <notnull>false</notnull> + </field> + <field> <name>deleted_at</name> <type>integer</type> <default>0</default> diff --git a/appinfo/info.xml b/appinfo/info.xml index cb58ef86b..38fd91fef 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -7,7 +7,7 @@ <author>Bernhard Posselt, Alessandro Cosentino, Jan-Christoph Borchardt</author> <category>multimedia</category> <licence>AGPL</licence> - <version>7.1.2</version> + <version>7.1.3</version> <namespace>News</namespace> <!-- resources --> diff --git a/controller/feedcontroller.php b/controller/feedcontroller.php index 37e415661..2b95794a3 100644 --- a/controller/feedcontroller.php +++ b/controller/feedcontroller.php @@ -124,16 +124,20 @@ class FeedController extends Controller { * @param string $url * @param int $parentFolderId * @param string $title + * @param string $user + * @param string $password * @return array|\OCP\AppFramework\Http\JSONResponse */ - public function create($url, $parentFolderId, $title){ + public function create($url, $parentFolderId, $title=null, + $user=null, $password=null){ try { // we need to purge deleted feeds if a feed is created to // prevent already exists exceptions $this->feedService->purgeDeleted($this->userId, false); $feed = $this->feedService->create($url, $parentFolderId, - $this->userId, $title); + $this->userId, $title, + $user, $password); $params = ['feeds' => [$feed]]; try { diff --git a/css/navigation.css b/css/navigation.css index 8d2d3a362..d5ea11d7f 100644 --- a/css/navigation.css +++ b/css/navigation.css @@ -76,6 +76,18 @@ margin-right: 0; } +#app-navigation .add-new-popup .add-feed-advanced { + width: 100%; +} + +#app-navigation .add-new-popup .add-feed-advanced-area { + padding: 10px 0; +} + +#app-navigation .add-new-popup .add-feed-advanced-area h2 { + font-size: 11pt; + font-weight: bold; +} #app-navigation .add-new-popup .error { padding: 0 0 10px 0; diff --git a/db/feed.php b/db/feed.php index 5e2c7fab0..f78b1f364 100644 --- a/db/feed.php +++ b/db/feed.php @@ -56,6 +56,10 @@ use \OCP\AppFramework\Db\Entity; * @method void setUpdateErrorCount(integer $value) * @method string getLastUpdateError() * @method void setLastUpdateError(string $value) + * @method string getBasicAuthUser() + * @method void setBasicAuthUser(string $value) + * @method string getBasicAuthPassword() + * @method void setBasicAuthPassword(string $value) */ class Feed extends Entity implements IAPI, \JsonSerializable { @@ -82,6 +86,8 @@ class Feed extends Entity implements IAPI, \JsonSerializable { protected $updateMode; protected $updateErrorCount; protected $lastUpdateError; + protected $basicAuthUser; + protected $basicAuthPassword; public function __construct(){ $this->addType('parentId', 'integer'); @@ -123,7 +129,9 @@ class Feed extends Entity implements IAPI, \JsonSerializable { 'pinned', 'updateMode', 'updateErrorCount', - 'lastUpdateError' + 'lastUpdateError', + 'basicAuthUser', + 'basicAuthPassword' ]); $url = parse_url($this->link)['host']; diff --git a/fetcher/feedfetcher.php b/fetcher/feedfetcher.php index 6b7460679..458f1220d 100644 --- a/fetcher/feedfetcher.php +++ b/fetcher/feedfetcher.php @@ -23,6 +23,8 @@ use PicoFeed\Client\InvalidUrlException; use PicoFeed\Client\MaxRedirectException; use PicoFeed\Client\MaxSizeException; use PicoFeed\Client\TimeoutException; +use PicoFeed\Client\ForbiddenException; +use PicoFeed\Client\UnauthorizedException; use OCP\IL10N; use OCP\AppFramework\Utility\ITimeFactory; @@ -71,14 +73,24 @@ class FeedFetcher implements IFeedFetcher { * no results are fetched * @param bool fullTextEnabled if true tells the fetcher to enhance the * articles by fetching custom enhanced content + * @param string $basicAuthUser if given, basic auth is set for this feed + * @param string $basicAuthPassword if given, basic auth is set for this + * feed. Ignored if user is null or an empty string * @throws FetcherException if it fails * @return array an array containing the new feed and its items, first * element being the Feed and second element being an array of Items */ public function fetch($url, $getFavicon=true, $lastModified=null, - $etag=null, $fullTextEnabled=false) { + $etag=null, $fullTextEnabled=false, + $basicAuthUser=null, $basicAuthPassword=null) { try { - $resource = $this->reader->discover($url, $lastModified, $etag); + if ($basicAuthUser !== null && trim($basicAuthUser) !== '') { + $resource = $this->reader->discover($url, $lastModified, $etag, + $basicAuthUser, + $basicAuthPassword); + } else { + $resource = $this->reader->discover($url, $lastModified, $etag); + } if (!$resource->isModified()) { return [null, null]; @@ -131,6 +143,11 @@ class FeedFetcher implements IFeedFetcher { $msg = $this->l10n->t('Bigger than maximum allowed size'); } else if ($ex instanceof TimeoutException) { $msg = $this->l10n->t('Request timed out'); + } else if ($ex instanceof UnauthorizedException) { + $msg = $this->l10n->t('Required credentials for feed were ' . + 'either missing or incorrect'); + } else if ($ex instanceof ForbiddenException) { + $msg = $this->l10n->t('Forbidden to access feed'); } throw new FetcherException($msg); diff --git a/fetcher/fetcher.php b/fetcher/fetcher.php index 4787ccfdd..43c9e7a3f 100644 --- a/fetcher/fetcher.php +++ b/fetcher/fetcher.php @@ -44,16 +44,21 @@ class Fetcher { * no results are fetched * @param bool fullTextEnabled if true tells the fetcher to enhance the * articles by fetching custom enhanced content + * @param string $basicAuthUser if given, basic auth is set for this feed + * @param string $basicAuthPassword if given, basic auth is set for this + * feed. Ignored if user is null or an empty string * @throws FetcherException if simple pie fails * @return array an array containing the new feed and its items, first * element being the Feed and second element being an array of Items */ public function fetch($url, $getFavicon=true, $lastModified=null, - $etag=null, $fullTextEnabled=false) { + $etag=null, $fullTextEnabled=false, + $basicAuthUser=null, $basicAuthPassword=null) { foreach($this->fetchers as $fetcher){ if($fetcher->canHandle($url)){ return $fetcher->fetch($url, $getFavicon, $lastModified, $etag, - $fullTextEnabled); + $fullTextEnabled, $basicAuthUser, + $basicAuthPassword); } } diff --git a/fetcher/ifeedfetcher.php b/fetcher/ifeedfetcher.php index 297885930..e854d83fe 100644 --- a/fetcher/ifeedfetcher.php +++ b/fetcher/ifeedfetcher.php @@ -27,12 +27,16 @@ interface IFeedFetcher { * no results are fetched * @param bool fullTextEnabled if true tells the fetcher to enhance the * articles by fetching custom enhanced content + * @param string $basicAuthUser if given, basic auth is set for this feed + * @param string $basicAuthPassword if given, basic auth is set for this + * feed. Ignored if user is null or an empty string * @throws FetcherException if the fetcher encounters a problem * @return array an array containing the new feed and its items, first * element being the Feed and second element being an array of Items */ function fetch($url, $getFavicon=true, $lastModified=null, $etag=null, - $fullTextEnabled=false); + $fullTextEnabled=false, $basicAuthUser=null, + $basicAuthPassword=null); /** * @param string $url the url that should be fetched diff --git a/fetcher/youtubefetcher.php b/fetcher/youtubefetcher.php index 35ecd80f0..3752ba197 100644 --- a/fetcher/youtubefetcher.php +++ b/fetcher/youtubefetcher.php @@ -55,16 +55,21 @@ class YoutubeFetcher implements IFeedFetcher { * no results are fetched * @param bool fullTextEnabled if true tells the fetcher to enhance the * articles by fetching custom enhanced content + * @param string $basicAuthUser if given, basic auth is set for this feed + * @param string $basicAuthPassword if given, basic auth is set for this + * feed. Ignored if user is null or an empty string * @throws FetcherException if it fails * @return array an array containing the new feed and its items, first * element being the Feed and second element being an array of Items */ public function fetch($url, $getFavicon=true, $lastModified=null, - $etag=null, $fullTextEnabled=false) { + $etag=null, $fullTextEnabled=false, + $basicAuthUser=null, $basicAuthPassword=null) { $transformedUrl = $this->buildUrl($url); $result = $this->feedFetcher->fetch( - $transformedUrl, $getFavicon, $lastModified, $etag, $fullTextEnabled + $transformedUrl, $getFavicon, $lastModified, $etag, + $fullTextEnabled, $basicAuthUser, $basicAuthPassword ); // reset feed url so we know the correct added url for the feed diff --git a/js/build/app.min.js b/js/build/app.min.js index 32ea96774..b7f3bbeb2 100644 --- a/js/build/app.min.js +++ b/js/build/app.min.js @@ -1,3 +1,3 @@ -var app=angular.module("News",["ngRoute","ngSanitize","ngAnimate"]);app.config(["$routeProvider","$provide","$httpProvider",function(e,n,r){"use strict";var i={FEED:0,FOLDER:1,STARRED:2,SUBSCRIPTIONS:3,SHARED:4,EXPLORE:5};n.constant("REFRESH_RATE",60),n.constant("ITEM_BATCH_SIZE",40),n.constant("ITEM_AUTO_PAGE_SIZE",20),n.constant("BASE_URL",OC.generateUrl("/apps/news")),n.constant("FEED_TYPE",i),n.constant("MARK_READ_TIMEOUT",.5),n.constant("SCROLL_TIMEOUT",.1),n.factory("CSRFInterceptor",["$q","BASE_URL","$window",function(e,t,n){return{request:function(r){var i=n.location.href.split(n.location.pathname)[0];return 0!==r.url.indexOf(t)&&0!==r.url.indexOf(i)||(r.headers.requesttoken=oc_requesttoken),r||e.when(r)}}}]);var o={0:t("news","Request failed, network connection unavailable!"),401:t("news","Request unauthorized. Are you logged in?"),403:t("news","Request forbidden. Are you an admin?"),412:t("news","Token expired or app not enabled! Reload the page!"),500:t("news","Internal server error! Please check your data/owncloud.log file for additional information!"),503:t("news","Request failed, ownCloud is in currently in maintenance mode!")};n.factory("ConnectionErrorInterceptor",["$q","$timeout",function(e,t){var n;return{responseError:function(r){return r.status in o&&(n&&t.cancel(n),OC.Notification.hide(),OC.Notification.showHtml(o[r.status]),n=t(function(){OC.Notification.hide()},5e3)),e.reject(r)}}}]),r.interceptors.push("CSRFInterceptor"),r.interceptors.push("ConnectionErrorInterceptor");var s=function(e){return{data:["$http","$route","$q","$location","BASE_URL","ITEM_BATCH_SIZE","FEED_TYPE","SettingsResource","FeedResource",function(t,n,r,i,o,s,a,u,d){var c=u.get("showAll"),l=u.get("oldestFirst"),f=i.search().search||"",h=r.defer();if(null===c||null===l)h.resolve({});else{var p={type:e,limit:s,showAll:c,oldestFirst:l,search:f};if(void 0!==n.current&&void 0!==n.current.params&&void 0!==n.current.params.id&&(p.id=n.current.params.id),e===a.FEED){var g=d.getById(p.id);void 0===g||2===g.ordering?p.oldestFirst=!1:1===g.ordering&&(p.oldestFirst=!0)}t({url:o+"/items",method:"GET",params:p}).success(function(e){h.resolve(e)})}return h.promise}]}},a=function(){return{sites:["$http","$q","BASE_URL","$location","Publisher","SettingsResource",function(e,t,n,r,i,o){var s=t.defer(),a=r.search().lang;return a||(a=o.get("language")),e.get(n+"/settings").then(function(t){i.publishAll(t);var n=o.get("exploreUrl").replace(/\/+$/,""),r=n+"/feeds."+a+".json",s=n+"/feeds.en.json";return e.get(r)["catch"](function(){return e.get(s)})}).then(function(e){s.resolve(e.data)})["catch"](function(){s.reject()}),s.promise}]}};e.when("/items",{controller:"ContentController as Content",templateUrl:"content.html",resolve:s(i.SUBSCRIPTIONS),type:i.SUBSCRIPTIONS}).when("/items/starred",{controller:"ContentController as Content",templateUrl:"content.html",resolve:s(i.STARRED),type:i.STARRED}).when("/items/feeds/:id",{controller:"ContentController as Content",templateUrl:"content.html",resolve:s(i.FEED),type:i.FEED}).when("/items/folders/:id",{controller:"ContentController as Content",templateUrl:"content.html",resolve:s(i.FOLDER),type:i.FOLDER}).when("/explore",{controller:"ExploreController as Explore",templateUrl:"explore.html",resolve:a(),type:i.EXPLORE}).when("/shortcuts",{templateUrl:"shortcuts.html",type:-1})}]),app.run(["$rootScope","$location","$http","$q","$interval","$route","Loading","ItemResource","FeedResource","FolderResource","SettingsResource","Publisher","BASE_URL","FEED_TYPE","REFRESH_RATE",function(e,t,n,r,i,o,s,a,u,d,c,l,f,h,p){"use strict";s.setLoading("global",!0),l.subscribe(a).toChannels(["items","newestItemId","starred"]),l.subscribe(d).toChannels(["folders"]),l.subscribe(u).toChannels(["feeds"]),l.subscribe(c).toChannels(["settings"]);var g=r.defer();n.get(f+"/settings").success(function(e){l.publishAll(e),g.resolve()});var v=r.defer(),m=t.path();n.get(f+"/feeds/active").success(function(e){var n;switch(e.activeFeed.type){case h.FEED:n="/items/feeds/"+e.activeFeed.id;break;case h.FOLDER:n="/items/folders/"+e.activeFeed.id;break;case h.STARRED:n="/items/starred";break;case h.EXPLORE:n="/explore";break;default:n="/items"}/^\/items(\/(starred|explore|feeds\/\d+|folders\/\d+))?\/?$/.test(m)||t.path(n),v.resolve()});var y,E=r.defer();n.get(f+"/feeds").success(function(e){y=e,E.resolve()});var C,F=r.defer();n.get(f+"/folders").success(function(e){C=e,F.resolve()}),r.all([E.promise,F.promise]).then(function(){l.publishAll(y),l.publishAll(C),0===y.feeds.length&&0===C.folders.length&&t.path("/explore")}),r.all([g.promise,v.promise,E.promise,F.promise]).then(function(){o.reload(),s.setLoading("global",!1)}),i(function(){n.get(f+"/feeds").success(function(e){l.publishAll(e)}),n.get(f+"/folders").success(function(e){l.publishAll(e)})},1e3*p),e.$on("$routeChangeStart",function(){s.setLoading("content",!0)}),e.$on("$routeChangeSuccess",function(){s.setLoading("content",!1)}),e.$on("$routeChangeError",function(){t.path("/items")})}]),app.controller("AppController",["Loading","FeedResource","FolderResource",function(e,t,n){"use strict";this.loading=e,this.isFirstRun=function(){return 0===t.size()&&0===n.size()},this.play=function(e){this.playingItem=e}}]),app.controller("ContentController",["Publisher","FeedResource","ItemResource","SettingsResource","data","$route","$routeParams","$location","FEED_TYPE","ITEM_AUTO_PAGE_SIZE","Loading",function(e,t,n,r,i,o,s,a,u,d,c){"use strict";n.clear(),e.publishAll(i),this.isAutoPagingEnabled=!0,n.size()>=d?this.isNothingMoreToAutoPage=!1:this.isNothingMoreToAutoPage=!0,this.getItems=function(){return n.getAll()},this.toggleStar=function(e){n.toggleStar(e)},this.toggleItem=function(e){this.isCompactView()&&(e.show=!e.show)},this.isShowAll=function(){return r.get("showAll")},this.markRead=function(e){var r=n.get(e);r.keepUnread||r.unread!==!0||(n.markItemRead(e),t.markItemOfFeedRead(r.feedId))},this.getFeed=function(e){return t.getById(e)},this.toggleKeepUnread=function(e){var r=n.get(e);r.unread||(t.markItemOfFeedUnread(r.feedId),n.markItemRead(e,!1)),r.keepUnread=!r.keepUnread};var l=this,f=function(){var e=r.get("oldestFirst");if(l.isFeed()){var n=t.getById(s.id);n&&1===n.ordering?e=!0:n&&2===n.ordering&&(e=!1)}return e};this.orderBy=function(){return f()?"id":"-id"},this.isCompactView=function(){return r.get("compact")},this.isCompactExpand=function(){return r.get("compactExpand")},this.autoPagingEnabled=function(){return this.isAutoPagingEnabled},this.markReadEnabled=function(){return!r.get("preventReadOnScroll")},this.scrollRead=function(e){var r=[],i=[];e.forEach(function(e){var t=n.get(e);t.keepUnread||(r.push(e),i.push(t.feedId))}),r.length>0&&(t.markItemsOfFeedsRead(i),n.markItemsRead(r))},this.isFeed=function(){return o.current.$$route.type===u.FEED},this.autoPage=function(){if(!this.isNothingMoreToAutoPage){if(!this.isAutoPagingEnabled)return void(this.autoPageAgain=!0);this.isAutoPagingEnabled=!1,this.autoPageAgain=!1;var t=o.current.$$route.type,i=s.id,u=f(),l=r.get("showAll"),h=this,p=a.search().search;c.setLoading("autopaging",!0),n.autoPage(t,i,u,l,p).success(function(t){e.publishAll(t),t.items.length>=d?h.isAutoPagingEnabled=!0:h.isNothingMoreToAutoPage=!0,h.isAutoPagingEnabled&&h.autoPageAgain&&h.autoPage()}).error(function(){h.isAutoPagingEnabled=!0})["finally"](function(){c.setLoading("autopaging",!1)})}},this.getRelativeDate=function(e){if(void 0!==e&&""!==e){var t=r.get("language"),n=moment.unix(e).locale(t).fromNow()+"";return n}return""},this.refresh=function(){o.reload()},this.getMediaType=function(e){return e&&0===e.indexOf("audio")?"audio":e&&0===e.indexOf("video")?"video":void 0}}]),app.controller("ExploreController",["sites","$rootScope","FeedResource","SettingsResource","$location",function(e,t,n,r,i){"use strict";this.sites=e,this.feeds=Object.keys(e).map(function(t){return[t,e[t]]}).reduce(function(e,t){var n=t[0],r=t[1];return r.forEach(function(e){e.category=n}),e.concat(r)},[]),this.feedExists=function(e){return void 0!==n.getByLocation(e)},this.subscribeTo=function(e){t.$broadcast("addFeed",e)},this.isCategoryShown=function(e){return e.filter(function(e){return void 0===n.getByLocation(e.feed)}).length>0},this.getSupportedLanguageCodes=function(){return r.getSupportedLanguageCodes()},this.getCurrentLanguageCode=function(){var e=i.search().lang;return e||(e=r.get("language")),e},this.showLanguage=function(e){i.url("/explore/?lang="+e)},this.selectedLanguageCode=this.getCurrentLanguageCode()}]),app.controller("NavigationController",["$route","FEED_TYPE","FeedResource","FolderResource","ItemResource","SettingsResource","Publisher","$rootScope","$location","$q",function(e,t,n,r,i,o,s,a,u,d){"use strict";this.feedError="",this.showNewFolder=!1,this.renamingFolder=!1,this.addingFeed=!1,this.addingFolder=!1,this.folderError="",this.renameError="",this.feed={};var c=function(){return parseInt(e.current.params.id,10)};this.getLanguageCode=function(){return o.get("language")},this.getFeeds=function(){return n.getAll()},this.getFolders=function(){return r.getAll()},this.markFolderRead=function(e){n.markFolderRead(e),n.getByFolderId(e).forEach(function(e){i.markFeedRead(e.id)})},this.markFeedRead=function(e){i.markFeedRead(e),n.markFeedRead(e)},this.markRead=function(){i.markRead(),n.markRead()},this.isShowAll=function(){return o.get("showAll")},this.getFeedsOfFolder=function(e){return n.getByFolderId(e)},this.getUnreadCount=function(){return n.getUnreadCount()},this.isUnread=function(){return this.getUnreadCount()>0},this.getFeedUnreadCount=function(e){var t=n.getById(e);return void 0!==t?t.unreadCount:0},this.isFeedUnread=function(e){return this.getFeedUnreadCount(e)>0},this.getFolderUnreadCount=function(e){return n.getFolderUnreadCount(e)},this.isFolderUnread=function(e){return this.getFolderUnreadCount(e)>0},this.getStarredCount=function(){return i.getStarredCount()},this.isStarredUnread=function(){return this.getStarredCount()>0},this.toggleFolder=function(e){r.toggleOpen(e)},this.hasFeeds=function(e){return void 0!==n.getFolderUnreadCount(e)},this.subFeedActive=function(r){var i=e.current.$$route.type;if(i===t.FEED){var o=n.getById(c());if(void 0!==o&&o.folderId===r)return!0}return!1},this.isSubscriptionsActive=function(){return e.current&&e.current.$$route.type===t.SUBSCRIPTIONS},this.isStarredActive=function(){return e.current&&e.current.$$route.type===t.STARRED},this.isExploreActive=function(){return e.current&&e.current.$$route.type===t.EXPLORE},this.isFolderActive=function(n){return e.current&&e.current.$$route.type===t.FOLDER&&c()===n},this.isFeedActive=function(n){return e.current&&e.current.$$route.type===t.FEED&&c()===n},this.folderNameExists=function(e){return e=e||"",void 0!==r.get(e.trim())},this.feedUrlExists=function(e){return e=e||"",e=e.trim(),void 0!==n.get(e)||void 0!==n.get("http://"+e)},this.createFeed=function(e){var t=this;this.showNewFolder=!1,this.addingFeed=!0;var i=e.newFolder,o=e.existingFolder||{id:0};void 0===i||""===i?(o.getsFeed=!0,n.create(e.url,o.id,void 0).then(function(e){s.publishAll(e),u.path("/items/feeds/"+e.feeds[0].id+"/")})["finally"](function(){o.getsFeed=void 0,e.url="",t.addingFeed=!1})):r.create(i).then(function(n){s.publishAll(n),e.existingFolder=r.get(n.folders[0].name),e.newFolder=void 0,t.createFeed(e)})},this.createFolder=function(e){var t=this;this.addingFolder=!0,r.create(e.name).then(function(e){s.publishAll(e)})["finally"](function(){t.addingFolder=!1,e.name=""})},this.moveFeed=function(t,r){var i=!1,o=n.getById(t);o.folderId!==r&&((this.isFolderActive(o.folderId)||this.isFolderActive(r))&&(i=!0),n.move(t,r),i&&e.reload())},this.renameFeed=function(e){n.patch(e.id,{title:e.title}),e.editing=!1},this.renameFolder=function(e,t){e.renameError="",this.renamingFolder=!0;var n=this;e.name===t?(e.renameError="",e.editing=!1,this.renamingFolder=!1):r.rename(e.name,t).then(function(){e.renameError="",e.editing=!1},function(t){e.renameError=t})["finally"](function(){n.renamingFolder=!1})},this.reversiblyDeleteFeed=function(t){n.reversiblyDelete(t.id)["finally"](function(){e.reload()})},this.undoDeleteFeed=function(t){n.undoDelete(t.id)["finally"](function(){e.reload()})},this.deleteFeed=function(e){n["delete"](e.url)},this.reversiblyDeleteFolder=function(t){d.all(n.reversiblyDeleteFolder(t.id),r.reversiblyDelete(t.name))["finally"](function(){e.reload()})},this.undoDeleteFolder=function(t){d.all(n.undoDeleteFolder(t.id),r.undoDelete(t.name))["finally"](function(){e.reload()})},this.deleteFolder=function(e){n.deleteFolder(e.id),r["delete"](e.name)},this.setOrdering=function(t,r){n.patch(t.id,{ordering:r}),e.reload()},this.togglePinned=function(e){var t=n.getById(e);return t?n.patch(e,{pinned:!t.pinned}):void 0},this.setUpdateMode=function(e,t){return n.patch(e,{updateMode:t})},this.toggleFullText=function(t){a.$broadcast("$routeChangeStart"),n.toggleFullText(t.id)["finally"](function(){a.$broadcast("$routeChangeSuccess"),e.reload()})},this.search=function(e){""===e?u.search("search",null):u.search("search",e)};var l=this;a.$on("moveFeedToFolder",function(e,t){l.moveFeed(t.feedId,t.folderId)});var f=function(){var i;e.current&&(i=e.current.$$route.type);var o=0;if(i===t.FOLDER)o=c();else if(i===t.FEED){var s=n.getById(c());s&&(o=s.folderId)}var a;0!==o&&(a=r.getById(o)),l.feed.existingFolder=a};a.$on("$routeChangeSuccess",function(){f()})}]),app.controller("SettingsController",["$route","$q","SettingsResource","ItemResource","OPMLParser","OPMLImporter","Publisher",function(e,t,n,r,i,o,s){"use strict";this.isOPMLImporting=!1,this.isArticlesImporting=!1,this.opmlImportError=!1,this.articleImportError=!1;var a=function(t,r){n.set(t,r),["showAll","oldestFirst","compact"].indexOf(t)>=0&&e.reload()};this.toggleSetting=function(e){a(e,!this.getSetting(e))},this.getSetting=function(e){return n.get(e)},this.importOPML=function(e){this.opmlImportError=!1,this.articleImportError=!1;try{this.isOPMLImporting=!1;var t=i.parse(e),n=this,r=5;o.importFolders(t).then(function(e){return o.importFeedQueue(e,r)})["finally"](function(){n.isOPMLImporting=!1})}catch(s){console.error(s),this.isOPMLImporting=!1,this.opmlImportError=!0}},this.importArticles=function(e){this.opmlImportError=!1,this.articleImportError=!1;try{this.isArticlesImporting=!0;var t=JSON.parse(e),n=this;r.importArticles(t).success(function(e){s.publishAll(e)})["finally"](function(){n.isArticlesImporting=!1})}catch(i){console.error(i),this.articleImportError=!0,this.isArticlesImporting=!1}}}]),app.filter("trustUrl",["$sce",function(e){"use strict";return function(t){return e.trustAsResourceUrl(t)}}]),app.filter("unreadCountFormatter",function(){"use strict";return function(e){return e>999?"999+":e}}),app.factory("FeedResource",["Resource","$http","BASE_URL","$q",function(e,t,n,r){"use strict";var i=function(t,n,r){e.call(this,t,n,"url"),this.ids={},this.locations={},this.unreadCount=0,this.folderUnreadCount={},this.folderIds={},this.$q=r};return i.prototype=Object.create(e.prototype),i.prototype.receive=function(t){e.prototype.receive.call(this,t),this.updateUnreadCache(),this.updateFolderCache()},i.prototype.clear=function(){e.prototype.clear.call(this),this.unreadCount=0,this.folderUnreadCount={},this.folderIds={},this.ids={},this.locations={}},i.prototype.updateUnreadCache=function(){this.unreadCount=0,this.folderUnreadCount={};var e=this;this.values.forEach(function(t){t.unreadCount&&(e.unreadCount+=t.unreadCount),void 0!==t.folderId&&(e.folderUnreadCount[t.folderId]=e.folderUnreadCount[t.folderId]||0,e.folderUnreadCount[t.folderId]+=t.unreadCount)})},i.prototype.updateFolderCache=function(){this.folderIds={};var e=this;this.values.forEach(function(t){e.folderIds[t.folderId]=e.folderIds[t.folderId]||[],e.folderIds[t.folderId].push(t)})},i.prototype.add=function(t){e.prototype.add.call(this,t),void 0!==t.id&&(this.ids[t.id]=this.hashMap[t.url]),void 0!==t.location&&(this.locations[t.location]=this.hashMap[t.url])},i.prototype.markRead=function(){this.values.forEach(function(e){e.unreadCount=0}),this.updateUnreadCache()},i.prototype.markFeedRead=function(e){this.ids[e].unreadCount=0,this.updateUnreadCache()},i.prototype.markFolderRead=function(e){this.values.forEach(function(t){t.folderId===e&&(t.unreadCount=0)}),this.updateUnreadCache()},i.prototype.markItemOfFeedRead=function(e){this.ids[e].unreadCount-=1,this.updateUnreadCache()},i.prototype.markItemsOfFeedsRead=function(e){var t=this;e.forEach(function(e){t.ids[e].unreadCount-=1}),this.updateUnreadCache()},i.prototype.markItemOfFeedUnread=function(e){this.ids[e].unreadCount+=1,this.updateUnreadCache()},i.prototype.getUnreadCount=function(){return this.unreadCount},i.prototype.getFolderUnreadCount=function(e){return this.folderUnreadCount[e]},i.prototype.getByFolderId=function(e){return this.folderIds[e]||[]},i.prototype.getById=function(e){return this.ids[e]},i.prototype.getByLocation=function(e){return this.locations[e]},i.prototype.move=function(e,t){var n=this.getById(e);return n.folderId=t,this.updateFolderCache(),this.updateUnreadCache(),this.patch(e,{folderId:t})},i.prototype.create=function(e,t,n){e=e.trim(),e.startsWith("http")||(e="https://"+e),void 0!==n&&(n=n.trim());var r={url:e,folderId:t||0,title:n||e,unreadCount:0};this.add(r),this.updateFolderCache();var i=this.$q.defer();return this.http({method:"POST",url:this.BASE_URL+"/feeds",data:{url:e,parentFolderId:t||0,title:n}}).success(function(e){i.resolve(e)}).error(function(e){r.faviconLink="",r.error=e.message,i.reject()}),i.promise},i.prototype.reversiblyDelete=function(e,t,n){var r=this.getById(e);return r&&n!==!0&&(r.deleted=!0),t!==!1&&this.updateUnreadCache(),this.http["delete"](this.BASE_URL+"/feeds/"+e)},i.prototype.reversiblyDeleteFolder=function(e){var t=this,n=[];this.getByFolderId(e).forEach(function(e){n.push(t.reversiblyDelete(e.id,!1,!0))}),this.updateUnreadCache();var r=this.$q.all(n);return r.promise},i.prototype["delete"]=function(t,n){var r=this.get(t);return void 0!==r&&r.id&&delete this.ids[r.id],void 0!==r&&r.location&&delete this.locations[r.location],e.prototype["delete"].call(this,t),n!==!1&&(this.updateUnreadCache(),this.updateFolderCache()),r},i.prototype.deleteFolder=function(e){var t=this;this.getByFolderId(e).forEach(function(e){t["delete"](e.url,!1)}),this.updateUnreadCache(),this.updateFolderCache()},i.prototype.undoDelete=function(e,t){var n=this.getById(e);return n&&(n.deleted=!1),t!==!1&&this.updateUnreadCache(),this.http.post(this.BASE_URL+"/feeds/"+e+"/restore")},i.prototype.undoDeleteFolder=function(e){var t=this,n=[];this.getByFolderId(e).forEach(function(e){n.push(t.undoDelete(e.id,!1))}),this.updateUnreadCache();var r=this.$q.all(n);return r.promise},i.prototype.setOrdering=function(e,t){var n=this.getById(e);if(n){n.ordering=t;var r=this.BASE_URL+"/feeds/"+e;return this.http.patch(r,{ordering:t})}},i.prototype.setPinned=function(e,t){var n=this.getById(e);if(n){n.pinned=t;var r=this.BASE_URL+"/feeds/"+e;return this.http.patch(r,{pinned:t})}},i.prototype.patch=function(e,t){var n=this.getById(e);if(n){Object.keys(t).forEach(function(e){n[e]=t[e]});var r=this.BASE_URL+"/feeds/"+e;return this.http.patch(r,t)}},i.prototype.toggleFullText=function(e){var t=this.getById(e);return this.patch(e,{fullTextEnabled:!t.fullTextEnabled})},new i(t,n,r)}]),app.factory("FolderResource",["Resource","$http","BASE_URL","$q",function(e,t,n,r){"use strict";var i=function(t,n,r){e.call(this,t,n,"name"),this.deleted=null,this.$q=r,this.ids={}};return i.prototype=Object.create(e.prototype),i.prototype.add=function(t){e.prototype.add.call(this,t),void 0!==t.id&&(this.ids[t.id]=this.hashMap[t.name])},i.prototype.clear=function(){e.prototype.clear.call(this),this.ids={}},i.prototype["delete"]=function(t){var n=this.get(t);return void 0!==n&&n.id&&delete this.ids[n.id],e.prototype["delete"].call(this,t),n},i.prototype.toggleOpen=function(e){var t=this.get(e);return t.opened=!t.opened,this.http({url:this.BASE_URL+"/folders/"+t.id+"/open",method:"POST",data:{folderId:t.id,open:t.opened}})},i.prototype.rename=function(e,t){var n=this.get(e),r=this.$q.defer(),i=this;return this.http({url:this.BASE_URL+"/folders/"+n.id+"/rename",method:"POST",data:{folderName:t}}).success(function(){n.name=t,delete i.hashMap[e],i.hashMap[t]=n,r.resolve()}).error(function(e){r.reject(e.message)}),r.promise},i.prototype.getById=function(e){return this.ids[e]},i.prototype.create=function(e){e=e.trim();var t={name:e};this.add(t);var n=this.$q.defer();return this.http({url:this.BASE_URL+"/folders",method:"POST",data:{folderName:e}}).success(function(e){n.resolve(e)}).error(function(e){t.error=e.message}),n.promise},i.prototype.reversiblyDelete=function(e){var t=this.get(e),n=t.id;return t.deleted=!0,this.http["delete"](this.BASE_URL+"/folders/"+n)},i.prototype.undoDelete=function(e){var t=this.get(e),n=t.id;return t.deleted=!1,this.http.post(this.BASE_URL+"/folders/"+n+"/restore")},new i(t,n,r)}]),app.factory("ItemResource",["Resource","$http","BASE_URL","ITEM_BATCH_SIZE",function(e,t,n,r){"use strict";var i=function(t,n,r){e.call(this,t,n),this.batchSize=r,this.clear()};return i.prototype=Object.create(e.prototype),i.prototype.clear=function(){this.starredCount=0,this.lowestId=0,this.highestId=0,e.prototype.clear.call(this)},i.prototype.receive=function(t,n){switch(n){case"newestItemId":this.newestItemId=t;break;case"starred":this.starredCount=t;break;default:var r=this;t.forEach(function(e){0===r.lowestId&&(r.lowestId=e.id),0===r.highestId&&(r.highestId=e.id),e.id>r.highestId&&(r.highestId=e.id),e.id<r.lowestId&&(r.lowestId=e.id)}),e.prototype.receive.call(this,t,n)}},i.prototype.getNewestItemId=function(){return this.newestItemId},i.prototype.getStarredCount=function(){return this.starredCount},i.prototype.star=function(e,t){void 0===t&&(t=!0);var n=this.get(e),r=this.BASE_URL+"/items/"+n.feedId+"/"+n.guidHash+"/star";return n.starred=t,t?this.starredCount+=1:this.starredCount-=1,this.http({url:r,method:"POST",data:{isStarred:t}})},i.prototype.toggleStar=function(e){this.get(e).starred?this.star(e,!1):this.star(e,!0)},i.prototype.markItemRead=function(e,t){return void 0===t&&(t=!0),this.get(e).unread=!t,this.http({url:this.BASE_URL+"/items/"+e+"/read",method:"POST",data:{isRead:t}})},i.prototype.markItemsRead=function(e){var t=this;return e.forEach(function(e){t.get(e).unread=!1}),this.http({url:this.BASE_URL+"/items/read/multiple",method:"POST",data:{itemIds:e}})},i.prototype.markFeedRead=function(e,t){void 0===t&&(t=!0);var n=this.values.filter(function(t){return t.feedId===e});return n.forEach(function(e){e.unread=!t}),this.http.post(this.BASE_URL+"/feeds/"+e+"/read",{highestItemId:this.getNewestItemId()})},i.prototype.markRead=function(){return this.values.forEach(function(e){e.unread=!1}),this.http({url:this.BASE_URL+"/items/read",method:"POST",data:{highestItemId:this.getNewestItemId()}})},i.prototype.autoPage=function(e,t,n,r,i){var o;return o=n?this.highestId:this.lowestId,this.http({url:this.BASE_URL+"/items",method:"GET",params:{type:e,id:t,offset:o,limit:this.batchSize,oldestFirst:n,showAll:r,search:i}})},i.prototype.importArticles=function(e){return this.http({url:this.BASE_URL+"/feeds/import/articles",method:"POST",data:{json:e}})},new i(t,n,r)}]),app.service("Loading",function(){"use strict";this.loading={global:!1,content:!1,autopaging:!1},this.setLoading=function(e,t){this.loading[e]=t},this.isLoading=function(e){return this.loading[e]}}),app.service("OPMLImporter",["FeedResource","FolderResource","Publisher","$q",function(e,t,n,r){"use strict";var i=function(o){var s=r.defer();if(o.length>0){var a=o.pop(),u=a.url,d=a.title,c=0,l=a.folderName;if(void 0!==l&&void 0!==t.get(l)){var f=t.get(l);f.opened=!0,c=f.id,f.getsFeed=!0,f.getsFeedCounter=f.getsFeedCounter||0,f.getsFeedCounter+=1}void 0!==u&&void 0===e.get(u)&&e.create(u,c,d).then(function(e){n.publishAll(e)})["finally"](function(){0!==c&&(f.getsFeedCounter-=1,0===f.getsFeedCounter&&(f.getsFeed=!1)),i(o)})}else s.resolve();return s.promise};this.import |