first stab at recommended sites

This commit is contained in:
Bernhard Posselt 2014-11-19 15:54:32 +01:00
Родитель 62b6bd4850
Коммит 676b8dd187
28 изменённых файлов: 292 добавлений и 91 удалений

Просмотреть файл

@ -62,7 +62,7 @@ use \OCA\News\ArticleEnhancer\GlobalArticleEnhancer;
use \OCA\News\ArticleEnhancer\XPathArticleEnhancer;
use \OCA\News\ArticleEnhancer\RegexArticleEnhancer;
use \OCA\News\RecommendedSites\RecommendedSites;
use \OCA\News\Explore\RecommendedSites;
class Application extends App {
@ -514,7 +514,7 @@ class Application extends App {
});
$container->registerService('RecommendedSites', function($c) {
return new RecommendedSites(__DIR__ . '/../recommendedsites');
return new RecommendedSites(__DIR__ . '/../explore');
});
}

Просмотреть файл

@ -22,7 +22,7 @@ $application->registerRoutes($this, ['routes' => [
['name' => 'page#settings', 'url' => '/settings', 'verb' => 'GET'],
['name' => 'page#update_settings', 'url' => '/settings', 'verb' => 'PUT'],
['name' => 'page#manifest', 'url' => '/manifest.webapp', 'verb' => 'GET'],
['name' => 'page#recommended', 'url' => '/recommended', 'verb' => 'GET'],
['name' => 'page#explore', 'url' => '/explore', 'verb' => 'GET'],
// admin
['name' => 'admin#update', 'url' => '/admin', 'verb' => 'PUT'],

Просмотреть файл

@ -23,7 +23,8 @@ use \OCP\AppFramework\Controller;
use \OCA\News\Config\AppConfig;
use \OCA\News\Config\Config;
use \OCA\News\RecommendedSites\RecommendedSites;
use \OCA\News\Explore\RecommendedSites;
use \OCA\News\Db\FeedType;
class PageController extends Controller {
@ -174,10 +175,15 @@ class PageController extends Controller {
* @NoAdminRequired
* @NoCSRFRequired
*/
public function recommended() {
public function explore() {
$languageCode = $this->l10n->getLanguageCode();
$default = 'en';
$this->settings->setUserValue($this->userId, $this->appName,
'lastViewedFeedId', 0);
$this->settings->setUserValue($this->userId, $this->appName,
'lastViewedFeedType', FeedType::EXPLORE);
return $this->recommendedSites->forLanguage($languageCode, $default);
}

Просмотреть файл

@ -33,53 +33,6 @@
text-decoration: underline;
}
/**
* First run styles
*/
#first-run {
display: none;
height: 100%;
width: 100%;
}
.first-run #first-run {
display: block;
}
#first-run > div {
width: 100%;
height: 100%;
display: table;
}
#first-run > div > div {
display: table-row;
}
#first-run > div > div > * {
display: table-cell;
vertical-align: middle;
}
#first-run .title h1 {
color: #222;
font-size: 18px;
text-align: center;
}
#first-run .helper h1 {
color: #222;
font-size: 18px;
height: 44px;
padding-left: 45px;
background-position: 15px center;
background-image: url('../img/arrow.svg');
background-repeat: no-repeat;
}
.first-run #app-content-wrapper {
height: 100%;
}
/**
* Content styles
@ -93,7 +46,7 @@
display: none;
}
#app-content:not(.first-run):after {
#app-content:not(.explore):after {
content: '';
display: block;
height: 100%;

67
css/explore.css Normal file
Просмотреть файл

@ -0,0 +1,67 @@
/**
* ownCloud - News
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @copyright Bernhard Posselt 2014
*/
/**
* Explore styles
*/
.explore #app-content-wrapper {
height: 100%;
}
#explore {
height: 100%;
width: 100%;
}
#explore h1 {
font-size: 16pt;
padding: 30px 0;
text-align: center;
}
#explore .explore-section {
padding: 20px 45px;
}
#explore .explore-section h2 {
color: #333;
font-size: 14pt;
padding-bottom: 10px;
}
#explore .explore-section li {
padding: 30px;
margin: 10px;
display: inline-block;
vertical-align: top;
width: 300px;
background-color: #f5f5f5;
}
#explore .explore-section h3 {
line-height: 45px;
height: 45px;
font-size: 13pt;
background-repeat: no-repeat;
background-position: 0 center;
background-size: 24px 24px;
padding: 0 0 15px 35px;
}
#explore .explore-section img {
width: 100%;
margin-top: 25px;
}
#explore .explore-subscribe {
padding-top: 15px;
text-align: right;
}

2
css/news-owncloud7.min.css поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
css/news.min.css поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -20,4 +20,5 @@ class FeedType {
const STARRED = 2;
const SUBSCRIPTIONS = 3;
const SHARED = 4;
const EXPLORE = 5;
};

Просмотреть файл

@ -11,7 +11,7 @@
* @copyright Bernhard Posselt 2012, 2014
*/
namespace OCA\News\RecommendedSites;
namespace OCA\News\Explore;
class RecommendedSites {
@ -33,7 +33,12 @@ class RecommendedSites {
$file = $this->directory . '/sites.' . $default . '.json';
}
return json_decode(file_get_contents($file), true);
if (file_exists($file)) {
return json_decode(file_get_contents($file), true);
} else {
return [];
}
}

Просмотреть файл

@ -5,6 +5,7 @@
"favicon": "http://owncloud.org/wp-content/themes/owncloudorgnew/assets/img/common/favicon.png",
"url": "http://owncloud.org/news/",
"description": "ownCloud Planet is a blog feed aggregator",
"image": "http://tuxanddroid.de/wp-content/uploads/2013/08/owncloud_logo.png",
"votes": 1000
},
{

Просмотреть файл

@ -77,7 +77,8 @@ module.exports = function (grunt) {
'../css/mobile.css',
'../css/shortcuts.css',
'../css/navigation.css',
'../css/settings.css'
'../css/settings.css',
'../css/explore.css'
]}
},
news: {
@ -88,7 +89,8 @@ module.exports = function (grunt) {
'../css/shortcuts.css',
'../css/mobile.css',
'../css/navigation.css',
'../css/settings.css'
'../css/settings.css',
'../css/explore.css'
]}
}
},

Просмотреть файл

@ -15,7 +15,8 @@ app.config(function ($routeProvider, $provide, $httpProvider) {
FOLDER: 1,
STARRED: 2,
SUBSCRIPTIONS: 3,
SHARED: 4
SHARED: 4,
EXPLORE: 5
};
// constants
@ -97,6 +98,15 @@ app.config(function ($routeProvider, $provide, $httpProvider) {
templateUrl: 'content.html',
resolve: getResolve(feedType.FOLDER),
type: feedType.FOLDER
}).when('/explore', {
controller: 'ExploreController as Explore',
templateUrl: 'explore.html',
resolve: {
sites: /* @ngInject */ function ($http, BASE_URL) {
return $http.get(BASE_URL + '/explore');
}
},
type: feedType.EXPLORE
}).when('/shortcuts', {
templateUrl: 'shortcuts.html',
type: -1

Просмотреть файл

@ -49,6 +49,10 @@ app.run(function ($rootScope, $location, $http, $q, $interval, Loading,
url = '/items/starred';
break;
case FEED_TYPE.EXPLORE:
url = '/explore';
break;
default:
url = '/items';
}

Просмотреть файл

@ -13,7 +13,8 @@ app.config(["$routeProvider", "$provide", "$httpProvider", function ($routeProvi
FOLDER: 1,
STARRED: 2,
SUBSCRIPTIONS: 3,
SHARED: 4
SHARED: 4,
EXPLORE: 5
};
// constants
@ -95,6 +96,15 @@ app.config(["$routeProvider", "$provide", "$httpProvider", function ($routeProvi
templateUrl: 'content.html',
resolve: getResolve(feedType.FOLDER),
type: feedType.FOLDER
}).when('/explore', {
controller: 'ExploreController as Explore',
templateUrl: 'explore.html',
resolve: {
sites: /* @ngInject */ ["$http", "BASE_URL", function ($http, BASE_URL) {
return $http.get(BASE_URL + '/explore');
}]
},
type: feedType.EXPLORE
}).when('/shortcuts', {
templateUrl: 'shortcuts.html',
type: -1
@ -145,6 +155,10 @@ app.run(["$rootScope", "$location", "$http", "$q", "$interval", "Loading", "Item
url = '/items/starred';
break;
case FEED_TYPE.EXPLORE:
url = '/explore';
break;
default:
url = '/items';
}
@ -230,10 +244,14 @@ app.controller('AppController',
}]);
app.controller('ContentController',
["Publisher", "FeedResource", "ItemResource", "SettingsResource", "data", "$route", "$routeParams", "FEED_TYPE", function (Publisher, FeedResource, ItemResource, SettingsResource, data,
$route, $routeParams, FEED_TYPE) {
["Publisher", "FeedResource", "ItemResource", "SettingsResource", "data", "$route", "$routeParams", "FEED_TYPE", "$location", "FolderResource", function (Publisher, FeedResource, ItemResource, SettingsResource, data,
$route, $routeParams, FEED_TYPE, $location, FolderResource) {
'use strict';
if (FeedResource.size() === 0 && FolderResource.size() === 0) {
$location.path('/explore');
}
// dont cache items across multiple route changes
ItemResource.clear();
@ -369,6 +387,12 @@ app.controller('ContentController',
$route.reload();
};
}]);
app.controller('ExploreController', ["sites", function (sites) {
'use strict';
this.sites = sites.data;
}]);
app.controller('NavigationController',
["$route", "FEED_TYPE", "FeedResource", "FolderResource", "ItemResource", "SettingsResource", "Publisher", "$rootScope", "$location", "$q", function ($route, FEED_TYPE, FeedResource, FolderResource, ItemResource,
@ -475,6 +499,11 @@ app.controller('NavigationController',
$route.current.$$route.type === FEED_TYPE.STARRED;
};
this.isExploreActive = function () {
return $route.current &&
$route.current.$$route.type === FEED_TYPE.EXPLORE;
};
this.isFolderActive = function (folderId) {
return $route.current &&
$route.current.$$route.type === FEED_TYPE.FOLDER &&

2
js/build/app.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -9,9 +9,13 @@
*/
app.controller('ContentController',
function (Publisher, FeedResource, ItemResource, SettingsResource, data,
$route, $routeParams, FEED_TYPE) {
$route, $routeParams, FEED_TYPE, $location, FolderResource) {
'use strict';
if (FeedResource.size() === 0 && FolderResource.size() === 0) {
$location.path('/explore');
}
// dont cache items across multiple route changes
ItemResource.clear();

Просмотреть файл

@ -0,0 +1,15 @@
/**
* ownCloud - News
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @copyright Bernhard Posselt 2014
*/
app.controller('ExploreController', function (sites) {
'use strict';
this.sites = sites.data;
});

Просмотреть файл

@ -112,6 +112,11 @@ function ($route, FEED_TYPE, FeedResource, FolderResource, ItemResource,
$route.current.$$route.type === FEED_TYPE.STARRED;
};
this.isExploreActive = function () {
return $route.current &&
$route.current.$$route.type === FEED_TYPE.EXPLORE;
};
this.isFolderActive = function (folderId) {
return $route.current &&
$route.current.$$route.type === FEED_TYPE.FOLDER &&

Просмотреть файл

@ -10,14 +10,21 @@
describe('AppController', function () {
'use strict';
var controller;
var controller,
location;
beforeEach(module('News', function ($provide) {
$provide.value('BASE_URL', 'base');
}));
beforeEach(inject(function ($controller) {
controller = $controller('AppController');
location = {
path: jasmine.createSpy('path')
};
controller = $controller('AppController', {
$location: location
});
}));
@ -43,6 +50,7 @@ describe('AppController', function () {
FolderResource.add({name: 'test'});
expect(controller.isFirstRun()).toBe(false);
expect(location.path).not.toHaveBeenCalled();
}));
});

Просмотреть файл

@ -481,4 +481,47 @@ describe('ContentController', function () {
}));
it('should redirect to the explore page if there are no feeds and folders',
inject(function ($controller) {
var location = {
path: jasmine.createSpy('reload')
};
$controller('ContentController', {
data: {},
$location: location
});
expect(location.path).toHaveBeenCalledWith('/explore');
}));
it('should not redirect to the explore page if there are feeds and folders',
inject(function ($controller, FolderResource, FeedResource) {
FolderResource.add({id: 3, name: 'test'});
var location = {
path: jasmine.createSpy('reload')
};
$controller('ContentController', {
data: {},
$location: location
});
expect(location.path).not.toHaveBeenCalledWith('/explore');
FolderResource.clear({id: 3, name: 'test'});
FeedResource.add({id: 3, url: 'test'});
location = {
path: jasmine.createSpy('reload')
};
$controller('ContentController', {
data: {},
$location: location
});
expect(location.path).not.toHaveBeenCalledWith('/explore');
}));
});

Просмотреть файл

@ -225,7 +225,7 @@ describe('NavigationController', function () {
}));
it('should check if a starred is active', inject(function (FeedResource,
it('should check if starred is active', inject(function (FeedResource,
FEED_TYPE, $controller) {
var ctrl = $controller('NavigationController', {
FeedResource: FeedResource,
@ -242,6 +242,22 @@ describe('NavigationController', function () {
}));
it('should check if explore is active', inject(function (FeedResource,
FEED_TYPE, $controller) {
var ctrl = $controller('NavigationController', {
FeedResource: FeedResource,
$route: {
current: {
$$route: {
type: FEED_TYPE.EXPLORE
}
}
}
});
expect(ctrl.isExploreActive()).toBe(true);
}));
it('should check if a feed is active', inject(function (FeedResource,
FEED_TYPE, $controller) {

Просмотреть файл

@ -20,7 +20,8 @@ if (defined('DEBUG') && DEBUG === true) {
'mobile',
'navigation',
'settings',
'shortcuts'
'shortcuts',
'explore'
]);
script('news', 'build/app');
@ -55,6 +56,7 @@ if (defined('DEBUG') && DEBUG === true) {
<ul class="with-icon" data-id="0" news-droppable>
<?php print_unescaped($this->inc('part.navigation.addfeed')) ?>
<?php print_unescaped($this->inc('part.navigation.addfolder')) ?>
<?php print_unescaped($this->inc('part.navigation.explore')) ?>
<?php print_unescaped($this->inc('part.navigation.unreadfeed')) ?>
<?php print_unescaped($this->inc('part.navigation.starredfeed')) ?>
<?php print_unescaped($this->inc(
@ -76,12 +78,15 @@ if (defined('DEBUG') && DEBUG === true) {
<script type="text/ng-template" id="shortcuts.html">
<?php print_unescaped($this->inc('part.content.shortcuts')) ?>
</script>
<script type="text/ng-template" id="explore.html">
<?php print_unescaped($this->inc('part.content.explore')) ?>
</script>
<div id="app-content"
ng-class="{
'loading-content': App.loading.isLoading('content') &&
!App.loading.isLoading('global'),
'first-run': App.isFirstRun()
'explore': App.isFirstRun()
}"
tabindex="-1"
news-pull-to-refresh="showPullToRefresh">

Просмотреть файл

@ -0,0 +1,23 @@
<?php print_unescaped($this->inc('part.content.cronwarning')) ?>
<div id="explore">
<div ng-repeat="(category, data) in Explore.sites" class="explore-section">
<h2>{{ category }}</h2>
<ul>
<li ng-repeat="entry in data | orderBy:votes:true">
<h3 ng-style="{ backgroundImage: 'url(' + entry.favicon + ')'}">
{{ entry.title }}
</h3>
<div>
{{ entry.description }}
<img ng-src="{{ entry.image }}" ng-if="entry.image">
</div>
<div class="explore-subscribe">
<button><?php p($l->t('Subscribe')) ?></button>
</div>
</li>
</ul>
</div>
</div>

Просмотреть файл

@ -1,17 +0,0 @@
<?php if ($_['cronWarning'] !== 'ajaxCron') { ?>
<div id="first-run">
<div>
<div class="helper"><h1><?php p($l->t('Add a feed')) ?></h1></div>
<div class="title">
<h1>
<?php
p($l->t('There are no feeds yet. Go ahead and add some'))
?> :)
</h1>
</div>
<div class="helper">
<h1><?php p($l->t('Import feeds and articles')) ?></h1>
</div>
</div>
</div>
<?php }; ?>

Просмотреть файл

@ -1,5 +1,4 @@
<?php print_unescaped($this->inc('part.content.cronwarning')) ?>
<?php print_unescaped($this->inc('part.content.firstrun')) ?>
<div news-auto-focus="#app-content"
id="articles"

Просмотреть файл

@ -1,3 +1,5 @@
<?php print_unescaped($this->inc('part.content.cronwarning')) ?>
<div id="app-shortcuts">
<div>
<table>

Просмотреть файл

@ -0,0 +1,5 @@
<li ng-class="{active: Navigation.isExploreActive()}">
<a class="icon-link" ng-href="#/explore/">
<?php p($l->t('Explore')) ?>
</a>
</li>

Просмотреть файл

@ -13,6 +13,8 @@
namespace OCA\News\Controller;
use \OCA\News\Db\FeedType;
class PageControllerTest extends \PHPUnit_Framework_TestCase {
@ -69,7 +71,7 @@ class PageControllerTest extends \PHPUnit_Framework_TestCase {
->disableOriginalConstructor()
->getMock();
$this->recommended = $this->getMockBuilder(
'\OCA\News\RecommendedSites\RecommendedSites')
'\OCA\News\Explore\RecommendedSites')
->disableOriginalConstructor()
->getMock();
$this->controller = new PageController($this->appName, $this->request,
@ -229,8 +231,21 @@ class PageControllerTest extends \PHPUnit_Framework_TestCase {
}
public function testRecommended(){
public function testExplore(){
$in = 'test';
$this->settings->expects($this->at(0))
->method('setUserValue')
->with($this->equalTo($this->user),
$this->equalTo($this->appName),
$this->equalTo('lastViewedFeedId'),
$this->equalTo(0));
$this->settings->expects($this->at(1))
->method('setUserValue')
->with($this->equalTo($this->user),
$this->equalTo($this->appName),
$this->equalTo('lastViewedFeedType'),
$this->equalTo(FeedType::EXPLORE));
$this->l10n->expects($this->once())
->method('getLanguageCode')
->will($this->returnValue('de_DE'));
@ -241,7 +256,7 @@ class PageControllerTest extends \PHPUnit_Framework_TestCase {
->will($this->returnValue($in));
$out = $this->controller->recommended();
$out = $this->controller->explore();
$this->assertEquals($in, $out);
}