mirror of https://github.com/friendica/friendica
Merge pull request #7749 from MrPetovan/bug/7740-frio-hovercard-add-unfollow
[frio] Rework hovercard actions
This commit is contained in:
commit
ebef0d5cd4
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Name: Frio Hovercard
|
||||
* Description: Hovercard addon for the frio theme
|
||||
* Version: 0.1
|
||||
* Author: Rabuzarus <https://github.com/rabuzarus>
|
||||
* License: GNU AFFERO GENERAL PUBLIC LICENSE (Version 3)
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Util\Proxy as ProxyUtils;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
function hovercard_init(App $a)
|
||||
{
|
||||
// Just for testing purposes
|
||||
$_GET['mode'] = 'minimal';
|
||||
}
|
||||
|
||||
function hovercard_content()
|
||||
{
|
||||
$profileurl = $_REQUEST['profileurl'] ?? '';
|
||||
$datatype = ($_REQUEST['datatype'] ?? '') ?: 'json';
|
||||
|
||||
// Get out if the system doesn't have public access allowed
|
||||
if (intval(Config::get('system', 'block_public'))) {
|
||||
throw new \Friendica\Network\HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
// Return the raw content of the template. We use this to make templates usable for js functions.
|
||||
// Look at hovercard.js (function getHoverCardTemplate()).
|
||||
// This part should be moved in its own module. Maybe we could make more templates accessible.
|
||||
// (We need to discuss possible security leaks before doing this)
|
||||
if ($datatype == 'tpl') {
|
||||
$templatecontent = get_template_content('hovercard.tpl');
|
||||
echo $templatecontent;
|
||||
exit();
|
||||
}
|
||||
|
||||
// If a contact is connected the url is internally changed to 'redir/CID'. We need the pure url to search for
|
||||
// the contact. So we strip out the contact id from the internal url and look in the contact table for
|
||||
// the real url (nurl)
|
||||
if (strpos($profileurl, 'redir/') === 0) {
|
||||
$cid = intval(substr($profileurl, 6));
|
||||
$remote_contact = DBA::selectFirst('contact', ['nurl'], ['id' => $cid]);
|
||||
$profileurl = $remote_contact['nurl'] ?? '';
|
||||
}
|
||||
|
||||
$contact = [];
|
||||
// if it's the url containing https it should be converted to http
|
||||
$nurl = Strings::normaliseLink(GContact::cleanContactUrl($profileurl));
|
||||
if (!$nurl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Search for contact data
|
||||
// Look if the local user has got the contact
|
||||
if (local_user()) {
|
||||
$contact = Contact::getDetailsByURL($nurl, local_user());
|
||||
}
|
||||
|
||||
// If not then check the global user
|
||||
if (!count($contact)) {
|
||||
$contact = Contact::getDetailsByURL($nurl);
|
||||
}
|
||||
|
||||
// Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url
|
||||
if (!count($contact) && local_user()) {
|
||||
$nurl = Strings::normaliseLink($profileurl);
|
||||
$contact = Contact::getDetailsByURL($nurl, local_user());
|
||||
}
|
||||
|
||||
if (!count($contact)) {
|
||||
$nurl = Strings::normaliseLink($profileurl);
|
||||
$contact = Contact::getDetailsByURL($nurl);
|
||||
}
|
||||
|
||||
if (!count($contact)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the photo_menu - the menu if possible contact actions
|
||||
if (local_user()) {
|
||||
$actions = Contact::photoMenu($contact);
|
||||
} else {
|
||||
$actions = [];
|
||||
}
|
||||
|
||||
// Move the contact data to the profile array so we can deliver it to
|
||||
$profile = [
|
||||
'name' => $contact['name'],
|
||||
'nick' => $contact['nick'],
|
||||
'addr' => ($contact['addr'] ?? '') ?: $contact['url'],
|
||||
'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
|
||||
'url' => Contact::magicLink($contact['url']),
|
||||
'nurl' => $contact['nurl'], // We additionally store the nurl as identifier
|
||||
'location' => $contact['location'],
|
||||
'gender' => $contact['gender'],
|
||||
'about' => $contact['about'],
|
||||
'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']),
|
||||
'tags' => $contact['keywords'],
|
||||
'bd' => $contact['birthday'] <= DBA::NULL_DATE ? '' : $contact['birthday'],
|
||||
'account_type' => Contact::getAccountType($contact),
|
||||
'actions' => $actions,
|
||||
];
|
||||
if ($datatype == 'html') {
|
||||
$tpl = Renderer::getMarkupTemplate('hovercard.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$profile' => $profile,
|
||||
]);
|
||||
|
||||
return $o;
|
||||
} else {
|
||||
System::jsonExit($profile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the raw content of a template file
|
||||
*
|
||||
* @param string $template The name of the template
|
||||
* @param string $root Directory of the template
|
||||
*
|
||||
* @return string|bool Output the raw content if existent, otherwise false
|
||||
* @throws Exception
|
||||
*/
|
||||
function get_template_content($template, $root = '')
|
||||
{
|
||||
// We load the whole template system to get the filename.
|
||||
// Maybe we can do it a little bit smarter if I get time.
|
||||
$templateEngine = Renderer::getTemplateEngine();
|
||||
$template = $templateEngine->getTemplateFile($template, $root);
|
||||
|
||||
$filename = $template->filename;
|
||||
|
||||
// Get the content of the template file
|
||||
if (file_exists($filename)) {
|
||||
$content = file_get_contents($filename);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -983,41 +983,43 @@ class Contact extends BaseObject
|
|||
|
||||
$ssl_url = str_replace('http://', 'https://', $url);
|
||||
|
||||
$nurl = Strings::normaliseLink($url);
|
||||
|
||||
// Fetch contact data from the contact table for the given user
|
||||
$s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
|
||||
FROM `contact` WHERE `nurl` = ? AND `uid` = ?", Strings::normaliseLink($url), $uid);
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `nurl` = ? AND `uid` = ?", $nurl, $uid);
|
||||
$r = DBA::toArray($s);
|
||||
|
||||
// Fetch contact data from the contact table for the given user, checking with the alias
|
||||
if (!DBA::isResult($r)) {
|
||||
$s = DBA::p("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
|
||||
FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", Strings::normaliseLink($url), $url, $ssl_url, $uid);
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = ?", $nurl, $url, $ssl_url, $uid);
|
||||
$r = DBA::toArray($s);
|
||||
}
|
||||
|
||||
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
|
||||
if (!DBA::isResult($r)) {
|
||||
$s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
|
||||
FROM `contact` WHERE `nurl` = ? AND `uid` = 0", Strings::normaliseLink($url));
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `nurl` = ? AND `uid` = 0", $nurl);
|
||||
$r = DBA::toArray($s);
|
||||
}
|
||||
|
||||
// Fetch the data from the contact table with "uid=0" (which is filled automatically) - checked with the alias
|
||||
if (!DBA::isResult($r)) {
|
||||
$s = DBA::p("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
|
||||
FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", Strings::normaliseLink($url), $url, $ssl_url);
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `alias` IN (?, ?, ?) AND `uid` = 0", $nurl, $url, $ssl_url);
|
||||
$r = DBA::toArray($s);
|
||||
}
|
||||
|
||||
// Fetch the data from the gcontact table
|
||||
if (!DBA::isResult($r)) {
|
||||
$s = DBA::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
|
||||
FROM `gcontact` WHERE `nurl` = ?", Strings::normaliseLink($url));
|
||||
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending`
|
||||
FROM `gcontact` WHERE `nurl` = ?", $nurl);
|
||||
$r = DBA::toArray($s);
|
||||
}
|
||||
|
||||
|
@ -1121,7 +1123,7 @@ class Contact extends BaseObject
|
|||
|
||||
// Fetch contact data from the contact table for the given user
|
||||
$r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `addr` = '%s' AND `uid` = %d AND NOT `deleted`",
|
||||
DBA::escape($addr),
|
||||
intval($uid)
|
||||
|
@ -1129,7 +1131,7 @@ class Contact extends BaseObject
|
|||
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
|
||||
if (!DBA::isResult($r)) {
|
||||
$r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
|
||||
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`, `rel`, `pending`
|
||||
FROM `contact` WHERE `addr` = '%s' AND `uid` = 0 AND NOT `deleted`",
|
||||
DBA::escape($addr)
|
||||
);
|
||||
|
@ -1138,7 +1140,7 @@ class Contact extends BaseObject
|
|||
// Fetch the data from the gcontact table
|
||||
if (!DBA::isResult($r)) {
|
||||
$r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
|
||||
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
|
||||
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`, 2 AS `rel`, 0 AS `pending`
|
||||
FROM `gcontact` WHERE `addr` = '%s'",
|
||||
DBA::escape($addr)
|
||||
);
|
||||
|
@ -1225,28 +1227,40 @@ class Contact extends BaseObject
|
|||
$contact_drop_link = System::baseUrl() . '/contact/' . $contact['id'] . '/drop?confirm=1';
|
||||
}
|
||||
|
||||
$follow_link = '';
|
||||
$unfollow_link = '';
|
||||
if (in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
|
||||
if ($contact['uid'] && in_array($contact['rel'], [self::SHARING, self::FRIEND])) {
|
||||
$unfollow_link = 'unfollow?url=' . urlencode($contact['url']);
|
||||
} elseif(!$contact['pending']) {
|
||||
$follow_link = 'follow?url=' . urlencode($contact['url']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu array:
|
||||
* "name" => [ "Label", "link", (bool)Should the link opened in a new tab? ]
|
||||
*/
|
||||
if (empty($contact['uid'])) {
|
||||
$connlnk = 'follow/?url=' . $contact['url'];
|
||||
$menu = [
|
||||
'profile' => [L10n::t('View Profile'), $profile_link, true],
|
||||
'network' => [L10n::t('Network Posts'), $posts_link, false],
|
||||
'edit' => [L10n::t('View Contact'), $contact_url, false],
|
||||
'follow' => [L10n::t('Connect/Follow'), $connlnk, true],
|
||||
'profile' => [L10n::t('View Profile') , $profile_link , true],
|
||||
'network' => [L10n::t('Network Posts') , $posts_link , false],
|
||||
'edit' => [L10n::t('View Contact') , $contact_url , false],
|
||||
'follow' => [L10n::t('Connect/Follow'), $follow_link , true],
|
||||
'unfollow'=> [L10n::t('UnFollow') , $unfollow_link, true],
|
||||
];
|
||||
} else {
|
||||
$menu = [
|
||||
'status' => [L10n::t('View Status'), $status_link, true],
|
||||
'profile' => [L10n::t('View Profile'), $profile_link, true],
|
||||
'photos' => [L10n::t('View Photos'), $photos_link, true],
|
||||
'network' => [L10n::t('Network Posts'), $posts_link, false],
|
||||
'edit' => [L10n::t('View Contact'), $contact_url, false],
|
||||
'drop' => [L10n::t('Drop Contact'), $contact_drop_link, false],
|
||||
'pm' => [L10n::t('Send PM'), $pm_url, false],
|
||||
'poke' => [L10n::t('Poke'), $poke_link, false],
|
||||
'status' => [L10n::t('View Status') , $status_link , true],
|
||||
'profile' => [L10n::t('View Profile') , $profile_link , true],
|
||||
'photos' => [L10n::t('View Photos') , $photos_link , true],
|
||||
'network' => [L10n::t('Network Posts') , $posts_link , false],
|
||||
'edit' => [L10n::t('View Contact') , $contact_url , false],
|
||||
'drop' => [L10n::t('Drop Contact') , $contact_drop_link, false],
|
||||
'pm' => [L10n::t('Send PM') , $pm_url , false],
|
||||
'poke' => [L10n::t('Poke') , $poke_link , false],
|
||||
'follow' => [L10n::t('Connect/Follow'), $follow_link , true],
|
||||
'unfollow'=> [L10n::t('UnFollow') , $unfollow_link , true],
|
||||
];
|
||||
|
||||
if (!empty($contact['pending'])) {
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Module\Contact;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Session;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\GContact;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Util\Proxy;
|
||||
|
||||
/**
|
||||
* Asynchronous HTML fragment provider for frio contact hovercards
|
||||
*/
|
||||
class Hovercard extends BaseModule
|
||||
{
|
||||
public static function rawContent()
|
||||
{
|
||||
$contact_url = $_REQUEST['url'] ?? '';
|
||||
|
||||
// Get out if the system doesn't have public access allowed
|
||||
if (Config::get('system', 'block_public') && !Session::isAuthenticated()) {
|
||||
throw new HTTPException\ForbiddenException();
|
||||
}
|
||||
|
||||
// If a contact is connected the url is internally changed to 'redir/CID'. We need the pure url to search for
|
||||
// the contact. So we strip out the contact id from the internal url and look in the contact table for
|
||||
// the real url (nurl)
|
||||
if (strpos($contact_url, 'redir/') === 0) {
|
||||
$cid = intval(substr($contact_url, 6));
|
||||
$remote_contact = Contact::selectFirst(['nurl'], ['id' => $cid]);
|
||||
$contact_url = $remote_contact['nurl'] ?? '';
|
||||
}
|
||||
|
||||
$contact = [];
|
||||
|
||||
// if it's the url containing https it should be converted to http
|
||||
$contact_nurl = Strings::normaliseLink(GContact::cleanContactUrl($contact_url));
|
||||
if (!$contact_nurl) {
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
// Search for contact data
|
||||
// Look if the local user has got the contact
|
||||
if (Session::isAuthenticated()) {
|
||||
$contact = Contact::getDetailsByURL($contact_nurl, local_user());
|
||||
}
|
||||
|
||||
// If not then check the global user
|
||||
if (!count($contact)) {
|
||||
$contact = Contact::getDetailsByURL($contact_nurl);
|
||||
}
|
||||
|
||||
// Feeds url could have been destroyed through "cleanContactUrl", so we now use the original url
|
||||
if (!count($contact) && Session::isAuthenticated()) {
|
||||
$contact_nurl = Strings::normaliseLink($contact_url);
|
||||
$contact = Contact::getDetailsByURL($contact_nurl, local_user());
|
||||
}
|
||||
|
||||
if (!count($contact)) {
|
||||
$contact_nurl = Strings::normaliseLink($contact_url);
|
||||
$contact = Contact::getDetailsByURL($contact_nurl);
|
||||
}
|
||||
|
||||
if (!count($contact)) {
|
||||
throw new HTTPException\NotFoundException();
|
||||
}
|
||||
|
||||
// Get the photo_menu - the menu if possible contact actions
|
||||
if (Session::isAuthenticated()) {
|
||||
$actions = Contact::photoMenu($contact);
|
||||
} else {
|
||||
$actions = [];
|
||||
}
|
||||
|
||||
// Move the contact data to the profile array so we can deliver it to
|
||||
$tpl = Renderer::getMarkupTemplate('hovercard.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$profile' => [
|
||||
'name' => $contact['name'],
|
||||
'nick' => $contact['nick'],
|
||||
'addr' => $contact['addr'] ?: $contact['url'],
|
||||
'thumb' => Proxy::proxifyUrl($contact['thumb'], false, Proxy::SIZE_THUMB),
|
||||
'url' => Contact::magicLink($contact['url']),
|
||||
'nurl' => $contact['nurl'],
|
||||
'location' => $contact['location'],
|
||||
'gender' => $contact['gender'],
|
||||
'about' => $contact['about'],
|
||||
'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']),
|
||||
'tags' => $contact['keywords'],
|
||||
'bd' => $contact['birthday'] <= DBA::NULL_DATE ? '' : $contact['birthday'],
|
||||
'account_type' => Contact::getAccountType($contact),
|
||||
'actions' => $actions,
|
||||
],
|
||||
]);
|
||||
|
||||
echo $o;
|
||||
exit();
|
||||
}
|
||||
}
|
|
@ -90,7 +90,9 @@ return [
|
|||
'/blocked' => [Module\Contact::class, [R::GET]],
|
||||
'/hidden' => [Module\Contact::class, [R::GET]],
|
||||
'/ignored' => [Module\Contact::class, [R::GET]],
|
||||
'/hovercard' => [Module\Contact\Hovercard::class, [R::GET]],
|
||||
],
|
||||
|
||||
'/credits' => [Module\Credits::class, [R::GET]],
|
||||
'/delegation'=> [Module\Delegation::class, [R::GET, R::POST]],
|
||||
'/dirfind' => [Module\Search\Directory::class, [R::GET]],
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
{{if $profile.actions.network}}<a class="btn btn-labeled btn-primary btn-sm" href="{{$profile.actions.network.1}}" aria-label="{{$profile.actions.network.0}}" title="{{$profile.actions.network.0}}"><i class="fa fa-cloud" aria-hidden="true"></i></a>{{/if}}
|
||||
{{if $profile.actions.edit}}<a class="btn btn-labeled btn-primary btn-sm" href="{{$profile.actions.edit.1}}" aria-label="{{$profile.actions.edit.0}}" title="{{$profile.actions.edit.0}}"><i class="fa fa-user" aria-hidden="true"></i></a>{{/if}}
|
||||
{{if $profile.actions.follow}}<a class="btn btn-labeled btn-primary btn-sm" href="{{$profile.actions.follow.1}}" aria-label="{{$profile.actions.follow.0}}" title="{{$profile.actions.follow.0}}"><i class="fa fa-user-plus" aria-hidden="true"></i></a>{{/if}}
|
||||
{{if $profile.actions.unfollow}}<a class="btn btn-labeled btn-primary btn-sm" href="{{$profile.actions.unfollow.1}}" aria-label="{{$profile.actions.unfollow.0}}" title="{{$profile.actions.unfollow.0}}"><i class="fa fa-user-times" aria-hidden="true"></i></a>{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1800,6 +1800,28 @@ aside .panel-body {
|
|||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Contact avatar click card */
|
||||
.userinfo.click-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.userinfo.click-card > *:hover:after {
|
||||
content: '⌄';
|
||||
color: #bebebe;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
background-color: #ffffff;
|
||||
text-align: center;
|
||||
line-height: 40%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 33%;
|
||||
height: 33%;
|
||||
opacity: .8;
|
||||
border-radius: 0 0 40% 0;
|
||||
}
|
||||
|
||||
/* The lock symbol popup */
|
||||
#panel {
|
||||
position: absolute;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -7,282 +7,179 @@
|
|||
* It is licensed under the GNU Affero General Public License <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
$(document).ready(function(){
|
||||
// Elements with the class "userinfo" will get a hover-card.
|
||||
// Note that this elements does need a href attribute which links to
|
||||
// a valid profile url
|
||||
$("body").on("mouseover", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function(e) {
|
||||
var timeNow = new Date().getTime();
|
||||
removeAllhoverCards(e,timeNow);
|
||||
var hoverCardData = false;
|
||||
var hrefAttr = false;
|
||||
var targetElement = $(this);
|
||||
$(document).ready(function () {
|
||||
let $body = $('body');
|
||||
// Prevents normal click action on click hovercard elements
|
||||
$body.on('click', '.userinfo.click-card', function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
// This event listener needs to be declared before the one that removes
|
||||
// all cards so that we can stop the immediate propagation of the event
|
||||
// Since the manual popover appears instantly and the hovercard removal is
|
||||
// on a 100ms delay, leaving event propagation immediately hides any click hovercard
|
||||
$body.on('mousedown', '.userinfo.click-card', function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
let timeNow = new Date().getTime();
|
||||
|
||||
let contactUrl = false;
|
||||
let targetElement = $(this);
|
||||
|
||||
// get href-attribute
|
||||
if(targetElement.is('[href]')) {
|
||||
hrefAttr = targetElement.attr('href');
|
||||
if (targetElement.is('[href]')) {
|
||||
contactUrl = targetElement.attr('href');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
// no hovercard for anchor links
|
||||
if (contactUrl.substring(0, 1) === '#') {
|
||||
return true;
|
||||
}
|
||||
|
||||
openHovercard(targetElement, contactUrl, timeNow);
|
||||
});
|
||||
|
||||
// hover cards should be removed very easily, e.g. when any of these events happens
|
||||
$body.on('mouseleave touchstart scroll mousedown submit keydown', function (e) {
|
||||
// remove hover card only for desktiop user, since on mobile we open the hovercards
|
||||
// by click event insteadof hover
|
||||
removeAllHovercards(e, new Date().getTime());
|
||||
});
|
||||
|
||||
$body.on('mouseover', '.userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a', function (e) {
|
||||
let timeNow = new Date().getTime();
|
||||
removeAllHovercards(e, timeNow);
|
||||
let contactUrl = false;
|
||||
let targetElement = $(this);
|
||||
|
||||
// get href-attribute
|
||||
if (targetElement.is('[href]')) {
|
||||
contactUrl = targetElement.attr('href');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
// no hover card if the element has the no-hover-card class
|
||||
if(targetElement.hasClass('no-hover-card')) {
|
||||
if (targetElement.hasClass('no-hover-card')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// no hovercard for anchor links
|
||||
if(hrefAttr.substring(0,1) == '#') {
|
||||
if (contactUrl.substring(0, 1) === '#') {
|
||||
return true;
|
||||
}
|
||||
|
||||
targetElement.attr('data-awaiting-hover-card',timeNow);
|
||||
targetElement.attr('data-awaiting-hover-card', timeNow);
|
||||
|
||||
// Take link href attribute as link to the profile
|
||||
var profileurl = hrefAttr;
|
||||
// the url to get the contact and template data
|
||||
var url = baseurl + "/hovercard";
|
||||
// Delay until the hover-card does appear
|
||||
setTimeout(function () {
|
||||
if (
|
||||
targetElement.is(':hover')
|
||||
&& parseInt(targetElement.attr('data-awaiting-hover-card'), 10) === timeNow
|
||||
&& $('.hovercard').length === 0
|
||||
) {
|
||||
openHovercard(targetElement, contactUrl, timeNow);
|
||||
}
|
||||
}, 500);
|
||||
}).on('mouseleave', '.userinfo.hover-card, .wall-item-responses a, .wall-item-bottom .mention a', function (e) { // action when mouse leaves the hover-card
|
||||
removeAllHovercards(e, new Date().getTime());
|
||||
});
|
||||
|
||||
// store the title in an other data attribute beause bootstrap
|
||||
// popover destroys the title.attribute. We can restore it later
|
||||
var title = targetElement.attr("title");
|
||||
targetElement.attr({"data-orig-title": title, title: ""});
|
||||
// if we're hovering a hover card, give it a class, so we don't remove it
|
||||
$body.on('mouseover', '.hovercard', function (e) {
|
||||
$(this).addClass('dont-remove-card');
|
||||
});
|
||||
|
||||
// if the device is a mobile open the hover card by click and not by hover
|
||||
if(typeof is_mobile != "undefined") {
|
||||
targetElement[0].removeAttribute("href");
|
||||
var hctrigger = 'click';
|
||||
} else {
|
||||
var hctrigger = 'manual';
|
||||
};
|
||||
$body.on('mouseleave', '.hovercard', function (e) {
|
||||
$(this).removeClass('dont-remove-card');
|
||||
$(this).popover('hide');
|
||||
});
|
||||
}); // End of $(document).ready
|
||||
|
||||
// removes all hover cards
|
||||
function removeAllHovercards(event, priorTo) {
|
||||
// don't remove hovercards until after 100ms, so user have time to move the cursor to it (which gives it the dont-remove-card class)
|
||||
setTimeout(function () {
|
||||
$.each($('.hovercard'), function () {
|
||||
let title = $(this).attr('data-orig-title');
|
||||
// don't remove card if it was created after removeAllhoverCards() was called
|
||||
if ($(this).data('card-created') < priorTo) {
|
||||
// don't remove it if we're hovering it right now!
|
||||
if (!$(this).hasClass('dont-remove-card')) {
|
||||
let $handle = $('[data-hover-card-active="' + $(this).data('card-created') + '"]');
|
||||
$handle.removeAttr('data-hover-card-active');
|
||||
|
||||
// Restoring the popover handle title
|
||||
let title = $handle.attr('data-orig-title');
|
||||
$handle.attr({'data-orig-title': '', title: title});
|
||||
|
||||
$(this).popover('hide');
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function openHovercard(targetElement, contactUrl, timeNow) {
|
||||
// store the title in a data attribute because Bootstrap
|
||||
// popover destroys the title attribute.
|
||||
let title = targetElement.attr('title');
|
||||
targetElement.attr({'data-orig-title': title, title: ''});
|
||||
|
||||
// Timeout until the hover-card does appear
|
||||
setTimeout(function(){
|
||||
if(targetElement.is(":hover") && parseInt(targetElement.attr('data-awaiting-hover-card'),10) == timeNow) {
|
||||
if($('.hovercard').length == 0) { // no card if there already is one open
|
||||
// get an additional data atribute if the card is active
|
||||
targetElement.attr('data-hover-card-active',timeNow);
|
||||
targetElement.attr('data-hover-card-active', timeNow);
|
||||
// get the whole html content of the hover card and
|
||||
// push it to the bootstrap popover
|
||||
getHoverCardContent(profileurl, url, function(data){
|
||||
if(data) {
|
||||
getHoverCardContent(contactUrl, function (data) {
|
||||
if (data) {
|
||||
targetElement.popover({
|
||||
html: true,
|
||||
placement: function () {
|
||||
// Calculate the placement of the the hovercard (if top or bottom)
|
||||
// The placement depence on the distance between window top and the element
|
||||
// which triggers the hover-card
|
||||
var get_position = $(targetElement).offset().top - $(window).scrollTop();
|
||||
if (get_position < 270 ){
|
||||
return "bottom";
|
||||
let get_position = $(targetElement).offset().top - $(window).scrollTop();
|
||||
if (get_position < 270) {
|
||||
return 'bottom';
|
||||
}
|
||||
return "top";
|
||||
return 'top';
|
||||
},
|
||||
trigger: hctrigger,
|
||||
trigger: 'manual',
|
||||
template: '<div class="popover hovercard" data-card-created="' + timeNow + '"><div class="arrow"></div><div class="popover-content hovercard-content"></div></div>',
|
||||
content: data,
|
||||
container: "body",
|
||||
container: 'body',
|
||||
sanitizeFn: function (content) {
|
||||
return DOMPurify.sanitize(content)
|
||||
},
|
||||
}).popover('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}).on("mouseleave", ".userinfo, .wall-item-responses a, .wall-item-bottom .mention a", function(e) { // action when mouse leaves the hover-card
|
||||
var timeNow = new Date().getTime();
|
||||
// copy the original title to the title atribute
|
||||
var title = $(this).attr("data-orig-title");
|
||||
$(this).attr({"data-orig-title": "", title: title});
|
||||
removeAllhoverCards(e,timeNow);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// hover cards should be removed very easily, e.g. when any of these events happen
|
||||
$('body').on("mouseleave touchstart scroll click dblclick mousedown mouseup submit keydown keypress keyup", function(e){
|
||||
// remove hover card only for desktiop user, since on mobile we openen the hovercards
|
||||
// by click event insteadof hover
|
||||
if(typeof is_mobile == "undefined") {
|
||||
var timeNow = new Date().getTime();
|
||||
removeAllhoverCards(e,timeNow);
|
||||
};
|
||||
});
|
||||
|
||||
// if we're hovering a hover card, give it a class, so we don't remove it
|
||||
$('body').on('mouseover','.hovercard', function(e) {
|
||||
$(this).addClass('dont-remove-card');
|
||||
});
|
||||
$('body').on('mouseleave','.hovercard', function(e) {
|
||||
$(this).removeClass('dont-remove-card');
|
||||
$(this).popover("hide");
|
||||
});
|
||||
|
||||
}); // End of $(document).ready
|
||||
|
||||
// removes all hover cards
|
||||
function removeAllhoverCards(event,priorTo) {
|
||||
// don't remove hovercards until after 100ms, so user have time to move the cursor to it (which gives it the dont-remove-card class)
|
||||
setTimeout(function(){
|
||||
$.each($('.hovercard'),function(){
|
||||
var title = $(this).attr("data-orig-title");
|
||||
// don't remove card if it was created after removeAllhoverCards() was called
|
||||
if($(this).data('card-created') < priorTo) {
|
||||
// don't remove it if we're hovering it right now!
|
||||
if(!$(this).hasClass('dont-remove-card')) {
|
||||
$('[data-hover-card-active="' + $(this).data('card-created') + '"]').removeAttr('data-hover-card-active');
|
||||
$(this).popover("hide");
|
||||
}
|
||||
}
|
||||
});
|
||||
},100);
|
||||
}
|
||||
|
||||
// Ajax request to get json contact data
|
||||
function getContactData(purl, url, actionOnSuccess) {
|
||||
var postdata = {
|
||||
mode : 'none',
|
||||
profileurl : purl,
|
||||
datatype : 'json',
|
||||
getHoverCardContent.cache = {};
|
||||
|
||||
function getHoverCardContent(contact_url, callback) {
|
||||
let postdata = {
|
||||
url: contact_url,
|
||||
};
|
||||
|
||||
// Normalize and clean the profile so we can use a standardized url
|
||||
// as key for the cache
|
||||
var nurl = cleanContactUrl(purl).normalizeLink();
|
||||
let nurl = cleanContactUrl(contact_url).normalizeLink();
|
||||
|
||||
// If the contact is allready in the cache use the cached result instead
|
||||
// If the contact is already in the cache use the cached result instead
|
||||
// of doing a new ajax request
|
||||
if(nurl in getContactData.cache) {
|
||||
setTimeout(function() { actionOnSuccess(getContactData.cache[nurl]); } , 1);
|
||||
if (nurl in getHoverCardContent.cache) {
|
||||
callback(getHoverCardContent.cache[nurl]);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
url: baseurl + '/contact/hovercard',
|
||||
data: postdata,
|
||||
dataType: "json",
|
||||
success: function(data, textStatus, request){
|
||||
// Check if the nurl (normalized profile url) is present and store it to the cache
|
||||
// The nurl will be the identifier in the object
|
||||
if(data.nurl.length > 0) {
|
||||
// Test if the contact is allready connected with the user (if url containing
|
||||
// the expression ("redir/") We will store different cache keys
|
||||
if((data.url.search("redir/")) >= 0 ) {
|
||||
var key = data.url;
|
||||
} else {
|
||||
var key = data.nurl;
|
||||
}
|
||||
getContactData.cache[key] = data;
|
||||
}
|
||||
actionOnSuccess(data, url, request);
|
||||
},
|
||||
error: function(data) {
|
||||
actionOnSuccess(false, data, url);
|
||||
}
|
||||
});
|
||||
}
|
||||
getContactData.cache = {};
|
||||
|
||||
// Get hover-card template data and the contact-data and transform it with
|
||||
// the help of jSmart. At the end we have full html content of the hovercard
|
||||
function getHoverCardContent(purl, url, callback) {
|
||||
// fetch the raw content of the template
|
||||
getHoverCardTemplate(url, function(stpl) {
|
||||
var template = unescape(stpl);
|
||||
|
||||
// get the contact data
|
||||
getContactData (purl, url, function(data) {
|
||||
if(typeof template != 'undefined') {
|
||||
// get the hover-card variables
|
||||
var variables = getHoverCardVariables(data);
|
||||
var tpl;
|
||||
|
||||
// use friendicas template delimiters instead of
|
||||
// the original one
|
||||
jSmart.prototype.left_delimiter = '{{';
|
||||
jSmart.prototype.right_delimiter = '}}';
|
||||
|
||||
// create a new jSmart instant with the raw content
|
||||
// of the template
|
||||
var tpl = new jSmart (template);
|
||||
// insert the variables content into the template content
|
||||
var HoverCardContent = tpl.fetch(variables);
|
||||
|
||||
callback(HoverCardContent);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// This is interisting. this pice of code ajax request are done asynchron.
|
||||
// To make it work getHOverCardTemplate() and getHOverCardData have to return it's
|
||||
// data (no succes handler for each of this). I leave it here, because it could be useful.
|
||||
// https://lostechies.com/joshuaflanagan/2011/10/20/coordinating-multiple-ajax-requests-with-jquery-when/
|
||||
// $.when(
|
||||
// getHoverCardTemplate(url),
|
||||
// getContactData (term, url )
|
||||
//
|
||||
// ).done(function(template, profile){
|
||||
// if(typeof template != 'undefined') {
|
||||
// var variables = getHoverCardVariables(profile);
|
||||
//
|
||||
// jSmart.prototype.left_delimiter = '{{';
|
||||
// jSmart.prototype.right_delimiter = '}}';
|
||||
// var tpl = new jSmart (template);
|
||||
// var html = tpl.fetch(variables);
|
||||
//
|
||||
// return html;
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
// Ajax request to get the raw template content
|
||||
function getHoverCardTemplate (url, callback) {
|
||||
var postdata = {
|
||||
mode: 'none',
|
||||
datatype: 'tpl'
|
||||
};
|
||||
|
||||
// Look if we have the template already in the cace, so we don't have
|
||||
// request it again
|
||||
if('hovercard' in getHoverCardTemplate.cache) {
|
||||
setTimeout(function() { callback(getHoverCardTemplate.cache['hovercard']); } , 1);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
data: postdata,
|
||||
success: function(data, textStatus) {
|
||||
// write the data in the cache
|
||||
getHoverCardTemplate.cache['hovercard'] = data;
|
||||
success: function (data, textStatus, request) {
|
||||
getHoverCardContent.cache[nurl] = data;
|
||||
callback(data);
|
||||
}
|
||||
}).fail(function () {callback([]); });
|
||||
}
|
||||
getHoverCardTemplate.cache = {};
|
||||
|
||||
// The Variables used for the template
|
||||
function getHoverCardVariables(object) {
|
||||
var profile = {
|
||||
name: object.name,
|
||||
nick: object.nick,
|
||||
addr: object.addr,
|
||||
thumb: object.thumb,
|
||||
url: object.url,
|
||||
nurl: object.nurl,
|
||||
location: object.location,
|
||||
gender: object.gender,
|
||||
about: object.about,
|
||||
network: object.network,
|
||||
tags: object.tags,
|
||||
bd: object.bd,
|
||||
account_type: object.account_type,
|
||||
actions: object.actions
|
||||
};
|
||||
|
||||
var variables = { profile: profile};
|
||||
|
||||
return variables;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
<div class="event-card-profile-name profile-entry-name">
|
||||
<a href="{{$author_link}}" class="userinfo">{{$author_name}}</a>
|
||||
<a href="{{$author_link}}" class="userinfo hover-card">{{$author_name}}</a>
|
||||
</div>
|
||||
{{if $location.map}}
|
||||
<div id="event-location-map-{{$id}}" class="event-location-map">{{$location.map nofilter}}</div>
|
||||
|
|
|
@ -75,7 +75,6 @@
|
|||
<script type="text/javascript" src="view/theme/frio/frameworks/justifiedGallery/jquery.justifiedGallery.min.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/bootstrap-colorpicker/js/bootstrap-colorpicker.min.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/flexMenu/flexmenu.custom.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/jsmart/jsmart.custom.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/jquery-scrollspy/jquery-scrollspy.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/autosize/autosize.min.js"></script>
|
||||
<script type="text/javascript" src="view/theme/frio/frameworks/sticky-kit/jquery.sticky-kit.min.js"></script>
|
||||
|
|
|
@ -285,7 +285,7 @@
|
|||
<ul id="nav-notifications-template" class="media-list" style="display:none;" rel="template">
|
||||
<li class="{4} notif-entry">
|
||||
<div class="notif-entry-wrapper media">
|
||||
<div class="notif-photo-wrapper media-object pull-left"><a href="{6}" class="userinfo"><img data-src="{1}"></a></div>
|
||||
<div class="notif-photo-wrapper media-object pull-left"><a href="{6}" class="userinfo click-card"><img data-src="{1}"></a></div>
|
||||
<a href="{0}" class="notif-desc-wrapper media-body">
|
||||
{2}
|
||||
<div><time class="notif-when time" data-toggle="tooltip" title="{5}">{3}</time></div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
<div class="notif-item {{if !$item_seen}}unseen{{/if}} {{$item_label}} media">
|
||||
<div class="notif-photo-wrapper media-object pull-left">
|
||||
<a class="userinfo" href="{{$item_url}}"><img src="{{$item_image}}" class="notif-image"></a>
|
||||
<a class="userinfo click-card" href="{{$item_url}}"><img src="{{$item_image}}" class="notif-image"></a>
|
||||
</div>
|
||||
<div class="notif-desc-wrapper media-body">
|
||||
<a href="{{$item_link}}">
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
{{* avatar picture *}}
|
||||
<div class="contact-photo-wrapper mframe p-author h-card pull-left">
|
||||
<a class="userinfo u-url" id="wall-item-photo-menu-{{$id}}" href="{{$profile_url}}">
|
||||
<a class="userinfo click-card u-url" id="wall-item-photo-menu-{{$id}}" href="{{$profile_url}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$thumb}}" class="contact-photo-xs media-object p-name u-photo" id="wall-item-photo-{{$id}}" alt="{{$name}}" />
|
||||
</div>
|
||||
|
@ -33,7 +33,7 @@
|
|||
{{* the header with the comment author name *}}
|
||||
<div role="heading " class="contact-info-comment">
|
||||
<h5 class="media-heading">
|
||||
<a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-name-link userinfo"><span class="btn-link">{{$name}}</span></a>
|
||||
<a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-name-link userinfo hover-card"><span class="btn-link">{{$name}}</span></a>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -74,14 +74,14 @@
|
|||
{{* The avatar picture and the photo-menu *}}
|
||||
<div class="dropdown pull-left"><!-- Dropdown -->
|
||||
<div class="hidden-sm hidden-xs contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
|
||||
<a href="{{$item.profile_url}}" class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}">
|
||||
<a href="{{$item.profile_url}}" class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$item.thumb}}" class="contact-photo media-object {{$item.sparkle}}" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden-lg hidden-md contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
|
||||
<a href="{{$item.profile_url}}" class="userinfo u-url" id="wall-item-photo-menu-xs-{{$item.id}}">
|
||||
<a href="{{$item.profile_url}}" class="userinfo click-card u-url" id="wall-item-photo-menu-xs-{{$item.id}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}}" id="wall-item-photo-xs-{{$item.id}}" alt="{{$item.name}}" />
|
||||
</div>
|
||||
|
@ -91,10 +91,22 @@
|
|||
|
||||
|
||||
{{* contact info header*}}
|
||||
<div role="heading " class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
|
||||
<h4 class="media-heading"><a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span></a>
|
||||
{{if $item.owner_url}}{{$item.via}} <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span></a>{{/if}}
|
||||
{{if $item.lock}}<span class="navicon lock fakelink" onClick="lockview(event, {{$item.id}});" title="{{$item.lock}}"> <small><i class="fa fa-lock" aria-hidden="true"></i></small></span>{{/if}}
|
||||
<div role="heading" class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
|
||||
<h4 class="media-heading">
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card">
|
||||
<span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span>
|
||||
</a>
|
||||
{{if $item.owner_url}}
|
||||
{{$item.via}}
|
||||
<a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo hover-card">
|
||||
<span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $item.lock}}
|
||||
<span class="navicon lock fakelink" onClick="lockview(event, {{$item.id}});" title="{{$item.lock}}">
|
||||
<small><i class="fa fa-lock" aria-hidden="true"></i></small>
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<div class="additional-info text-muted">
|
||||
<div id="wall-item-ago-{{$item.id}}" class="wall-item-ago">
|
||||
|
@ -114,7 +126,7 @@
|
|||
{{* contact info header for smartphones *}}
|
||||
<div role="heading " class="contact-info-xs hidden-lg hidden-md">
|
||||
<h5 class="media-heading">
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span>{{$item.name}}</span></a>
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span>{{$item.name}}</span></a>
|
||||
<p class="text-muted"><small>
|
||||
<span class="wall-item-ago">{{$item.ago}}</span> {{if $item.location}} — ({{$item.location nofilter}}){{/if}}</small>
|
||||
</p>
|
||||
|
|
|
@ -159,14 +159,14 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
<div class="dropdown pull-left"><!-- Dropdown -->
|
||||
{{if $item.thread_level==1}}
|
||||
<div class="hidden-sm hidden-xs contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}} p-author h-card">
|
||||
<a class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<a class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$item.thumb}}" class="contact-photo media-object {{$item.sparkle}} p-name u-photo" id="wall-item-photo-{{$item.id}}" alt="{{$item.name}}" />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden-lg hidden-md contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}}">
|
||||
<a class="userinfo u-url" id="wall-item-photo-menu-xs-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<a class="userinfo click-card u-url" id="wall-item-photo-menu-xs-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}}" id="wall-item-photo-xs-{{$item.id}}" alt="{{$item.name}}" />
|
||||
</div>
|
||||
|
@ -187,7 +187,7 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
{{* The avatar picture for comments *}}
|
||||
{{if $item.thread_level!=1}}
|
||||
<div class="contact-photo-wrapper mframe{{if $item.owner_url}} wwfrom{{/if}} p-author h-card">
|
||||
<a class="userinfo u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<a class="userinfo click-card u-url" id="wall-item-photo-menu-{{$item.id}}" href="{{$item.profile_url}}">
|
||||
<div class="contact-photo-image-wrapper">
|
||||
<img src="{{$item.thumb}}" class="contact-photo-xs media-object {{$item.sparkle}} p-name u-photo" id="wall-item-photo-comment-{{$item.id}}" alt="{{$item.name}}" />
|
||||
</div>
|
||||
|
@ -201,9 +201,21 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
{{* contact info header*}}
|
||||
{{if $item.thread_level==1}}
|
||||
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info hidden-sm hidden-xs media-body"><!-- <= For computer -->
|
||||
<h4 class="media-heading"><a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span></a>
|
||||
{{if $item.owner_url}}{{$item.via}} <a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo"><span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span></a>{{/if}}
|
||||
{{if $item.lock}}<span class="navicon lock fakelink" onClick="lockview(event,{{$item.id}});" title="{{$item.lock}}" data-toggle="tooltip"> <small><i class="fa fa-lock" aria-hidden="true"></i></small></span>{{/if}}
|
||||
<h4 class="media-heading">
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card">
|
||||
<span class="wall-item-name {{$item.sparkle}}">{{$item.name}}</span>
|
||||
</a>
|
||||
{{if $item.owner_url}}
|
||||
{{$item.via}}
|
||||
<a href="{{$item.owner_url}}" target="redir" title="{{$item.olinktitle}}" class="wall-item-name-link userinfo hover-card">
|
||||
<span class="wall-item-name {{$item.osparkle}}" id="wall-item-ownername-{{$item.id}}">{{$item.owner_name}}</span>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $item.lock}}
|
||||
<span class="navicon lock fakelink" onClick="lockview(event,{{$item.id}});" title="{{$item.lock}}" data-toggle="tooltip">
|
||||
<small><i class="fa fa-lock" aria-hidden="true"></i></small>
|
||||
</span>
|
||||
{{/if}}
|
||||
</h4>
|
||||
|
||||
<div class="additional-info text-muted">
|
||||
|
@ -232,7 +244,7 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
{{* contact info header for smartphones *}}
|
||||
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info-xs hidden-lg hidden-md"><!-- <= For smartphone (responsive) -->
|
||||
<h5 class="media-heading">
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span>{{$item.name}}</span></a>
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span>{{$item.name}}</span></a>
|
||||
<p class="text-muted">
|
||||
<small>
|
||||
<a class="time" href="{{$item.plink.orig}}"><span class="wall-item-ago">{{$item.ago}}</span></a>
|
||||
|
@ -251,7 +263,7 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
<div class="media-body">{{*this is the media body for comments - this div must be closed at the end of the file *}}
|
||||
<div role="heading " aria-level="{{$item.thread_level}}" class="contact-info-comment">
|
||||
<h5 class="media-heading">
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo"><span class="fakelink">{{$item.name}}</span></a>
|
||||
<a href="{{$item.profile_url}}" title="{{$item.linktitle}}" class="wall-item-name-link userinfo hover-card"><span class="fakelink">{{$item.name}}</span></a>
|
||||
<span class="text-muted">
|
||||
<small>
|
||||
<a class="time" href="{{$item.plink.orig}}" title="{{$item.localtime}}" data-toggle="tooltip">{{$item.ago}}</a>
|
||||
|
|
Loading…
Reference in New Issue