working on search, API, RSS feeds, etc.

This commit is contained in:
Sacha Greif 2015-09-18 11:12:53 +09:00
parent 19b561df20
commit 5df17c6305
20 changed files with 184 additions and 258 deletions

View file

@ -157,6 +157,7 @@ useraccounts:core@1.12.3
useraccounts:flow-routing@1.12.3
useraccounts:unstyled@1.11.1
utilities:avatar@0.9.0
utilities:onsubscribed@0.1.0
webapp@1.2.0
webapp-hashing@1.0.3
zimme:active-route@2.3.2

View file

@ -9,6 +9,8 @@
* Removed `Telescope.utils.getCurrentTemplate()`;
*
* RSS feed and API can now both accept any post query parameter (`limit`, `view`, `cat`, `before`, `after`, etc.)
## v0.24 “SubScope2”
* [BREAKING] Modules data context must now be passed on explicitely using the `moduleData` attribute.

View file

@ -1,10 +1,11 @@
serveAPI = function(limitSegment){
serveAPI = function(terms){
var posts = [];
var limit = isNaN(limitSegment) ? 20 : limitSegment; // default limit: 20 posts
Posts.find({status: Posts.config.STATUS_APPROVED}, {sort: {postedAt: -1}, limit: limit}).forEach(function(post) {
var parameters = Posts.parameters.get(terms);
Posts.find(parameters.find, parameters.options).forEach(function(post) {
var url = Posts.getLink(post);
var properties = {
var postOutput = {
title: post.title,
headline: post.title, // for backwards compatibility
author: post.author,
@ -15,18 +16,18 @@ serveAPI = function(limitSegment){
};
if(post.body)
properties.body = post.body;
postOutput.body = post.body;
if(post.url)
properties.domain = Telescope.utils.getDomain(url);
postOutput.domain = Telescope.utils.getDomain(url);
if (post.thumbnailUrl) {
properties.thumbnailUrl = Telescope.utils.addHttp(post.thumbnailUrl);
postOutput.thumbnailUrl = Telescope.utils.addHttp(post.thumbnailUrl);
}
var twitterName = Users.getTwitterNameById(post.userId);
if(twitterName)
properties.twitterName = twitterName;
postOutput.twitterName = twitterName;
var comments = [];
@ -60,9 +61,9 @@ serveAPI = function(limitSegment){
comments.splice(index,1);
});
properties.comments = comments;
postOutput.comments = comments;
posts.push(properties);
posts.push(postOutput);
});
return JSON.stringify(posts);

View file

@ -1,13 +1,7 @@
// Meteor.startup(function () {
// Router.route('api', {
// where: 'server',
// path: '/api/:limit?',
// action: function() {
// var limit = parseInt(this.params.limit);
// this.response.write(serveAPI(limit));
// this.response.end();
// }
// });
// });
// for backwards compatibility's sake, accept a "limit" segment
Picker.route('/api/:limit?', function(params, req, res, next) {
if (typeof params.limit !== "undefined") {
params.query.limit = params.limit;
}
res.end(serveAPI(params.query));
});

View file

@ -34,6 +34,7 @@ Package.onUse(function (api) {
'kadira:flow-router@2.6.0',
'kadira:blaze-layout@2.1.0',
'arillo:flow-router-helpers@0.4.5',
'meteorhacks:picker@1.0.3',
'dburles:collection-helpers@1.0.3',
// 'meteorhacks:flow-router@1.5.0',
// 'meteorhacks:flow-layout@1.1.1',
@ -64,6 +65,7 @@ Package.onUse(function (api) {
// 'utilities:state-transitions@0.1.0',
'tmeasday:publish-counts@0.7.1',
// 'dburles:iron-router-query-array@1.0.1'
'utilities:onsubscribed@0.1.0'
];
api.use(packages);

View file

@ -5,13 +5,16 @@
.top-nav{
.desktop-nav{
.pages-menu{
a{
font-weight: normal;
.pages-menu-item{
display: inline-block;
margin-right: 20px;
&:last-of-type{
margin: 0;
}
}
a{
font-weight: normal;
}
}
}
}
@ -19,8 +22,7 @@
.side-nav{
.pages-menu{
order: 2;
a{
display: block;
.pages-menu-item{
margin-bottom: 10px;
&:last-child{
margin: 0;
@ -31,7 +33,7 @@
.mobile-nav{
.pages-menu{
a{
.pages-menu-item{
border-bottom:1px white(0.2) solid;
margin: 0;
&:last-of-type{

View file

@ -2,7 +2,9 @@
{{#if hasPages}}
<div class="pages-menu {{moduleClass}}">
{{#each pages}}
<div class="pages-menu-item">
<a href="{{pathFor 'page' slug=this.slug}}">{{title}}</a>
</div>
{{/each}}
</div>
{{/if}}

View file

@ -4,6 +4,10 @@ Template.feeds.onCreated(function () {
template.subscribe('allUsersAdmin');
});
Template.feeds.onSubscribed(function () {
console.log("subscription done!");
});
Template.feeds.helpers({
feeds: function(){
return Feeds.find({}, {sort: {url: 1}});

View file

@ -1,63 +1,26 @@
// Meteor.startup(function () {
Picker.route('/feed.xml', function(params, req, res, next) {
if (typeof params.query.view === "undefined") {
params.query.view = 'new';
}
res.end(servePostRSS(params.query, 'feed.xml'));
});
// // New Post RSS
Picker.route('/rss/posts/new.xml', function(params, req, res, next) {
res.end(servePostRSS({view: 'new'}, '/rss/posts/new.xml'));
});
// Router.route('/feed.xml', function () {
// this.response.write(servePostRSS('new', 'feed.xml'));
// this.response.end();
// }, {
// name: 'feed',
// where: 'server'
// });
Picker.route('/rss/posts/top.xml', function(params, req, res, next) {
res.end(servePostRSS({view: 'top'}, '/rss/posts/top.xml'));
});
// // New Post RSS
Picker.route('/rss/posts/best.xml', function(params, req, res, next) {
res.end(servePostRSS({view: 'best'}, '/rss/posts/best.xml'));
});
// Router.route('/rss/posts/new.xml', function () {
// this.response.write(servePostRSS('top', 'rss/posts/new.xml'));
// this.response.end();
// }, {
// name: 'rss_posts_new',
// where: 'server'
// });
Picker.route('/rss/category/:slug/feed.xml', function(params, req, res, next) {
res.end(servePostRSS({view: 'new', cat: params.slug}, '/rss/category/:slug/feed.xml'));
});
// // Categories RSS
// Router.route('/rss/category/:slug/feed.xml', function () {
// this.response.write(servePostRSS('new', '/rss/category/:slug/feed.xml',this.params.slug));
// this.response.end();
// }, {
// name: 'rss_posts_category',
// where: 'server'
// });
// // Top Post RSS
// Router.route('/rss/posts/top.xml', function () {
// this.response.write(servePostRSS('top', 'rss/posts/top.xml'));
// this.response.end();
// }, {
// name: 'rss_posts_top',
// where: 'server'
// });
// // Best Post RSS
// Router.route('/rss/posts/best.xml', function () {
// this.response.write(servePostRSS('best', 'rss/posts/best.xml'));
// this.response.end();
// }, {
// name: 'rss_posts_best',
// where: 'server'
// });
// // Comment RSS
// Router.route('/rss/comments.xml', function() {
// this.response.write(serveCommentRSS());
// this.response.end();
// }, {
// name: 'rss_comments',
// where: 'server'
// });
// });
Picker.route('/rss/comments.xml', function(params, req, res, next) {
res.end(serveCommentRSS());
});

View file

@ -11,13 +11,9 @@ getMeta = function(url) {
};
};
servePostRSS = function(view, url, category) {
servePostRSS = function(terms, url) {
var feed = new RSS(getMeta(url));
var terms = {view: view, limit: 20};
if (category) {
terms.category = category;
};
var params = Posts.parameters.get(terms);
delete params['options']['sort']['sticky'];

View file

@ -1,74 +0,0 @@
// Meteor.startup(function () {
// Posts.controllers.search = Posts.controllers.list.extend({
// view: 'search',
// showViewsNav: false,
// getTitle: function() {
// return i18n.t("Search") + ' - ' + Settings.get('title', "Telescope");
// },
// getDescription: function() {
// return Settings.get('description');
// },
// onBeforeAction: function() {
// var query = this.params.query;
// if ('q' in query) {
// // if search box has 'empty' class, that means user just deleted last character in search keyword
// // but router hasn't updated url, so params.query still has '?q=<LAST CHARACTER>'
// // if we set searchQuery in this case, user will see last character pops up again unexpectedly
// // so add this check to fix the bug. issue #825
// if (!$('.search').hasClass('empty')) {
// Session.set('searchQuery', query.q);
// }
// if (query.q) {
// Meteor.call('logSearch', query.q);
// }
// }
// this.next();
// },
// data: function () {
// var terms = {
// view: "search",
// limit: this.params.query.limit || Settings.get('postsPerPage', 10),
// query: this.params.query.q
// };
// return {searchQuery: this.params.query.q, terms: terms};
// }
// });
// Router.onBeforeAction(Router._filters.isAdmin, {only: ['logs']});
// // Search
// Router.route('/search', {
// name: 'search',
// controller: Posts.controllers.search
// });
// // Search Logs
// Router.route('/logs/:limit?', {
// controller: Telescope.controllers.admin,
// name: 'searchLogs',
// template: 'search_logs',
// waitOn: function () {
// var limit = this.params.limit || 100;
// if(Meteor.isClient) {
// Session.set('logsLimit', limit);
// }
// return Meteor.subscribe('searches', limit);
// },
// data: function () {
// return Searches.find({}, {sort: {timestamp: -1}});
// },
// fastRender: true
// });
// });

View file

@ -7,46 +7,44 @@ var delay = (function(){
};
})();
Meteor.startup(function () {
Template.search.helpers({
Template.search.helpers({
canSearch: function () {
return Users.can.view(Meteor.user());
},
searchQuery: function () {
return this.searchQuery;
return FlowRouter.getQueryParam("query");
},
searchQueryEmpty: function () {
return this.searchQuery ? "" : "empty";
return !!FlowRouter.getQueryParam("query") ? "" : "empty";
}
});
});
Template.search.events({
Template.search.events({
'keyup .search-field': function (e) {
e.preventDefault();
var val = $(e.target).val(),
$search = $('.search');
// if we're not on search route, go to it
if (FlowRouter.getRouteName() !== "postsDefault") {
FlowRouter.go("postsDefault");
}
if (val === '') {
// if search field is empty, just do nothing and show an empty template
// if search field is empty
$search.addClass('empty');
// Router.go('search', null, {replaceState: true});
val = null;
} else {
$search.removeClass('empty');
// if search field is not empty, add a delay to avoid firing new searches for every keystroke
}
delay(function(){
// Update the querystring.
var opts = {query: {q: val}};
// if we're already on the search page, do a replaceState. Otherwise,
// just use the pushState default.
if(Router.current().route.getName() === 'search') {
opts.replaceState = true;
}
Router.go('search', null, opts);
FlowRouter.setQueryParams({query: val});
}, 700 );
}
}
});
}
});

View file

@ -1,5 +1,6 @@
<template name="search_logs">
<h2>Search Logs</h2>
{{# loader ready=Template.subscriptionsReady}}
<table>
<thead>
<tr>
@ -8,7 +9,7 @@
</tr>
</thead>
<tbody>
{{#each this}}
{{#each searches}}
{{#if isNewDate}}
<tr class='search-date-header'>
<th colspan="2">
@ -24,7 +25,5 @@
{{/each}}
</tbody>
</table>
<div class="grid more-button">
<a class="more-link" href="{{loadMoreUrl}}">{{_ "load_more"}}</a>
</div>
{{/loader}}
</template>

View file

@ -1,5 +1,12 @@
Meteor.startup(function () {
Template.search_logs.helpers({
Template.search_logs.onCreated(function () {
var template = this;
template.subscribe('searches', 100);
});
Template.search_logs.helpers({
searches: function () {
return Searches.find({}, {sort: {timestamp: -1}});
},
getTime: function () {
return moment(this.timestamp).format("HH:mm:ss");
},
@ -26,5 +33,4 @@ Meteor.startup(function () {
var count = parseInt(Session.get('logsLimit')) + 100;
return '/logs/' + count;
},
});
});

View file

@ -0,0 +1,15 @@
function addSearchQueryParameter (parameters, terms) {
if(!!terms.query) {
var parameters = Telescope.utils.deepExtend(true, parameters, {
find: {
$or: [
{title: {$regex: terms.query, $options: 'i'}},
{url: {$regex: terms.query, $options: 'i'}},
{body: {$regex: terms.query, $options: 'i'}}
]
}
});
}
return parameters;
}
Telescope.callbacks.add("postsParameters", addSearchQueryParameter);

View file

@ -0,0 +1,14 @@
Telescope.menuItems.add("adminMenu", [
{
route: 'adminSearchLogs',
label: 'searchLogs',
description: 'telescope_settings_panel'
}
]);
Telescope.adminRoutes.route('/search-logs', {
name: "adminSearchLogs",
action: function(params, queryParams) {
BlazeLayout.render("layout", {main: "admin_wrapper", admin: "search_logs"});
}
});

View file

@ -13,11 +13,12 @@ Package.onUse(function (api) {
api.addFiles([
'lib/search.js',
'lib/routes.js',
'lib/parameters.js',
'package-tap.i18n'
], ['client', 'server']);
api.addFiles([
'lib/client/routes.js',
'lib/client/templates/search.html',
'lib/client/templates/search.js',
'lib/client/templates/search_logs.html',

View file

@ -1,6 +1,6 @@
Telescope.menuItems.add("adminMenu", [
{
route: 'settings',
route: 'adminSettings',
label: 'settings',
description: 'telescope_settings_panel'
}

View file

@ -15,7 +15,7 @@ Package.onUse(function (api) {
'lib/categories.js',
'lib/helpers.js',
'lib/callbacks.js',
'lib/views.js',
'lib/parameters.js',
'lib/custom_fields.js',
'lib/methods.js',
'lib/modules.js',