Merged in feature_public_feed (pull request #16)

Public feed implemented.
This commit is contained in:
Alexey Eschenko 2017-11-04 21:40:35 +00:00
commit b8668c66ce
12 changed files with 157 additions and 64 deletions

View file

@ -25,6 +25,7 @@
{% block header_navbar_menus %} {% block header_navbar_menus %}
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{{ path('index') }}"><span class="glyphicon glyphicon-home"></span> {{ 'Main'|trans }}</a></li> <li><a href="{{ path('index') }}"><span class="glyphicon glyphicon-home"></span> {{ 'Main'|trans }}</a></li>
<li><a href="{{ path('feed_public') }}"><span class="glyphicon glyphicon-bullhorn"></span> {{ 'Public feed'|trans }}</a></li>
<li><a href="{{ path('statistics') }}"><span class="glyphicon glyphicon-stats"></span> {{ 'Statistics'|trans }}</a></li> <li><a href="{{ path('statistics') }}"><span class="glyphicon glyphicon-stats"></span> {{ 'Statistics'|trans }}</a></li>
<li><a href="{{ path('events_last') }}"><span class="glyphicon glyphicon-th-list"></span> {{ 'Last'|trans }}</a></li> <li><a href="{{ path('events_last') }}"><span class="glyphicon glyphicon-th-list"></span> {{ 'Last'|trans }}</a></li>
</ul> </ul>

View file

@ -0,0 +1,37 @@
<?php
namespace Skobkin\Bundle\PointToolsBundle\Controller;
use Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class PublicFeedController extends Controller
{
private const POSTS_PER_PAGE = 20;
public function indexAction(Request $request)
{
// @todo autowire
$postRepository = $this->getDoctrine()->getRepository(Post::class);
$paginator = $this->get('knp_paginator');
$postsPagination = $paginator->paginate(
$postRepository->createPublicFeedPostsQuery(),
$request->query->getInt('page', 1),
self::POSTS_PER_PAGE
);
return $this->render(
'SkobkinPointToolsBundle:Post:feed.html.twig',
[
// @todo Move to translation
'feed_title' => 'All',
'posts' => $postsPagination,
// Special feed mark (to not show comments and other)
'is_feed' => true,
]
);
}
}

View file

@ -3,13 +3,11 @@
namespace Skobkin\Bundle\PointToolsBundle\Controller; namespace Skobkin\Bundle\PointToolsBundle\Controller;
use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManager;
use Skobkin\Bundle\PointToolsBundle\DTO\DailyEvents; use Skobkin\Bundle\PointToolsBundle\DTO\{DailyEvents, TopUserDTO};
use Skobkin\Bundle\PointToolsBundle\DTO\TopUserDTO;
use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Ob\HighchartsBundle\Highcharts\Highchart; use Ob\HighchartsBundle\Highcharts\Highchart;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\{Request, Response};
use Symfony\Component\HttpFoundation\Response;
class UserController extends Controller class UserController extends Controller
{ {

View file

@ -89,7 +89,7 @@ class Post
private $files; private $files;
/** /**
* @var Tag[]|ArrayCollection * @var PostTag[]|ArrayCollection
* *
* @ORM\OneToMany(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag", mappedBy="post", fetch="EXTRA_LAZY", cascade={"persist"}, orphanRemoval=true) * @ORM\OneToMany(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\PostTag", mappedBy="post", fetch="EXTRA_LAZY", cascade={"persist"}, orphanRemoval=true)
*/ */

View file

@ -17,6 +17,7 @@ class PostRepository extends EntityRepository
{ {
/** @var QueryBuilder $qb */ /** @var QueryBuilder $qb */
$qb = $this->createQueryBuilder('p'); $qb = $this->createQueryBuilder('p');
return $qb return $qb
->select(['p', 'c', 'a']) ->select(['p', 'c', 'a'])
->leftJoin('p.comments', 'c') ->leftJoin('p.comments', 'c')
@ -28,4 +29,19 @@ class PostRepository extends EntityRepository
->getQuery()->getOneOrNullResult() ->getQuery()->getOneOrNullResult()
; ;
} }
public function createPublicFeedPostsQuery(): QueryBuilder
{
$qb = $this->createQueryBuilder('p');
return $qb
// @todo optimize hydration
->select(['p', 'pa', 'pt', 'pf'])
->innerJoin('p.author', 'pa')
->leftJoin('p.postTags', 'pt')
->leftJoin('p.files', 'pf')
->where('p.private = FALSE')
->andWhere('pa.public = TRUE')
;
}
} }

View file

@ -36,6 +36,11 @@ events_last:
defaults: { _controller: SkobkinPointToolsBundle:Events:last } defaults: { _controller: SkobkinPointToolsBundle:Events:last }
methods: [GET] methods: [GET]
feed_public:
path: /posts/all
defaults: { _controller: SkobkinPointToolsBundle:PublicFeed:index }
methods: [GET]
post_show: post_show:
path: /{id} path: /{id}
defaults: { _controller: SkobkinPointToolsBundle:Post:show } defaults: { _controller: SkobkinPointToolsBundle:Post:show }

View file

@ -3,6 +3,7 @@
# Header # Header
Toggle navigation: Переключить навигацию Toggle navigation: Переключить навигацию
Main: Главная Main: Главная
Public feed: Публичная лента
Statistics: Статистика Statistics: Статистика
Report a bug: Сообщить об ошибке Report a bug: Сообщить об ошибке
Telegram Bot: Бот в Telegram Telegram Bot: Бот в Telegram

View file

@ -0,0 +1,11 @@
{% extends "::base.html.twig" %}
{% block css %}
{{ parent() }}
<link href="{{ asset('css/lib/magnific-popup.css') }}" rel="stylesheet">
{% endblock %}
{% block footer_js %}
{{ parent() }}
<script src="{{ asset('js/lib/jquery.magnific-popup.min.js') }}"></script>
<script src="{{ asset('js/popups.js') }}"></script>
{% endblock %}

View file

@ -0,0 +1,26 @@
{% extends 'SkobkinPointToolsBundle:Post:base_feed.html.twig' %}
{% block header_title %}{{ feed_title }} @ Point Tools{% endblock %}
{% block content %}
{% if is_feed is defined %}
<div class="navigation">
{{ knp_pagination_render(posts) }}
</div>
{% endif %}
{% for post in posts %}
<div class="feed-post panel panel-default">
{% include 'SkobkinPointToolsBundle:Post:post.html.twig' with {
'post': post,
'is_feed': is_feed
} %}
</div>
{% endfor %}
{% if is_feed is defined %}
<div class="navigation">
{{ knp_pagination_render(posts) }}
</div>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,47 @@
<div class="container post-block">
<div class="row">
<div class="col-xs-1">
<img src="{{ point_avatar_large(post.author.login) }}" alt="Avatar">
</div>
<div class="col-xs-11">
<div><a href="{{ path('user_show', {'login': post.author.login}) }}">@{{ post.author.login }}</a></div>
<div class="post-date">{{ post.createdAt|date('j M Y G:i') }}</div>
<div>
{% for pt in post.postTags %}
<span class="tag">{{ pt.text }}</span>
{% endfor %}
</div>
</div>
</div>
<div class="row post-text">
<div class="col-xs-12">
{{ post.text|markdown('app.point.markdown_parser') }}
</div>
</div>
<div class="row post-files">
<div class="col-xs-2">
{% for file in post.files %}
<div class="post-attachment">
<a href="{{ file.remoteUrl }}" class="post-image"><img src="{{ file.remoteUrl }}" class="img-thumbnail" /></a>
</div>
{% endfor %}
</div>
</div>
<div class="row">
<div class="col-xs-12"><a href="{{ post.id|point_post_url }}" class="post">#{{ post.id }}</a></div>
</div>
{% if is_feed is defined and post.comments|length > 0 %}
<div class="row comments">
{#
{% include '@SkobkinPointTools/Post/comments_tree.html.twig' with {
'comments': post.firstLevelComments
} only %}
#}
{% include 'SkobkinPointToolsBundle:Post:comments_list.html.twig' with {
'comments': post.comments
} %}
</div>
{% endif %}
</div>

View file

@ -1,63 +1,7 @@
{% extends "::base.html.twig" %} {% extends 'SkobkinPointToolsBundle:Post:base_feed.html.twig' %}
{% block css %}
{{ parent() }}
<link href="{{ asset('css/lib/magnific-popup.css') }}" rel="stylesheet">
{% endblock %}
{% block footer_js %}
{{ parent() }}
<script src="{{ asset('js/lib/jquery.magnific-popup.min.js') }}"></script>
<script src="{{ asset('js/popups.js') }}"></script>
{% endblock %}
{% block header_title %}#{{ post.id }} @ Point Tools{% endblock %} {% block header_title %}#{{ post.id }} @ Point Tools{% endblock %}
{% block content %} {% block content %}
<div class="container post-block"> {% include 'SkobkinPointToolsBundle:Post:post.html.twig' with {'post': post} %}
<div class="row">
<div class="col-xs-1">
<img src="{{ point_avatar_large(post.author.login) }}" alt="Avatar">
</div>
<div class="col-xs-11">
<div><a href="{{ path('user_show', {'login': post.author.login}) }}">@{{ post.author.login }}</a></div>
<div class="post-date">{{ post.createdAt|date('j M Y G:i') }}</div>
<div>
{% for pt in post.postTags %}
<a href="#" class="tag">{{ pt.text }}</a>
{% endfor %}
</div>
</div>
</div>
<div class="row post-text">
<div class="col-xs-12">
{{ post.text|markdown('app.point.markdown_parser') }}
</div>
</div>
<div class="row post-files">
<div class="col-xs-2">
{% for file in post.files %}
<div class="post-attachment">
<a href="{{ file.remoteUrl }}" class="post-image"><img src="{{ file.remoteUrl }}" class="img-thumbnail" /></a>
</div>
{% endfor %}
</div>
</div>
<div class="row">
<div class="col-xs-12"><a href="{{ post.id|point_post_url }}" class="post">#{{ post.id }}</a></div>
</div>
{% if post.comments|length > 0 %}
<div class="row comments">
{#
{% include '@SkobkinPointTools/Post/comments_tree.html.twig' with {
'comments': post.firstLevelComments
} only %}
#}
{% include '@SkobkinPointTools/Post/comments_list.html.twig' with {
'comments': post.comments
} %}
</div>
{% endif %}
</div>
{% endblock %} {% endblock %}

View file

@ -106,7 +106,8 @@ ul.users.mosaic li:nth-child(odd) {
color: red; color: red;
} }
a.tag { a.tag,
span.tag {
position: relative; position: relative;
display: inline-block; display: inline-block;
margin: .8em .8em 0 0; margin: .8em .8em 0 0;
@ -117,6 +118,12 @@ a.tag {
text-decoration: none; text-decoration: none;
} }
/* Posts */
.feed-post {
padding: 10px 0;
}
.post-block .post-date { .post-block .post-date {
margin-top: 5px; margin-top: 5px;
color: #a0a0a0; color: #a0a0a0;