List and delete scheduled statuses

This commit is contained in:
Thomas 2019-08-11 18:24:07 +02:00
parent a7e38578c7
commit 07f01ee504
8 changed files with 362 additions and 26 deletions

View file

@ -6,7 +6,7 @@
"ext-ctype": "*",
"ext-iconv": "*",
"craue/formflow-bundle": "^3.2",
"friendsofsymfony/jsrouting-bundle": "^2.3",
"friendsofsymfony/jsrouting-bundle": "^2.4",
"sensio/framework-extra-bundle": "^5.4",
"symfony/asset": "4.3.*",
"symfony/console": "4.3.*",

20
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"content-hash": "26bef0bc0c89d795031fc70f28fdc8bd",
"content-hash": "428bb8a6c6ea6826f24ac19126f513ac",
"packages": [
{
"name": "craue/formflow-bundle",
@ -581,16 +581,16 @@
},
{
"name": "friendsofsymfony/jsrouting-bundle",
"version": "2.3.1",
"version": "2.4.0",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfSymfony/FOSJsRoutingBundle.git",
"reference": "f6c9ee6857bce5924fbd54d4c6176a4dfa9de5b4"
"reference": "e42ed450eac2b61d5fcba9cd834c294a429e9a40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FriendsOfSymfony/FOSJsRoutingBundle/zipball/f6c9ee6857bce5924fbd54d4c6176a4dfa9de5b4",
"reference": "f6c9ee6857bce5924fbd54d4c6176a4dfa9de5b4",
"url": "https://api.github.com/repos/FriendsOfSymfony/FOSJsRoutingBundle/zipball/e42ed450eac2b61d5fcba9cd834c294a429e9a40",
"reference": "e42ed450eac2b61d5fcba9cd834c294a429e9a40",
"shasum": ""
},
"require": {
@ -623,13 +623,13 @@
"MIT"
],
"authors": [
{
"name": "FriendsOfSymfony Community",
"homepage": "https://github.com/friendsofsymfony/FOSJsRoutingBundle/contributors"
},
{
"name": "William Durand",
"email": "will+git@drnd.me"
},
{
"name": "FriendsOfSymfony Community",
"homepage": "https://github.com/friendsofsymfony/FOSJsRoutingBundle/contributors"
}
],
"description": "A pretty nice way to expose your Symfony2 routing to client applications.",
@ -639,7 +639,7 @@
"javascript",
"routing"
],
"time": "2019-06-17T18:22:41+00:00"
"time": "2019-08-10T15:40:05+00:00"
},
{
"name": "psr/cache",

View file

@ -16,10 +16,11 @@ use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use DateTime;
use DateTimeZone;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
@ -213,7 +214,58 @@ class FediPlanController extends AbstractController
public function scheduled()
{
return $this->render("fediplan/index.html.twig");
return $this->render("fediplan/scheduled.html.twig");
}
/**
* @Route("/scheduled/messages/{max_id}", name="load_more", options={"expose"=true})
*/
public function loadMoreAction(Request $request, Mastodon_api $mastodon_api, String $max_id = null){
$user = $this->getUser();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" " ,$user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$params = [];
if( $max_id != null){
$params['max_id'] = $max_id;
}
$scheduled_reply = [];
try {
$scheduled_reply = $mastodon_api->get_scheduled($params);
} catch (\ErrorException $e) {
}
$statuses = $mastodon_api->getScheduledStatuses($scheduled_reply['response'], $this->getUser());
$data['max_id'] = $scheduled_reply['max_id'];
$data['html'] = $this->renderView('fediplan/Ajax/layout.html.twig', ['statuses' => $statuses]);
return new JsonResponse($data);
}
/**
* @Method({"POST"})
* @Route("/scheduled/delete/messages/{id}", name="delete_message", options={"expose"=true})
*/
public function deleteMessage(Request $request, Mastodon_api $mastodon_api, String $id = null){
$user = $this->getUser();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" " ,$user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$response = [];
try {
$response = $mastodon_api->delete_scheduled($id);
} catch (\ErrorException $e) {}
return new JsonResponse($response);
}
/**

View file

@ -170,7 +170,6 @@ class Mastodon_api {
'Authorization' => $this->token['token_type'] . ' ' . $this->token['access_token']
);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_remote($url,$parameters);
return $response;
@ -230,7 +229,6 @@ class Mastodon_api {
$url .= "&max_id=".$parameters['body']['max_id'];
$parameters['body'] = [];
}
if( isset($parameters["method"]) && $parameters['method'] == "POST" )
$response = $curl->post($url, $parameters['body'] );
else if( isset($parameters["method"]) && $parameters['method'] == "GET" )
@ -240,7 +238,8 @@ class Mastodon_api {
else if( isset($parameters["method"]) && $parameters['method'] == "PATCH" )
$response = $curl->patch($url, $parameters['body'] );
else if( isset($parameters["method"]) && $parameters['method'] == "DELETE" )
$response = $curl->delete($url, $parameters['body'] );
$response = $curl->delete($url);
$min_id = null;
$max_id = null;
if( $response->response_headers) {
@ -1048,6 +1047,22 @@ class Mastodon_api {
return $response;
}
/**
* delete_scheduled
*
* Deleting a scheduled status
*
* @param int $id
*
* @return array $response empty
* @throws \ErrorException
*/
public function delete_scheduled ($id) {
$response = $this->_delete('/api/v1/scheduled_statuses/'.$id);
return $response;
}
/**
* statuses_reblog
*
@ -1108,6 +1123,20 @@ class Mastodon_api {
return $response;
}
/**
* scheduled_statuses
*
* @param array $parameters
*
* @return array $response
* @throws \ErrorException
*/
public function get_scheduled ($parameters = array()) {
$response = $this->_get('/api/v1/scheduled_statuses/', $parameters);
return $response;
}
/**
* timelines_home
*
@ -1338,6 +1367,8 @@ class Mastodon_api {
return $statuses;
}
/**
* getSingleNotification Hydrate a Notification from API reply
* @param $notificationParams
@ -1452,6 +1483,95 @@ class Mastodon_api {
}
/**
* getScheduledStatuses Hydrate an array of Scheduled Status from API reply
* @param $statusParams
* @return array
*/
public function getScheduledStatuses($statusParams, $account){
$statuses = [];
foreach ($statusParams as $statusParam)
$statuses[] = $this->getSingleScheduledStatus($statusParam, $account);
return $statuses;
}
/**
* getSingleScheduledStatus Hydrate a scheduled Status from API reply
* @param $statusParams
* @return Status
*/
public function getSingleScheduledStatus($statusParams, $account){
$status = new Status();
$status->setId($statusParams['id']);
$status->setInReplyToId($statusParams['params']['in_reply_to_id']);
$status->setContent($statusParams['params']['text']);
$status->setScheduledAt($this->stringToDate($statusParams['scheduled_at']));
$status->setAccount($account);
if( isset($statusParams['emojis']) && count($statusParams['emojis']) > 0){
$emojis = [];
foreach ($statusParams['emojis'] as $_e){
$emoji = new Emoji();
$emoji->setUrl($_e['url']);
$emoji->setShortcode($_e['shortcode']);
$emoji->setStaticUrl($_e['static_url']);
$emoji->setVisibleInPicker($_e['visible_in_picker']);
$emojis[] = $emoji;
}
$status->setEmojis($emojis);
}
$status->setSensitive($statusParams['params']['sensitive']);
$status->setSpoilerText($statusParams['params']['spoiler_text']);;
$status->setVisibility($statusParams['params']['visibility']);
if( isset($statusParams['media_attachments']) && count($statusParams['media_attachments']) > 0){
$media_attachments = [];
foreach ($statusParams['media_attachments'] as $_m){
$attachment = new Attachment();
$attachment->setId($_m['id']);
$attachment->setUrl($_m['url']);
$attachment->setType($_m['type']);
if($_m['remote_url'])
$attachment->setRemoteUrl($_m['remote_url']);
$attachment->setPreviewUrl($_m['preview_url']);
if($_m['text_url'])
$attachment->setTextUrl($_m['text_url']);
$attachment->setMeta(serialize($_m['meta']));
if($_m['description'])
$attachment->setDescription($_m['description']);
$media_attachments[] = $attachment;
}
$status->setMediaAttachments($media_attachments);
}
if( isset($statusParams['mentions']) && count($statusParams['mentions']) > 0){
$mentions = [];
foreach ($statusParams['mentions'] as $_m){
$mention = new Mention();
$mention->setUrl($_m['url']);
$mention->setAcct($_m['acct']);
$mention->setUsername($_m['username']);
$mention->setId($_m['id']);
$mentions[] = $mention;
}
$status->setMentions($mentions);
}
if( isset($statusParams['tags']) && count($statusParams['tags']) > 0){
$tags = [];
foreach ($statusParams['tags'] as $_t){
$tag = new Tag();
$tag->setUrl($_t['url']);
$tag->setName($_t['name']);
$tag->setHistory(isset($_t['history'])?$_t['history']:[]);
$tags[] = $tag;
}
$status->setTags($tags);
}
return $status;
}
/**
* getSingleAttachment Hydrate an Attachment from API reply
* @param $mediaParams

View file

@ -4,9 +4,6 @@ namespace App\SocialEntity;
use App\Entity\Emoji;
use App\Entity\MastodonAccount;
class Status
{
/** @var string */
@ -25,6 +22,8 @@ class Status
private $content;
/** @var \DateTime */
private $created_at;
/** @var \DateTime */
private $scheduled_at;
/** @var Emoji[] */
private $emojis = [];
/** @var int */
@ -175,9 +174,9 @@ class Status
}
/**
* @return \DateTime
* @return \DateTime|null
*/
public function getCreatedAt(): \DateTime
public function getCreatedAt(): ?\DateTime
{
return $this->created_at;
}
@ -185,11 +184,28 @@ class Status
/**
* @param \DateTime $created_at
*/
public function setCreatedAt(\DateTime $created_at): void
public function setCreatedAt(?\DateTime $created_at): void
{
$this->created_at = $created_at;
}
/**
* @return \DateTime|null
*/
public function getScheduledAt(): ?\DateTime
{
return $this->scheduled_at;
}
/**
* @param \DateTime $scheduled_at
*/
public function setScheduledAt(\DateTime $scheduled_at): void
{
$this->scheduled_at = $scheduled_at;
}
/**
* @return Emoji[]
*/
@ -321,7 +337,7 @@ class Status
/**
* @return string
*/
public function getSpoilerText(): string
public function getSpoilerText(): ?string
{
return $this->spoiler_text;
}
@ -329,7 +345,7 @@ class Status
/**
* @param string $spoiler_text
*/
public function setSpoilerText(string $spoiler_text): void
public function setSpoilerText(?string $spoiler_text): void
{
$this->spoiler_text = $spoiler_text;
}

View file

@ -0,0 +1,43 @@
{# @var status \App\SocialEntity\Status #}
{% for status in statuses %}
<div class="row" id="message_container_{{ status.getId() }}" style="margin-bottom: 20px;">
<div class="col-md-8">
<div class="card">
<div class="card-horizontal" style=" display: flex;flex: 1 1 auto;">
<div class="img-square-wrapper">
<img class="" width="80" src="{{ status.account.avatar }}" style=" border-radius: 5%; margin: 5px;">
</div>
<div class="card-body">
<span class="card-title" style="size: 1.1em;">{{ convertAccountEmoji(status.account , status.account.displayName) | raw }} - @{{ status.account.acct }}</span>
<p class="card-text">
{% if status.spoilerText is defined %}
<b>{{ status.spoilerText }}</b> <br/>
{% endif %}
{{ status.content }}
</p>
</div>
</div>
<div class="card-footer">
<small class="text-muted">
{% if status.visibility == "public" %}
<i class="fa fa-globe"></i>
{% elseif status.visibility == "unlisted" %}
<i class="fa fa-unlock-alt"></i>
{% elseif status.visibility == "private" %}
<i class="fa fa-lock"></i>
{% elseif status.visibility == "direct" %}
<i class="fa fa-envelope"></i>
{% endif %}
</small> - {{ status.scheduledAt | date('d/m/y H:m') }}
<button class="btn btn-danger small" data-record-id="{{ status.getId() }}" style="position: absolute;right: 5px;bottom: 5px;"
data-record-title="{{ status.content }} - {{ status.scheduledAt | date('d/m/y H:m') }}"
data-toggle="modal" data-target="#confirm-delete"
>X</button>
</div>
</div>
</div>
</div>
{% endfor %}

View file

@ -0,0 +1,105 @@
{% extends 'base.html.twig' %}
{% trans_default_domain 'fediplan' %}
{% block title %}{{ 'common.scheduled'|trans }}{% endblock %}
{% block content %}
{% include 'nav.html.twig' %}
<h1>Scheduled</h1>
<div class="row container">
<div class="col-md-12" id="content"></div>
</div>
<div class="row container hide" id="loader" style="text-align: center;margin-top: 50px;"><div class="lds-ring"><div></div><div></div><div></div><div></div></div></div>
<div class="row hide" id="no_content" style="margin-top: 50px;">
<div class="col-md-offset-3 col-md-6">
<div class="alert alert-warning" style="font-size: 1.5em;text-align: center;">No results found!</div>
</div>
</div>
<div class="modal fade" id="confirm-delete" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Confirm Delete</h4>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
</div>
<div class="modal-body">
<p>You are about to delete <b><i class="title"></i></b></p>
<p>Do you want to proceed?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger btn-ok">Delete</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('bundles/fosjsrouting/js/router.min.js') }}"></script>
<script src="{{ path('fos_js_routing_js', { callback: 'fos.Router.setData' }) }}"></script>
<script type="text/javascript">
$(document).ready(function() {
window.max_id = "";
$(window).scroll(function() {
if(($(window).scrollTop() == $(document).height() - $(window).height() )&& max_id != null) {
loadMore();
}
});
loadMore(null);
function loadMore(){
if(max_id == null || max_id === ""){
$('#loader').removeClass("d-none");
}
$("#no_content").addClass("d-none");
$.get( Routing.generate('load_more', { 'max_id': window.max_id } ))
.done(function(data) {
$("#content").append(data.html);
$('#loader').addClass("d-none");
console.log( data);
if( typeof data.html != "undefined" && data.html != "") {
// $("#no_content").addClass("d-none");
}else{
$("#no_content").removeClass("d-none");
}
window.max_id = data.max_id;
})
.fail(function() {
$('#loader').addClass("d-none");
})
}
});
$('#confirm-delete').on('click', '.btn-ok', function(e) {
var $modalDiv = $(e.delegateTarget);
var id = $(this).data('recordId');
$.post( Routing.generate('delete_message', { 'id': id } ))
.done(function(data) {
$("#message_container_"+id).remove();
$('#confirm-delete').modal('hide');
$modalDiv.modal('hide').removeClass('loading');
})
.fail(function() {
$('#loader').addClass("d-none");
$modalDiv.modal('hide').removeClass('loading');
})
$modalDiv.addClass('loading');
});
$('#confirm-delete').on('show.bs.modal', function(e) {
var data = $(e.relatedTarget).data();
$('.title', this).text(data.recordTitle);
$('.btn-ok', this).data('recordId', data.recordId);
});
</script>
{% endblock %}

View file

@ -16,11 +16,11 @@
<li class="nav-item {% if "articles" in app.request.attributes.get('schedule') %} active {% endif %}">
<a class="nav-link" href="{{ path('schedule') }}">Shedule</a>
</li>
{#
<li class="nav-item {% if app.request.attributes.get('_route') == 'scheduled' %} active {% endif %}">
<a class="nav-link" href="{{ path('scheduled') }}">scheduled</a>
</li>
#}
<li class="nav-item">
<a class="nav-link" href="{{ path('logout') }}" tabindex="-1" >Logout</a>
</li>