Commit 8715b049 authored by Malcolm Blaney's avatar Malcolm Blaney

Reader module improve formatting of feed lists, store and display

the repost author on reposts. Also fixed reposting photo posts,
fixed bug in SimplePie microformats parser that stopped categories
being stored properly.
parent d9a751ee
Pipeline #36243697 passed with stage
in 1 minute and 15 seconds
......@@ -476,6 +476,7 @@ class Post extends Base {
'"",".post .thumb","border-radius","2px"',
'"",".post .options > *","padding","5px"',
'"",".post .navigation .next","float","right"',
'"",".post-reposted-by","font-size","0.8em"',
'"",".post-web-actions","padding-top","10px"',
'"",".post-web-actions a","padding","10px"',
'"",".post-web-actions a.highlight","font-weight","bold"'];
......
......@@ -384,13 +384,15 @@ class Reader extends Base {
$this->Log('Reader->Install 7: ' . $mysqli->error);
}
$format_with_title = '<h5 class="author">!author !date</h5>' .
$format_with_title = '<h5 class="author">!date !author</h5>' .
'<h4 class="title">!title</h4>' .
'<div class="content">!content</div>' .
'<div class="media">!media</div>' .
'<div class="options">!category</div>';
$format_no_title = '<h5 class="author">!author !date</h5>';
$format_no_title = '<h5 class="author">!date !author</h5>' .
'<div class="content">!content</div>' .
'<div class="options">!category</div>' .
'<div class="media">!media</div>' .
'<div class="options">!category</div>';
$template = ['"reader-item-count", "", "10"',
'"reader-format-with-title", "", ' .
'"' . $mysqli->escape_string($format_with_title) . '"',
......@@ -411,6 +413,9 @@ class Reader extends Base {
$site_style = ['"",".reader .feed-item","padding","5px"',
'"",".reader .feed-text.added","background-color","#ff5500"',
'"",".reader .feed-url","font-size","0.8em"',
'"",".reader .feed-url","color","#aaaaaa"',
'"",".reader .feed-url a","color","#aaaaaa"',
'"",".reader .content .h-card img","width","20px"',
'"",".reader .content .h-card img","border-radius","2px"',
'"",".reader .author","margin","10px 0"',
......@@ -469,6 +474,7 @@ class Reader extends Base {
'"",".reader-discovered-add","float","right"',
'"",".reader-discovered-feed-url","color","#aaaaaa"',
'"",".reader-discovered-feed-url","font-size","0.8em"',
'"",".reader-reposted-by","font-size","0.8em"',
'"",".h-card .ui-icon.ui-icon-person","display",' .
'"inline-block"'];
$this->AddSiteStyle($site_style);
......@@ -888,16 +894,21 @@ class Reader extends Base {
'user = "' . $owner . '" AND box_id = ' . $id . ' ORDER BY feed_title';
if ($result = $mysqli->query($query)) {
while ($reader = $result->fetch_assoc()) {
$url = $reader['xml_url'];
$short_url = strlen($url) <= 50 ? $url : substr($url, 0, 47) . '...';
$title = htmlspecialchars($reader['feed_title']);
$added = $us_xml_url === $url ? ' added' : '';
$link = '<a href="' . $url . '">' . $short_url . '</a>';
$content .= '<div class="feed-item">' .
'<button class="remove-feed-item">remove</button> ' .
'<span class="feed-text' . $added . '">';
if ($title === '') {
$title = '<i>feed has no title</i>';
$content .= $link . '</div>';
}
else {
$content .= '<span class="feed-title">' . $title . '</span> ' .
'<span class="feed-url">[' . $link . ']</span></div>';
}
$added = $us_xml_url === $reader['xml_url'] ? ' added' : '';
$content .= '<div class="feed-item">' .
'<button class="remove-feed-item">remove</button> ' .
'<a class="feed-text' . $added . '" ' .
'href="' . $reader['xml_url'] . '">' . $title . '</a>' .
'</div>';
}
$result->close();
}
......@@ -1124,8 +1135,19 @@ class Reader extends Base {
private function GetItem($item, $prev_class, $config, $microsub = false) {
$permalink = $item['permalink'];
// Look for a repost url to match the original author.
$check = $permalink;
$regex = '/<span class="repost-of"><\/span> <a href="([^"]+)">/';
if (preg_match($regex, $item['content'], $match)) {
$check = $match[1];
}
$repost_author = '';
$original_author = $item['author'];
if (strpos($original_author, ',') !== false) {
list($original_author, $repost_author) = explode(',', $original_author);
}
list($author_name, $author_url, $author_photo) =
$this->LookupNickname($item['author'], $permalink);
$this->LookupNickname($original_author, $check);
// If author name or photo wasn't found try using the feed title or image.
if ($author_name === '') {
$author_name = $item['feed_title'];
......@@ -1149,6 +1171,7 @@ class Reader extends Base {
'content' => ['html' => $item['content']], '_id' => 'home'];
}
$media = '';
$title = $item['title'];
$content = content_markup($item['content']);
if (isset($enclosure_list['photo'][0])) {
......@@ -1171,32 +1194,30 @@ class Reader extends Base {
// lightbox. Need a permanent, unique name for the image set, but
// don't have anything unique except for photo urls, so use that.
$image_set_id = preg_replace('/[[:^alnum:]]/', '', $photo_list[0]);
$content .= '<p class="photo-list">';
$media .= '<p class="photo-list">';
for ($i = 0; $i < $count; $i++) {
$hidden = $i <= 1 ? '' : 'class="hidden" ';
$content .= '<a href="' . $photo_list[$i] . '" ' . $hidden .
$media .= '<a href="' . $photo_list[$i] . '" ' . $hidden .
'data-lightbox="image-set-' . $image_set_id . '">' .
'<img src="' . $photo_list[$i] . '"></a>';
}
$content .= '<br><b>' . $count . ' photos</b></p>';
$media .= '<br><b>' . $count . ' photos</b></p>';
}
else if ($count === 1) {
$content .= '<p><img src="' . $photo_list[0] . '"></p>';
$media .= '<p><img src="' . $photo_list[0] . '"></p>';
}
}
if (isset($enclosure_list['audio'][0])) {
foreach ($enclosure_list['audio'] as $audio) {
$content .= '<audio controls src="' . $audio . '"></audio>';
$media .= '<audio controls src="' . $audio . '"></audio>';
}
}
if (isset($enclosure_list['video'][0])) {
foreach ($enclosure_list['video'] as $video) {
$content .= '<video controls src="' . $video . '"></video>';
$media .= '<video controls src="' . $video . '"></video>';
}
}
$date = date('j F g:ia', $item['timestamp']);
$reader_format = $title === '' || $config['hide-titles'] ?
'reader-format-no-title' : 'reader-format-with-title';
$author = '';
if ($author_photo !== '') {
$author .= '<a href="' . $author_url . '">' .
......@@ -1204,15 +1225,34 @@ class Reader extends Base {
}
$author .= '<a href="' . $author_url . '" class="author-name">' .
$author_name . '</a>';
if ($repost_author !== '') {
list($repost_name, $repost_url, $repost_photo) =
$this->LookupNickname($repost_author, $permalink);
$author .= '<span class="reader-reposted-by">reposted by ';
if ($repost_photo !== '') {
$author .= '<a href="' . $repost_url . '">' .
'<img class="thumb" src="' . $repost_photo . '"></a> ';
}
$author .= '<a href="' . $repost_url . '">' . $repost_name .'</a></span>';
}
$category = '';
if (isset($category_list[0])) {
foreach ($category_list as $tag) {
$category .= category_markup($tag);
if (is_array($category_list)) {
for ($i = 0; $i < count($category_list); $i++) {
if ($category_list[$i] === 'Reposted from') {
// Skip repost tags which are added by the Post module, the next tag
// is the repost-of url which is already displayed in the content.
$i++;
continue;
}
$category .= category_markup($category_list[$i]);
}
}
if ($category !== '') {
$category = '<span class="reader-tag-label">tags: </span>' . $category;
}
$date = date('j F g:ia', $item['timestamp']);
if (!$config['hide-permalinks']) {
$date = '<a href="' . $permalink . '" class="permalink">' . $date .'</a>';
}
......@@ -1297,9 +1337,11 @@ class Reader extends Base {
'<span class="ui-icon ui-icon-gear"></span>settings</a></div>';
}
$reader_format = $title === '' || $config['hide-titles'] ?
'reader-format-no-title' : 'reader-format-with-title';
$patterns = ['/!title/', '/!content/', '/!author/', '/!category/',
'/!date/'];
$replacements = [$title, $content, $author, $category, $date];
'/!date/', '/!media/'];
$replacements = [$title, $content, $author, $category, $date, $media];
$reader_item = $group_header .
'<div class="reader-item ' . $class . $hidden . '">' .
$this->Substitute($reader_format, $patterns, $replacements) .
......@@ -1766,6 +1808,8 @@ class Reader extends Base {
$domain = $match[1];
if ($domain === $this->user->config->ServerName()) return;
}
// Also don't try registering from localhost.
if ($this->user->config->ServerName() === 'localhost') return;
if ($us_attributes = $us_cloud[0]['attribs']['']) {
// Only supporting cloud notifications that specify 'http-post' as the
......@@ -1825,6 +1869,8 @@ class Reader extends Base {
$domain = $match[1];
if ($domain === $this->user->config->ServerName()) return;
}
// Also don't try registering from localhost.
if ($this->user->config->ServerName() === 'localhost') return;
$us_existing = '';
$mysqli = connect_db();
......
......@@ -57,12 +57,13 @@ else if($(this).hasClass('share')){var title=item.find('.title').text();var auth
if(dobrado.editor){dobrado.editor.setData(content);}
else{$('#writer-content').val($.trim(content));}
$('#writer-author').val(author);if(title){$('#writer-title').val(title);$('.writer-options-title').show();}
var media=item.find('.media img').map(function(){return this.outerHTML;}).get().join();if(media!==''){$('#writer-photo-selected').append(media);$('.writer-options-photo').show();}
dobrado.writer.action('share',permalink,sendTo);}
else if($(this).hasClass('reply')){if(dobrado.editor){dobrado.editor.setData('');}
else{$('#writer-content').val('');}
dobrado.writer.action('reply',permalink,sendTo);}
window.scroll(0,0);return false;}
function removeFeed(){dobrado.log('Removing feed...','info');$.post('/php/request.php',{id:'#'+$('.reader').attr('id'),request:'reader',action:'remove',xmlUrl:$(this).siblings('a').prop('href'),url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'reader remove')){return;}
function removeFeed(){dobrado.log('Removing feed...','info');$.post('/php/request.php',{id:'#'+$('.reader').attr('id'),request:'reader',action:'remove',xmlUrl:$(this).siblings().find('a').prop('href'),url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'reader remove')){return;}
var reader=JSON.parse(response);if(reader.content){$('.reader-content').html(reader.content);}
else{$('.reader-content').html('');}
if(reader.more){$('.reader-more').show();}
......
......@@ -291,6 +291,13 @@ if (!this.dobrado.reader) {
$('#writer-title').val(title);
$('.writer-options-title').show();
}
var media = item.find('.media img').map(function() {
return this.outerHTML;
}).get().join();
if (media !== '') {
$('#writer-photo-selected').append(media);
$('.writer-options-photo').show();
}
dobrado.writer.action('share', permalink, sendTo);
}
else if ($(this).hasClass('reply')) {
......@@ -309,8 +316,9 @@ if (!this.dobrado.reader) {
function removeFeed() {
dobrado.log('Removing feed...', 'info');
$.post('/php/request.php',
{ id: '#' + $('.reader').attr('id'), request : 'reader',
action: 'remove', xmlUrl: $(this).siblings('a').prop('href'),
{ id: '#' + $('.reader').attr('id'),
request : 'reader', action: 'remove',
xmlUrl: $(this).siblings().find('a').prop('href'),
url: location.href, token: dobrado.token },
function(response) {
if (dobrado.checkResponseError(response, 'reader remove')) {
......
......@@ -63,7 +63,7 @@ if(sendTo==='twitter'){sendToTwitter=1;}
dobrado.log('Loading action...','info');$.post('/php/request.php',{request:'writer',mode:'showWebaction',action:action,actionUrl:url,url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'writer action')){return;}
var webactionInfo=JSON.parse(response);$('#writer-webaction-info').html(webactionInfo.content);$('#writer-remove-action').button({icon:'ui-icon-closethick',showLabel:false}).click(removeActionExplicit);if(dobrado.reader){$('#writer-follow').button().click(dobrado.reader.addFeed);}});};dobrado.writer.select=function(filename){$('#writer-photo-selected').append('<img src="'+filename+'">');};dobrado.writer.showEditor=function(){function clearContent(){if(dobrado.editor.getData()===defaultText){dobrado.editor.setData('');}}
if(dobrado.editMode){dobrado.closeEditor();}
$('#writer-content').parents('div').each(function(){if(/^dobrado-/.test($(this).attr('id'))){dobrado.current='#'+$(this).attr('id');return false;}});$(dobrado.current).data('label','writer');var editorCssProperties=['background','background-attachment','background-color','background-image','background-position','background-repeat','background-size','clear','color','direction','empty-cells','font','font-family','font-size','font-style','font-variant','font-weight','letter-spacing','line-height','list-style','list-style-image','list-style-position','list-style-type','margin','margin-left','margin-right','margin-top','margin-bottom','opacity','outline','outline-color','outline-style','outline-width','padding','padding-bottom','padding-left','padding-right','padding-top','size','text-align','text-decoration','text-indent','text-transform','white-space','word-spacing'];var css='.cke_editable { ';var current=$(dobrado.current);for(var i=0;i<editorCssProperties.length;i++){var property=editorCssProperties[i];var value=current.css(property);if(value){css+=property+': '+value+';';}}
$('#writer-content').parents('div').each(function(){if(/^dobrado-/.test($(this).attr('id'))){dobrado.current='#'+$(this).attr('id');return false;}});$(dobrado.current).data('label','writer');var editorCssProperties=['background','background-attachment','background-color','background-image','background-position','background-repeat','background-size','clear','color','direction','empty-cells','font','font-family','font-size','font-style','font-variant','font-weight','letter-spacing','line-height','list-style','list-style-image','list-style-position','list-style-type','margin','margin-left','margin-right','margin-top','margin-bottom','opacity','outline','outline-color','outline-style','outline-width','padding','padding-bottom','padding-left','padding-right','padding-top','size','text-align','text-decoration','text-indent','text-transform','white-space','word-spacing'];var css='.photo-hidden img { display: none; } .cke_editable { ';var current=$(dobrado.current);for(var i=0;i<editorCssProperties.length;i++){var property=editorCssProperties[i];var value=current.css(property);if(value){css+=property+': '+value+';';}}
css+='}';CKEDITOR.addCss(css);var content=$('#writer-content').text();$('#writer-content').val(content);dobrado.editor=CKEDITOR.replace('writer-content',{allowedContent:true,autoGrow_minHeight:$('#writer-content').height(),autoGrow_onStartup:true,disableNativeSpellChecker:false,enterMode:CKEDITOR.ENTER_BR,extraPlugins:'autogrow,extended',filebrowserBrowseUrl:'/php/browse.php',removePlugins:'elementspath,tableselection,tabletools,contextmenu,liststyle',resize_enabled:false,on:{focus:clearContent},toolbar:[['Undo','Redo','-','Bold','Italic','-','Link','Unlink','-','Image','Extended']]});$('.writer-post').button('option','disabled',false);};dobrado.writer.newModuleCallback=function(id,context){var data=dobrado.editor?dobrado.editor.getData():$('#writer-content').val();var enclosure=[];$('#writer-photo-selected > img').each(function(){enclosure.push($(this).attr('src'));});var content={data:data,dataOnly:false,title:$('#writer-title').val(),author:$('#writer-author').val(),category:$('#writer-tag-input').val(),enclosure:enclosure,twitter:sendToTwitter,webactionType:webactionType,webactionUrl:webactionUrl};if(context==='post'){$.post('/php/content.php',{id:id,label:'post',content:JSON.stringify(content),url:location.href,token:dobrado.token},function(response){if(dobrado.checkResponseError(response,'writer callback')){return;}
var formatted=JSON.parse(response);if(formatted.html){$(id).html(formatted.html);$(id).show();}
if(dobrado.editMode){var element=$(id+' .dobrado-editable').get(0);if(element){dobrado.inlineEditor(element);}}
......
......@@ -356,7 +356,7 @@ if (!this.dobrado.writer) {
'size', 'text-align', 'text-decoration', 'text-indent',
'text-transform', 'white-space', 'word-spacing'];
var css = '.cke_editable { ';
var css = '.photo-hidden img { display: none; } .cke_editable { ';
var current = $(dobrado.current);
for (var i = 0; i < editorCssProperties.length; i++) {
var property = editorCssProperties[i];
......
This diff is collapsed.
......@@ -152,6 +152,7 @@ if (!this.dobrado.extended) {
// This checkbox is used in page mode.
var updateAllPages = $('#extended-update-all-pages:checked').length;
dobrado.log('Saving style changes.', 'info');
$.post('/php/' + call + '.php',
{ style: JSON.stringify(rules), media: media,
updateAllPages: updateAllPages,
......
......@@ -360,6 +360,10 @@ function create_site_style() {
'"","input[type=password]","border","1px solid #aaaaaa"',
'"","input[type=password]","border-radius","3px"',
'"","input[type=password]:focus","border-color","#32a3cf"',
'"","textarea","padding","4px"',
'"","textarea","border","1px solid #aaaaaa"',
'"","textarea","border-radius","3px"',
'"","textarea:focus","border-color","#32a3cf"',
'"",".form-spacing","margin-top","4px"',
'"",".form-spacing","margin-bottom","4px"',
'"",".form-spacing","clear","both"',
......@@ -372,7 +376,6 @@ function create_site_style() {
'"",".form-spacing > textarea","height","75px"',
'"",".form-spacing > textarea","width","300px"',
'"",".form-spacing > textarea","max-width","100%"',
'"",".form-spacing > textarea","padding","8px"',
'"",".form-spacing > select","height","30px"',
'"",".form-spacing > label","float","left"',
'"",".form-spacing > label","margin-top","0.3em"',
......
......@@ -317,6 +317,8 @@ function parse_hcard($author, $name_only = false) {
$us_photo = '';
$us_author_photo = '';
$us_author_url = '';
$us_repost_name = '';
$us_repost_url = '';
if (isset($author['type']) && in_array('h-card', $author['type'])) {
if (isset($author['properties']['photo'][0])) {
......@@ -342,9 +344,17 @@ function parse_hcard($author, $name_only = false) {
if (stripos($us_author_url, 'http') !== 0) {
$us_author_url = 'http://' . $us_author_url;
}
// A second author in an h-card is assumed to be a repost author.
if (isset($author['properties']['url'][1])) {
$us_repost_url = $author['properties']['url'][1];
if (stripos($us_repost_url, 'http') !== 0) {
$us_repost_url = 'http://' . $us_repost_url;
}
}
}
if (isset($author['properties']['name'][0])) {
$us_author_name = $author['properties']['name'][0];
// Can't contain commas because repost author may also be returned.
$us_author_name = str_replace(',', '', $author['properties']['name'][0]);
$mysqli = connect_db();
$name = $mysqli->escape_string($us_author_name);
$photo = $mysqli->escape_string($us_photo);
......@@ -357,7 +367,19 @@ function parse_hcard($author, $name_only = false) {
'"' . $photo . '", "' . $nickname . '", 0) ON DUPLICATE KEY UPDATE ' .
'name = "' . $name . '"' . $update_photo_query;
if (!$mysqli->query($query)) {
log_db('microformats->parse_hcard: ' . $mysqli->error);
log_db('microformats->parse_hcard 1: ' . $mysqli->error);
}
if (isset($author['properties']['name'][1])) {
$us_repost_name =
str_replace(',', '', $author['properties']['name'][1]);
$repost_name = $mysqli->escape_string($us_repost_name);
$repost_url = $mysqli->escape_string($us_repost_url);
$query = 'INSERT INTO nickname VALUES ("' . $repost_name . '", ' .
'"' . $repost_url . '", "", "", 0) ON DUPLICATE KEY UPDATE ' .
'name = "' . $repost_name . '"';
if (!$mysqli->query($query)) {
log_db('microformats->parse_hcard 2: ' . $mysqli->error);
}
}
$mysqli->close();
}
......@@ -365,7 +387,8 @@ function parse_hcard($author, $name_only = false) {
else if (isset($author['value'])) {
$us_author_name = $author['value'];
}
return $name_only ? $us_author_name :
if ($us_repost_name !== '') $us_repost_name = ',' . $us_repost_name;
return $name_only ? $us_author_name . $us_repost_name :
[$us_author_name, $us_author_photo, $us_author_url];
}
......
......@@ -418,11 +418,15 @@ class SimplePie_Parser
private function parse_hcard($data, $category = false) {
$name = '';
$link = '';
$repost = '';
// Check if h-card is set and pass that information on in the link.
if (isset($data['type']) && in_array('h-card', $data['type'])) {
if (isset($data['properties']['name'][0])) {
$name = $data['properties']['name'][0];
}
if (isset($data['properties']['name'][1])) {
$repost = $data['properties']['name'][1];
}
if (isset($data['properties']['url'][0])) {
$link = $data['properties']['url'][0];
if ($name === '') {
......@@ -432,9 +436,14 @@ class SimplePie_Parser
// can't have commas in categories.
$name = str_replace(',', '', $name);
}
if ($repost !== '' && isset($data['properties']['url'][1])) {
$repost = '<a class="p-name u-url" ' .
'href="' . $data['properties']['url'][1] . '">' . $repost . '</a>';
}
$person_tag = $category ? '<span class="person-tag"></span>' : '';
return '<a class="h-card" href="' . $link . '">' .
$person_tag . $name . '</a>';
return '<span class="h-card"><a class="p-name u-url" ' .
'href="' . $link . '">' . $person_tag . $name . '</a> ' .
$repost . '</span>';
}
}
return isset($data['value']) ? $data['value'] : '';
......@@ -601,21 +610,7 @@ class SimplePie_Parser
}
// Images, audio and video are all stored using Media RSS.
if (isset($entry['properties']['photo'][0])) {
$photo_list = array();
foreach ($entry['properties']['photo'] as $photo) {
if (!empty($photo) && strpos($description, $photo) === false) {
$photo_list[] = $photo;
}
}
if (count($photo_list) === 0) {
// All photos were found to be duplicated in description, so assume
// this is a photo post and display the lightbox. Add a wrapper
// around the existing description so those photos can be hidden.
$description = '<div class="photo-hidden">' . $description.'</div>';
$photo_list = $entry['properties']['photo'];
}
foreach ($photo_list as $photo) {
// Find the type of image.
if (preg_match('/\.([a-z]+)$/', strtolower($photo), $match)) {
$type = '';
if (in_array($match[1], array('gif', 'jpeg', 'png'))) {
......@@ -733,7 +728,7 @@ class SimplePie_Parser
array('data' => $this->parse_hcard($category, true));
}
}
$item['category'] = array($category_list);
$item['category'] = $category_list;
}
if (isset($entry['properties']['published'][0])) {
$timestamp = strtotime($entry['properties']['published'][0]);
......
......@@ -455,17 +455,22 @@ abstract class Base {
$this->Log('Base->LookupNickname: ' . $mysqli->error);
}
$mysqli->close();
if ($us_photo === '') {
// Assume here that there was no match and provide some simplified urls
// for facebook and twitter feeds knowing they have path segments.
if (strpos($us_url, 'https://twitter.com') === 0 &&
preg_match('/^(https:\/\/twitter.com\/[^\/]+)/i', $us_url, $match)) {
$us_url = $match[1];
$us_photo = $this->user->config->Secure() ? 'https://' : 'http://';
$us_photo .= $this->user->config->ServerName() . '/images/twitter.png';
}
else if (strpos($us_url, 'https://www.facebook.com') === 0 &&
preg_match('/^(https:\/\/www.facebook.com\/[^\/]+)/i',
$us_url, $match)) {
$us_url = $match[1];
$us_photo = $this->user->config->Secure() ? 'https://' : 'http://';
$us_photo .= $this->user->config->ServerName() . '/images/facebook.png';
}
}
return [$us_name, $us_url, $us_photo];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment