Browse Source

Make title and updated mandatory in Post to respect the Atom RFC4287

pull/1373/head
Timothée Jaussoin 1 year ago
parent
commit
71d86c6676
  1. 1
      CHANGELOG.md
  2. 249
      app/Post.php
  3. 27
      database/migrations/20241023191530_update_title_updated_not_null_posts_table.php

1
CHANGELOG.md

@ -15,6 +15,7 @@ v0.28.1 (master)
* Completely replace the offset parameter with a Session Timezone set on Login
* Use composite foreign keys for several relationships
* Add presences in the AdminSessions widget
* Make title and updated mandatory in Post to respect the Atom RFC4287
v0.28
---------------------------

249
app/Post.php

@ -15,10 +15,20 @@ class Post extends Model
protected $primaryKey = 'id';
protected $guarded = [];
public $with = ['attachments', 'likes', 'comments',
'contact', 'openlink', 'embed',
'links', 'files', 'pictures', 'picture',
'attachment', 'userAffiliation'];
public $with = [
'attachments',
'likes',
'comments',
'contact',
'openlink',
'embed',
'links',
'files',
'pictures',
'picture',
'attachment',
'userAffiliation'
];
public $withCount = ['userViews'];
private $titleLimit = 200;
@ -40,8 +50,8 @@ class Post extends Model
public function comments()
{
return $this->hasMany('App\Post', 'parent_id', 'id')
->orderBy('published')
->where('like', false);
->orderBy('published')
->where('like', false);
}
public function parent()
@ -57,7 +67,7 @@ class Post extends Model
public function userAffiliation()
{
return $this->hasOne('App\Affiliation', ['server', 'node'], ['server', 'node'])
->where('jid', \App\User::me()->id);
->where('jid', \App\User::me()->id);
}
public function userViews()
@ -68,64 +78,64 @@ class Post extends Model
public function likes()
{
return $this->hasMany('App\Post', 'parent_id', 'id')
->whereIn('id', function ($query) {
$query->select(DB::raw('min(id) as id'))
->from('posts')
->where('like', true)
->whereNotNull('aid')
->groupByRaw('aid, parent_id');
});
->whereIn('id', function ($query) {
$query->select(DB::raw('min(id) as id'))
->from('posts')
->where('like', true)
->whereNotNull('aid')
->groupByRaw('aid, parent_id');
});
}
public function openlink()
{
return $this->hasOne('App\Attachment')
->where('category', 'open');
->where('category', 'open');
}
public function embed()
{
return $this->hasOne('App\Attachment')
->where('category', 'embed');
->where('category', 'embed');
}
public function links()
{
return $this->hasMany('App\Attachment')
->where('category', 'link')
->whereNotIn('href', function ($query) {
$query->select('href')
->from('attachments')
->where('post_id', $this->id)
->where('category', 'picture')
->get();
});
->where('category', 'link')
->whereNotIn('href', function ($query) {
$query->select('href')
->from('attachments')
->where('post_id', $this->id)
->where('category', 'picture')
->get();
});
}
public function files()
{
return $this->hasMany('App\Attachment')
->where('category', 'file');
->where('category', 'file');
}
public function pictures()
{
return $this->hasMany('App\Attachment')
->where('category', 'picture')
->where('type', '!=', 'content');
->where('category', 'picture')
->where('type', '!=', 'content');
}
public function picture()
{
return $this->hasOne('App\Attachment')
->where('category', 'picture');
->where('category', 'picture');
}
public function attachment()
{
return $this->hasOne('App\Attachment')
->whereIn('rel', ['enclosure', 'related'])
->orderBy('rel', 'desc'); // related first
->whereIn('rel', ['enclosure', 'related'])
->orderBy('rel', 'desc'); // related first
}
public function attachments()
@ -136,6 +146,16 @@ class Post extends Model
public function save(array $options = [])
{
try {
if (!$this->validAtom()) {
\logError('Invalid Atom: ' . $this->server . '/' . $this->node . '/' . $this->nodeid);
if ($this->created_at) {
$this->delete();
}
return;
}
if (!$this->changed) return;
parent::save($options);
@ -156,6 +176,11 @@ class Post extends Model
}
}
private function validAtom(): bool
{
return ($this->title != null && $this->updated != null);
}
public function scopeRestrictToMicroblog($query)
{
return $query->where('posts.node', 'urn:xmpp:microblog:0');
@ -179,9 +204,9 @@ class Post extends Model
$query->whereIn('id', function ($query) {
$host = \App\User::me()->session->host;
$query->select('id')
->from('posts')
->where('server', 'like', '%.' . $host)
->orWhere('server', 'like', '@' . $host);
->from('posts')
->where('server', 'like', '%.' . $host)
->orWhere('server', 'like', '@' . $host);
});
}
}
@ -212,13 +237,14 @@ class Post extends Model
protected function withContactsScope($query)
{
return $query->unionAll(DB::table('posts')
->whereIn('posts.server', function ($query) {
$query->from('rosters')
->select('jid')
->where('session_id', SESSION_ID)
->where('subscription', 'both');
})
return $query->unionAll(
DB::table('posts')
->whereIn('posts.server', function ($query) {
$query->from('rosters')
->select('jid')
->where('session_id', SESSION_ID)
->where('subscription', 'both');
})
);
}
@ -229,9 +255,10 @@ class Post extends Model
protected function withMineScope($query)
{
return $query->unionAll(DB::table('posts')
->where('node', 'urn:xmpp:microblog:0')
->where('server', \App\User::me()->id)
return $query->unionAll(
DB::table('posts')
->where('node', 'urn:xmpp:microblog:0')
->where('server', \App\User::me()->id)
);
}
@ -242,17 +269,18 @@ class Post extends Model
protected function withSubscriptionsScope($query)
{
return $query->unionAll(DB::table('posts')
->whereIn('server', function ($query) {
$query->select('server')
->from('subscriptions')
->where('jid', \App\User::me()->id);
})
->whereIn('node', function ($query) {
$query->select('node')
->from('subscriptions')
->where('jid', \App\User::me()->id);
})
return $query->unionAll(
DB::table('posts')
->whereIn('server', function ($query) {
$query->select('server')
->from('subscriptions')
->where('jid', \App\User::me()->id);
})
->whereIn('node', function ($query) {
$query->select('node')
->from('subscriptions')
->where('jid', \App\User::me()->id);
})
);
}
@ -264,21 +292,21 @@ class Post extends Model
public function getPreviousAttribute()
{
return \App\Post::where('server', $this->server)
->where('node', $this->node)
->where('published', '<', $this->published)
->where('open', true)
->orderBy('published', 'desc')
->first();
->where('node', $this->node)
->where('published', '<', $this->published)
->where('open', true)
->orderBy('published', 'desc')
->first();
}
public function getNextAttribute()
{
return \App\Post::where('server', $this->server)
->where('node', $this->node)
->where('published', '>', $this->published)
->orderBy('published')
->where('open', true)
->first();
->where('node', $this->node)
->where('published', '>', $this->published)
->orderBy('published')
->where('open', true)
->first();
}
public function getTruenameAttribute()
@ -346,8 +374,8 @@ class Post extends Model
case 'xhtml':
$title = strip_tags(
($t->children()->getName() == 'div' && (string)$t->children()->attributes()->xmlns == 'http://www.w3.org/1999/xhtml')
? html_entity_decode((string)$t->children()->asXML())
: (string)$t->children()->asXML()
? html_entity_decode((string)$t->children()->asXML())
: (string)$t->children()->asXML()
);
break;
case 'text':
@ -379,8 +407,10 @@ class Post extends Model
}
// Ensure that the author is the publisher
if ($entry->entry->author && $entry->entry->author->uri
&& 'xmpp:'.baseJid((string)$entry->attributes()->publisher) == (string)$entry->entry->author->uri) {
if (
$entry->entry->author && $entry->entry->author->uri
&& 'xmpp:' . baseJid((string)$entry->attributes()->publisher) == (string)$entry->entry->author->uri
) {
$this->aid = substr((string)$entry->entry->author->uri, 5);
$this->aname = ($entry->entry->author->name)
? (string)$entry->entry->author->name
@ -393,38 +423,33 @@ class Post extends Model
$this->aid = null;
}
// Non standard support
/*if ($entry->entry->source && $entry->entry->source->author->name) {
$this->aname = (string)$entry->entry->source->author->name;
}
if ($entry->entry->source && $entry->entry->source->author->uri
&& substr((string)$entry->entry->source->author->uri, 5) == 'xmpp:') {
$this->aid = substr((string)$entry->entry->source->author->uri, 5);
}*/
if (empty($this->aname)) {
$this->aname = null;
}
// Content
$this->title = $entry->entry->title
? $this->extractTitle($entry->entry->title)
: null;
$summary = ($entry->entry->summary && (string)$entry->entry->summary != '')
? '<p class="summary">'.(string)$entry->entry->summary.'</p>'
? '<p class="summary">' . (string)$entry->entry->summary . '</p>'
: null;
$content = '';
$content = $entry->entry->content
? $this->extractContent($entry->entry->content)
: null;
if ($entry->entry->content) {
$content = $this->extractContent($entry->entry->content);
$this->title = $this->extractTitle($entry->entry->title);
} else {
$content = $this->extractContent($entry->entry->title);
}
$this->content = $this->contentcleaned = null;
$content = $summary.$content;
if ($summary != null || $content != null) {
$this->content = trim((string)$summary . (string)$content);
$this->contentcleaned = requestAPI('purifyhtml', 2, ['content' => $this->content]);
}
$this->updated = ($entry->entry->updated)
? toSQLDate($entry->entry->updated)
: gmdate(MOVIM_SQL_DATE);
: null;
if ($entry->entry->published) {
$this->published = toSQLDate($entry->entry->published);
@ -440,9 +465,11 @@ class Post extends Model
// Tags parsing
if ($entry->entry->category) {
if ($entry->entry->category->count() == 1
&& isset($entry->entry->category->attributes()->term)
&& !empty(trim($entry->entry->category->attributes()->term))) {
if (
$entry->entry->category->count() == 1
&& isset($entry->entry->category->attributes()->term)
&& !empty(trim($entry->entry->category->attributes()->term))
) {
$tag = \App\Tag::firstOrCreateSafe([
'name' => strtolower((string)$entry->entry->category->attributes()->term)
]);
@ -473,7 +500,7 @@ class Post extends Model
// Extract more tags if possible
$tagsContent = getHashtags(htmlspecialchars($this->title ?? ''))
+ getHashtags(htmlspecialchars($this->contentraw ?? ''));
+ getHashtags(htmlspecialchars($this->contentraw ?? ''));
foreach ($tagsContent as $tag) {
$tag = \App\Tag::firstOrCreateSafe([
'name' => strtolower((string)$tag)
@ -490,10 +517,6 @@ class Post extends Model
$this->commentserver = $this->server;
}
// Save the base and cleaned content
$this->content = trim($content);
$this->contentcleaned = requestAPI('purifyhtml', 2, ['content' => $this->content]);
// We fill empty aid
if ($this->isMicroblog() && empty($this->aid)) {
$this->aid = $this->server;
@ -510,7 +533,7 @@ class Post extends Model
$extra = false;
// We try to extract a picture
$xml = \simplexml_load_string('<div>'.$this->contentcleaned.'</div>');
$xml = \simplexml_load_string('<div>' . $this->contentcleaned . '</div>');
if ($xml) {
$results = $xml->xpath('//img/@src');
@ -537,8 +560,8 @@ class Post extends Model
if ($this->isComment()) {
$p = \App\Post::where('commentserver', $this->server)
->where('commentnodeid', substr($this->node, 30))
->first();
->where('commentnodeid', substr($this->node, 30))
->first();
if ($p) {
$this->parent_id = $p->id;
@ -635,13 +658,11 @@ class Post extends Model
{
$enclosures = [];
foreach (array_filter($this->attachments, fn ($a) => $a->rel == 'enclosure') as $attachment)
{
foreach (array_filter($this->attachments, fn($a) => $a->rel == 'enclosure') as $attachment) {
array_push($enclosures, $attachment->href);
}
foreach (array_filter($this->attachments, fn ($a) => $a->rel != 'enclosure') as $key => $attachment)
{
foreach (array_filter($this->attachments, fn($a) => $a->rel != 'enclosure') as $key => $attachment) {
if (in_array($attachment->href, $enclosures)) {
unset($this->attachments[$key]);
}
@ -649,11 +670,13 @@ class Post extends Model
// Remove duplicates...
foreach ($this->attachments as $key => $attachment) {
foreach($this->attachments as $keyCheck => $attachmentCheck) {
if ($key != $keyCheck
&& $attachment->href == $attachmentCheck->href
&& $attachment->category == $attachmentCheck->category
&& $attachment->rel == $attachmentCheck->rel) {
foreach ($this->attachments as $keyCheck => $attachmentCheck) {
if (
$key != $keyCheck
&& $attachment->href == $attachmentCheck->href
&& $attachment->category == $attachmentCheck->category
&& $attachment->rel == $attachmentCheck->rel
) {
unset($this->attachments[$key]);
}
}
@ -666,12 +689,12 @@ class Post extends Model
return $this->nodeid;
}
return 'urn:uuid:'.generateUUID($this->nodeid);
return 'urn:uuid:' . generateUUID($this->nodeid);
}
public function getRef()
{
return 'xmpp:'.$this->server.'?;node='.$this->node.';item='.$this->nodeid;
return 'xmpp:' . $this->server . '?;node=' . $this->node . ';item=' . $this->nodeid;
}
// Works only for the microblog posts
@ -738,7 +761,7 @@ class Post extends Model
public function hasCommentsNode(): bool
{
return (isset($this->commentserver)
&& isset($this->commentnodeid));
&& isset($this->commentnodeid));
}
public function getSummary()
@ -772,9 +795,9 @@ class Post extends Model
}
return \App\Post::where('server', $this->replyserver)
->where('node', $this->replynode)
->where('nodeid', $this->replynodeid)
->first();
->where('node', $this->replynode)
->where('nodeid', $this->replynodeid)
->first();
}
public function isLiked()

27
database/migrations/20241023191530_update_title_updated_not_null_posts_table.php

@ -0,0 +1,27 @@
<?php
use App\Post;
use Movim\Migration;
use Illuminate\Database\Schema\Blueprint;
class UpdateTitleUpdatedNotNullPostsTable extends Migration
{
public function up()
{
Post::where('title', '')->delete();
Post::whereNull('title')->delete();
$this->schema->table('posts', function (Blueprint $table) {
$table->text('title')->nullable(false)->change();
$table->datetime('updated')->nullable(false)->change();
});
}
public function down()
{
$this->schema->table('posts', function (Blueprint $table) {
$table->text('title')->nullable(true)->change();
$table->datetime('updated')->nullable(true)->change();
});
}
}
Loading…
Cancel
Save