Changed the option to enable the smart threading with the option to disable this
MYSQL_USER: friendica
MYSQL_PASSWORD: friendica
MYSQL_DATABASE: friendica
- volumes:
+ volumes:
- name: cache
path: /var/lib/mysql
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: mysql8.0-php7.2
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: mysql8.0-php7.3
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: mariadb10.1-php7.1
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: mariadb10.1-php7.2
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: mariadb10.1-php7.3
- name: cache
temp: {}
+trigger:
+ branch:
+# - master
+ - develop
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: redis-php7.1
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: redis-php7.2
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: redis-php7.3
- name: redis
image: redis
+trigger:
+ branch:
+# - master
+ - develop
+# - "*-rc"
+# event:
+# - pull_request
+# - push
+
---
kind: pipeline
name: memcache-php7.1
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: memcache-php7.2
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: memcache-php7.3
- name: memcached
image: memcached
+trigger:
+ branch:
+# - master
+ - develop
+# - "*-rc"
+# event:
+# - pull_request
+# - push
+
+
---
kind: pipeline
name: memcached-php7.1
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: memcached-php7.2
trigger:
branch:
- - master
+# - master
- develop
- - "*-rc"
- event:
- - pull_request
- - push
+# - "*-rc"
+# event:
+# - pull_request
+# - push
---
kind: pipeline
name: memcached-php7.3
services:
- name: memcached
image: memcached
+
+trigger:
+ branch:
+# - master
+ - develop
+# - "*-rc"
+# event:
+# - pull_request
+# - push
- phpenv config-add .travis/redis.ini
- phpenv config-add .travis/memcached.ini
-script: vendor/bin/phpunit --configuration tests/phpunit.xml
+script: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
+
+after_success: bash <(curl -s https://codecov.io/bash)
MINIFY_CMD=uglifyjs
JSFILES=(
- "view/js/acl.js"
"view/js/ajaxupload.js"
"view/js/country.js"
"view/js/main.js"
"vendor/asset/base64/base64.min.js"
- "view/theme/frost/js/acl.js"
"view/theme/frost/js/jquery.divgrow-1.3.1.f1.js"
"view/theme/frost/js/main.js"
"view/theme/frost/js/theme.js"
- "view/theme/frost-mobile/js/acl.js"
"view/theme/frost-mobile/js/jquery.divgrow-1.3.1.f1.js"
"view/theme/frost-mobile/js/main.js"
"view/theme/frost-mobile/js/theme.js"
$statustext = mb_substr($statustext, 0, 1000) . "... \n" . ($item['plink'] ?? '');
}
- $statushtml = BBCode::convert(api_clean_attachments($body), false);
+ $statushtml = BBCode::convert(BBCode::removeAttachment($body), false);
// Workaround for clients with limited HTML parser functionality
$search = ["<br>", "<blockquote>", "</blockquote>",
}
// Simplify "attachment" element
- $text = api_clean_attachments($text);
+ $text = BBCode::removeAttachment($text);
return $text;
}
-/**
- * @brief Removes most sharing information for API text export
- *
- * @param string $body The original body
- *
- * @return string Cleaned body
- * @throws InternalServerErrorException
- */
-function api_clean_attachments($body)
-{
- $data = BBCode::getAttachmentData($body);
-
- if (empty($data)) {
- return $body;
- }
- $body = "";
-
- if (isset($data["text"])) {
- $body = $data["text"];
- }
- if (($body == "") && isset($data["title"])) {
- $body = $data["title"];
- }
- if (isset($data["url"])) {
- $body .= "\n".$data["url"];
- }
- $body .= $data["after"];
-
- return $body;
-}
-
/**
*
* @param array $contacts
'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
use Friendica\Core\System;
+use Friendica\Core\Theme;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Model\Event;
$events[$key]['item'] = $event_item;
}
+ // ACL blocks are loaded in modals in frio
+ $a->page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
+ $a->page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
+ $a->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
+ $a->page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
+
$o = Renderer::replaceMacros($tpl, [
'$tabs' => $tabs,
'$title' => L10n::t('Events'),
$fhour = !empty($orig_event) ? DateTimeFormat::convert($fdt, $tz, 'UTC', 'H') : '00';
$fminute = !empty($orig_event) ? DateTimeFormat::convert($fdt, $tz, 'UTC', 'i') : '00';
- $perms = ACL::getDefaultUserPermissions($orig_event);
-
if (!$cid && in_array($mode, ['new', 'copy'])) {
- $acl = ACL::getFullSelectorHTML($a->user, false, $orig_event);
+ $acl = ACL::getFullSelectorHTML($a->page, $a->user, false, ACL::getDefaultUserPermissions($orig_event));
} else {
$acl = '';
}
'$cid' => $cid,
'$uri' => $uri,
- '$allow_cid' => json_encode($perms['allow_cid']),
- '$allow_gid' => json_encode($perms['allow_gid']),
- '$deny_cid' => json_encode($perms['deny_cid']),
- '$deny_gid' => json_encode($perms['deny_gid']),
-
'$title' => L10n::t('Event details'),
'$desc' => L10n::t('Starting date and Title are required.'),
'$s_text' => L10n::t('Event Starts:') . ' <span class="required" title="' . L10n::t('Required') . '">*</span>',
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($a->user),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
(strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) ||
strlen($a->user['deny_cid']) || strlen($a->user['deny_gid']))) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($a->user),
- 'acl' => ACL::getFullSelectorHTML($a->user, true, $default_permissions),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true, $default_permissions),
'bang' => (($gid || $cid || $nets) ? '!' : ''),
'visitor' => 'block',
'profile_uid' => local_user(),
$tpl = Renderer::getMarkupTemplate('photos_upload.tpl');
- $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML($a->user));
+ $aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML($a->page, $a->user));
$o .= Renderer::replaceMacros($tpl,[
'$pagename' => L10n::t('Upload Photos'),
$album_e = $ph[0]['album'];
$caption_e = $ph[0]['desc'];
- $aclselect_e = ACL::getFullSelectorHTML($a->user, false, $ph[0]);
+ $aclselect_e = ACL::getFullSelectorHTML($a->page, $a->user, false, ACL::getDefaultUserPermissions($ph[0]));
$edit = Renderer::replaceMacros($edit_tpl, [
'$id' => $ph[0]['id'],
'$permissions' => L10n::t('Default Post Permissions'),
'$permdesc' => L10n::t("\x28click to open/close\x29"),
'$visibility' => $profile['net-publish'],
- '$aclselect' => ACL::getFullSelectorHTML($a->user),
+ '$aclselect' => ACL::getFullSelectorHTML($a->page, $a->user),
'$suggestme' => $suggestme,
'$blockwall'=> $blockwall, // array('blockwall', L10n::t('Allow friends to post to your profile page:'), !$blockwall, ''),
'$blocktags'=> $blocktags, // array('blocktags', L10n::t('Allow friends to tag your posts:'), !$blocktags, ''),
use Friendica\Core\Theme;
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
use Friendica\Network\HTTPException;
+use Friendica\Util\Strings;
/**
* Contains the page specific environment variables for the current Page
/**
* @var array Contains all stylesheets, which should get loaded during page
*/
- private $stylesheets;
+ private $stylesheets = [];
/**
* @var array Contains all scripts, which are added to the footer at last
*/
- private $footerScripts;
+ private $footerScripts = [];
/**
* @var array The page content, which are showed directly
*/
- private $page;
+ private $page = [
+ 'aside' => '',
+ 'bottom' => '',
+ 'content' => '',
+ 'footer' => '',
+ 'htmlhead' => '',
+ 'nav' => '',
+ 'page_title' => '',
+ 'right_aside' => '',
+ 'template' => '',
+ 'title' => '',
+ ];
/**
* @var string The basepath of the page
*/
public function __construct(string $basepath)
{
$this->basePath = $basepath;
-
- $this->page = [
- 'aside' => '',
- 'bottom' => '',
- 'content' => '',
- 'footer' => '',
- 'htmlhead' => '',
- 'nav' => '',
- 'page_title' => '',
- 'right_aside' => '',
- 'template' => '',
- 'title' => ''
- ];
}
/**
* being first
*/
$this->page['htmlhead'] = Renderer::replaceMacros($tpl, [
- '$local_user' => local_user(),
- '$generator' => 'Friendica' . ' ' . FRIENDICA_VERSION,
- '$delitem' => $l10n->t('Delete this item?'),
- '$update_interval' => $interval,
- '$shortcut_icon' => $shortcut_icon,
- '$touch_icon' => $touch_icon,
- '$block_public' => intval($config->get('system', 'block_public')),
- '$stylesheets' => $this->stylesheets,
- ]) . $this->page['htmlhead'];
+ '$local_user' => local_user(),
+ '$generator' => 'Friendica' . ' ' . FRIENDICA_VERSION,
+ '$delitem' => $l10n->t('Delete this item?'),
+ '$update_interval' => $interval,
+ '$shortcut_icon' => $shortcut_icon,
+ '$touch_icon' => $touch_icon,
+ '$block_public' => intval($config->get('system', 'block_public')),
+ '$stylesheets' => array_unique($this->stylesheets),
+ ]) . $this->page['htmlhead'];
}
/**
$tpl = Renderer::getMarkupTemplate('footer.tpl');
$this->page['footer'] = Renderer::replaceMacros($tpl, [
- '$footerScripts' => $this->footerScripts,
- ]) . $this->page['footer'];
+ '$footerScripts' => array_unique($this->footerScripts),
+ ]) . $this->page['footer'];
}
/**
* to load another page template than the default one.
* The page templates are located in /view/php/ or in the theme directory.
*/
- if (isset($_GET["mode"])) {
- $template = Theme::getPathForFile($_GET["mode"] . '.php');
+ if (isset($_GET['mode'])) {
+ $template = Theme::getPathForFile('php/' . Strings::sanitizeFilePathItem($_GET['mode']) . '.php');
}
// If there is no page template use the default page template
if (empty($template)) {
- $template = Theme::getPathForFile("default.php");
+ $template = Theme::getPathForFile('php/default.php');
}
// Theme templates expect $a as an App instance
// Used as is in view/php/default.php
$lang = $l10n->getCurrentLang();
- /// @TODO Looks unsafe (remote-inclusion), is maybe not but Core\Theme::getPathForFile() uses file_exists() but does not escape anything
require_once $template;
}
}
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ': </p>' . "\n" . $content;
break;
case 7: // statusnet/GNU Social
- case 9: // ActivityPub
$text = ($is_quote_share? '<br />' : '') . '<p>' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' @' . $author_contact['addr'] . ': ' . $content . '</p>' . "\n";
break;
+ case 9: // ActivityPub
+ $author = '@<span class="vcard"><a href="' . $author_contact['url'] . '" class="url u-url mention" title="' . $author_contact['addr'] . '"><span class="fn nickname mention">' . $author_contact['addr'] . ':</span></a></span>';
+ $text = '<div><a href="' . $attributes['link'] . '">' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . '</a> ' . $author . '<blockquote>' . $content . '</blockquote></div>' . "\n";
+ break;
default:
// Transforms quoted tweets in rich attachments to avoid nested tweets
if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) {
namespace Friendica\Core;
+use Friendica\App\Page;
use Friendica\BaseObject;
-use Friendica\Content\Feature;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
-use Friendica\Model\GContact;
-use Friendica\Core\Session;
-use Friendica\Util\Network;
+use Friendica\Model\Group;
/**
* Handle ACL management and display
];
}
+ /**
+ * Returns the ACL list of contacts for a given user id
+ *
+ * @param int $user_id
+ * @return array
+ * @throws \Exception
+ */
+ public static function getContactListByUserId(int $user_id)
+ {
+ $acl_contacts = Contact::selectToArray(
+ ['id', 'name', 'addr', 'micro'],
+ ['uid' => $user_id, 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]
+ );
+ array_walk($acl_contacts, function (&$value) {
+ $value['type'] = 'contact';
+ });
+
+ return $acl_contacts;
+ }
+
+ /**
+ * Returns the ACL list of groups (including meta-groups) for a given user id
+ *
+ * @param int $user_id
+ * @return array
+ */
+ public static function getGroupListByUserId(int $user_id)
+ {
+ $acl_groups = [
+ [
+ 'id' => Group::FOLLOWERS,
+ 'name' => L10n::t('Followers'),
+ 'addr' => '',
+ 'micro' => 'images/twopeople.png',
+ 'type' => 'group',
+ ],
+ [
+ 'id' => Group::MUTUALS,
+ 'name' => L10n::t('Mutuals'),
+ 'addr' => '',
+ 'micro' => 'images/twopeople.png',
+ 'type' => 'group',
+ ]
+ ];
+ foreach (Group::getByUserId($user_id) as $group) {
+ $acl_groups[] = [
+ 'id' => $group['id'],
+ 'name' => $group['name'],
+ 'addr' => '',
+ 'micro' => 'images/twopeople.png',
+ 'type' => 'group',
+ ];
+ }
+
+ return $acl_groups;
+ }
+
/**
* Return the full jot ACL selector HTML
*
+ * @param Page $page
* @param array $user User array
- * @param bool $show_jotnets
- * @param array $default_permissions Static defaults permission array: ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']
+ * @param bool $for_federation
+ * @param array $default_permissions Static defaults permission array:
+ * [
+ * 'allow_cid' => [],
+ * 'allow_gid' => [],
+ * 'deny_cid' => [],
+ * 'deny_gid' => [],
+ * 'hidewall' => true/false
+ * ]
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public static function getFullSelectorHTML(array $user = null, $show_jotnets = false, array $default_permissions = [])
+ public static function getFullSelectorHTML(Page $page, array $user = null, bool $for_federation = false, array $default_permissions = [])
{
+ $page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
+ $page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
+ $page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
+
// Defaults user permissions
if (empty($default_permissions)) {
$default_permissions = self::getDefaultUserPermissions($user);
}
+ $default_permissions = [
+ 'allow_cid' => $default_permissions['allow_cid'] ?? [],
+ 'allow_gid' => $default_permissions['allow_gid'] ?? [],
+ 'deny_cid' => $default_permissions['deny_cid'] ?? [],
+ 'deny_gid' => $default_permissions['deny_gid'] ?? [],
+ 'hidewall' => $default_permissions['hidewall'] ?? false,
+ ];
+
+ if (count($default_permissions['allow_cid'])
+ + count($default_permissions['allow_gid'])
+ + count($default_permissions['deny_cid'])
+ + count($default_permissions['deny_gid'])) {
+ $visibility = 'custom';
+ } else {
+ $visibility = 'public';
+ // Default permission display for custom panel
+ $default_permissions['allow_gid'] = [Group::FOLLOWERS];
+ }
+
$jotnets_fields = [];
- if ($show_jotnets) {
+ if ($for_federation) {
$mail_enabled = false;
$pubmail_enabled = false;
if (function_exists('imap_open') && !Config::get('system', 'imap_disabled')) {
- $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
+ $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', $user['uid']]);
if (DBA::isResult($mailacct)) {
$mail_enabled = true;
$pubmail_enabled = !empty($mailacct['pubmail']);
}
}
- if (empty($default_permissions['hidewall'])) {
+ if ($default_permissions['hidewall']) {
if ($mail_enabled) {
$jotnets_fields[] = [
'type' => 'checkbox',
}
}
+ $acl_contacts = self::getContactListByUserId($user['uid']);
+
+ $acl_groups = self::getGroupListByUserId($user['uid']);
+
+ $acl_list = array_merge($acl_groups, $acl_contacts);
+
$tpl = Renderer::getMarkupTemplate('acl_selector.tpl');
$o = Renderer::replaceMacros($tpl, [
- '$showall' => L10n::t('Visible to everybody'),
- '$show' => L10n::t('show'),
- '$hide' => L10n::t('don\'t show'),
- '$allowcid' => json_encode(($default_permissions['allow_cid'] ?? '') ?: []), // We need arrays for
- '$allowgid' => json_encode(($default_permissions['allow_gid'] ?? '') ?: []), // Javascript since we
- '$denycid' => json_encode(($default_permissions['deny_cid'] ?? '') ?: []), // call .remove() and
- '$denygid' => json_encode(($default_permissions['deny_gid'] ?? '') ?: []), // .push() on these values
- '$networks' => $show_jotnets,
- '$emailcc' => L10n::t('CC: email addresses'),
- '$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
- '$jotnets_enabled' => empty($default_permissions['hidewall']),
+ '$public_title' => L10n::t('Public'),
+ '$public_desc' => L10n::t('This content will be shown to all your followers and can be seen in the community pages and by anyone with its link.'),
+ '$custom_title' => L10n::t('Limited/Private'),
+ '$custom_desc' => L10n::t('This content will be shown only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.'),
+ '$allow_label' => L10n::t('Show to:'),
+ '$deny_label' => L10n::t('Except to:'),
+ '$emailcc' => L10n::t('CC: email addresses'),
+ '$emtitle' => L10n::t('Example: bob@example.com, mary@example.com'),
'$jotnets_summary' => L10n::t('Connectors'),
- '$jotnets_fields' => $jotnets_fields,
'$jotnets_disabled_label' => L10n::t('Connectors disabled, since "%s" is enabled.', L10n::t('Hide your profile details from unknown viewers?')),
- '$aclModalTitle' => L10n::t('Permissions'),
- '$aclModalDismiss' => L10n::t('Close'),
- '$features' => [
- 'aclautomention' => !empty($user['uid']) && Feature::isEnabled($user['uid'], 'aclautomention') ? 'true' : 'false'
- ],
+ '$visibility' => $visibility,
+ '$acl_contacts' => $acl_contacts,
+ '$acl_groups' => $acl_groups,
+ '$acl_list' => $acl_list,
+ '$contact_allow' => implode(',', $default_permissions['allow_cid']),
+ '$group_allow' => implode(',', $default_permissions['allow_gid']),
+ '$contact_deny' => implode(',', $default_permissions['deny_cid']),
+ '$group_deny' => implode(',', $default_permissions['deny_gid']),
+ '$for_federation' => $for_federation,
+ '$jotnets_fields' => $jotnets_fields,
+ '$user_hidewall' => $default_permissions['hidewall'],
]);
return $o;
* and adds an application/json HTTP header to the output.
* After finishing the process is getting killed.
*
- * @param array $x The input content.
+ * @param mixed $x The input content.
* @param string $content_type Type of the input (Default: 'application/json').
*/
public static function jsonExit($x, $content_type = 'application/json') {
}
}
- return $allowed_themes;
+ return array_unique($allowed_themes);
}
public static function setAllowedList(array $allowed_themes)
{
- Config::set('system', 'allowed_themes', implode(',', $allowed_themes));
+ Config::set('system', 'allowed_themes', implode(',', array_unique($allowed_themes)));
}
/**
/**
* @brief Get the full path to relevant theme files by filename
*
- * This function search in the theme directory (and if not present in global theme directory)
- * if there is a directory with the file extension and for a file with the given
- * filename.
+ * This function searches in order in the current theme directory, in the current theme parent directory, and lastly
+ * in the base view/ folder.
*
* @param string $file Filename
- * @param string $root Full root path
* @return string Path to the file or empty string if the file isn't found
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \Exception
*/
- public static function getPathForFile($file, $root = '')
+ public static function getPathForFile($file)
{
- $file = basename($file);
+ $a = BaseObject::getApp();
+
+ $theme = $a->getCurrentTheme();
+
+ $parent = Strings::sanitizeFilePathItem($a->theme_info['extends'] ?? $theme);
- // Make sure $root ends with a slash / if it's not blank
- if ($root !== '' && $root[strlen($root) - 1] !== '/') {
- $root = $root . '/';
- }
- $theme_info = \get_app()->theme_info;
- if (is_array($theme_info) && array_key_exists('extends', $theme_info)) {
- $parent = $theme_info['extends'];
- } else {
- $parent = 'NOPATH';
- }
- $theme = \get_app()->getCurrentTheme();
- $parent = Strings::sanitizeFilePathItem($parent);
- $ext = substr($file, strrpos($file, '.') + 1);
$paths = [
- "{$root}view/theme/$theme/$ext/$file",
- "{$root}view/theme/$parent/$ext/$file",
- "{$root}view/$ext/$file",
+ "view/theme/$theme/$file",
+ "view/theme/$parent/$file",
+ "view/$file",
];
- foreach ($paths as $p) {
- // strpos() is faster than strstr when checking if one string is in another (http://php.net/manual/en/function.strstr.php)
- if (strpos($p, 'NOPATH') !== false) {
- continue;
- } elseif (file_exists($p)) {
- return $p;
+
+ foreach ($paths as $path) {
+ if (file_exists($path)) {
+ return $path;
}
}
+
return '';
}
}
$return = [];
+ $pubmail = false;
+ $networks = Protocol::SUPPORT_PRIVATE;
+
+ $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', $uid]);
+ if (DBA::isResult($mailacct)) {
+ $pubmail = $mailacct['pubmail'];
+ }
+
+ if (!$pubmail) {
+ $networks = array_diff($networks, [Protocol::MAIL]);
+ }
$key = array_search(self::FOLLOWERS, $group_ids);
if ($key !== false) {
$followers = Contact::selectToArray(['id'], [
'uid' => $uid,
'rel' => [Contact::FOLLOWER, Contact::FRIEND],
- 'network' => Protocol::SUPPORT_PRIVATE,
+ 'network' => $networks,
+ 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON],
+ 'archive' => false,
+ 'pending' => false,
+ 'blocked' => false,
]);
foreach ($followers as $follower) {
$mutuals = Contact::selectToArray(['id'], [
'uid' => $uid,
'rel' => [Contact::FRIEND],
- 'network' => Protocol::SUPPORT_PRIVATE,
+ 'network' => $networks,
+ 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON],
+ 'archive' => false,
+ 'pending' => false,
+ 'blocked' => false,
]);
foreach ($mutuals as $mutual) {
'nickname' => $app->user['nickname'],
'lockstate' => ((is_array($app->user) && ((strlen($app->user['allow_cid'])) || (strlen($app->user['allow_gid'])) || (strlen($app->user['deny_cid'])) || (strlen($app->user['deny_gid'])))) ? 'lock' : 'unlock'),
'default_perms' => ACL::getDefaultUserPermissions($app->user),
- 'acl' => ACL::getFullSelectorHTML($app->user, true),
+ 'acl' => ACL::getFullSelectorHTML($app->page, $app->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'],
'lockstate' => (is_array($a->user) && (strlen($a->user['allow_cid']) || strlen($a->user['allow_gid']) || strlen($a->user['deny_cid']) || strlen($a->user['deny_gid'])) ? 'lock' : 'unlock'),
- 'acl' => ACL::getFullSelectorHTML($a->user, true),
+ 'acl' => ACL::getFullSelectorHTML($a->page, $a->user, true),
'bang' => '',
'visitor' => 'block',
'profile_uid' => local_user(),
use Friendica\BaseModule;
use Friendica\Content\Feature;
+use Friendica\Core\ACL;
use Friendica\Core\Config;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
+use Friendica\Core\System;
+use Friendica\Core\Theme;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Model\FileTag;
}
/// @TODO Retrieve parameter from router
- $posttype = $a->argv[1] ?? Item::PT_ARTICLE;
+ $posttype = $parameters['type'] ?? Item::PT_ARTICLE;
if (!in_array($posttype, [Item::PT_ARTICLE, Item::PT_PERSONAL_NOTE])) {
switch ($posttype) {
case 'note':
/** @var ACLFormatter $aclFormatter */
$aclFormatter = self::getClass(ACLFormatter::class);
+ $contact_allow_list = $aclFormatter->expand($user['allow_cid']);
+ $group_allow_list = $aclFormatter->expand($user['allow_gid']);
+ $contact_deny_list = $aclFormatter->expand($user['deny_cid']);
+ $group_deny_list = $aclFormatter->expand($user['deny_gid']);
+
switch ($posttype) {
case Item::PT_PERSONAL_NOTE:
$compose_title = L10n::t('Compose new personal note');
$type = 'note';
$doesFederate = false;
- $contact_allow = $a->contact['id'];
- $group_allow = '';
+ $contact_allow_list = [$a->contact['id']];
+ $group_allow_list = [];
+ $contact_deny_list = [];
+ $group_deny_list = [];
break;
default:
$compose_title = L10n::t('Compose new post');
$type = 'post';
$doesFederate = true;
- $contact_allow = implode(',', $aclFormatter->expand($user['allow_cid']));
- $group_allow = implode(',', $aclFormatter->expand($user['allow_gid'])) ?: Group::FOLLOWERS;
+
+ if ($_REQUEST['contact_allow']
+ . $_REQUEST['group_allow']
+ . $_REQUEST['contact_deny']
+ . $_REQUEST['group_deny'])
+ {
+ $contact_allow_list = $_REQUEST['contact_allow'] ? explode(',', $_REQUEST['contact_allow']) : [];
+ $group_allow_list = $_REQUEST['group_allow'] ? explode(',', $_REQUEST['group_allow']) : [];
+ $contact_deny_list = $_REQUEST['contact_deny'] ? explode(',', $_REQUEST['contact_deny']) : [];
+ $group_deny_list = $_REQUEST['group_deny'] ? explode(',', $_REQUEST['group_deny']) : [];
+ }
+
break;
}
$body = $_REQUEST['body'] ?? '';
$location = $_REQUEST['location'] ?? $user['default-location'];
$wall = $_REQUEST['wall'] ?? $type == 'post';
- $contact_allow = $_REQUEST['contact_allow'] ?? $contact_allow;
- $group_allow = $_REQUEST['group_allow'] ?? $group_allow;
- $contact_deny = $_REQUEST['contact_deny'] ?? implode(',', $aclFormatter->expand($user['deny_cid']));
- $group_deny = $_REQUEST['group_deny'] ?? implode(',', $aclFormatter->expand($user['deny_gid']));
- $visibility = ($contact_allow . $user['allow_gid'] . $user['deny_cid'] . $user['deny_gid']) ? 'custom' : 'public';
-
- $acl_contacts = Contact::selectToArray(['id', 'name', 'addr', 'micro'], ['uid' => local_user(), 'pending' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]);
- array_walk($acl_contacts, function (&$value) {
- $value['type'] = 'contact';
- });
-
- $acl_groups = [
- [
- 'id' => Group::FOLLOWERS,
- 'name' => L10n::t('Followers'),
- 'addr' => '',
- 'micro' => 'images/twopeople.png',
- 'type' => 'group',
- ],
- [
- 'id' => Group::MUTUALS,
- 'name' => L10n::t('Mutuals'),
- 'addr' => '',
- 'micro' => 'images/twopeople.png',
- 'type' => 'group',
- ]
- ];
- foreach (Group::getByUserId(local_user()) as $group) {
- $acl_groups[] = [
- 'id' => $group['id'],
- 'name' => $group['name'],
- 'addr' => '',
- 'micro' => 'images/twopeople.png',
- 'type' => 'group',
- ];
- }
-
- $acl = array_merge($acl_groups, $acl_contacts);
-
- $jotnets_fields = [];
- $mail_enabled = false;
- $pubmail_enabled = false;
- if (function_exists('imap_open') && !Config::get('system', 'imap_disabled')) {
- $mailacct = DBA::selectFirst('mailacct', ['pubmail'], ['`uid` = ? AND `server` != ""', local_user()]);
- if (DBA::isResult($mailacct)) {
- $mail_enabled = true;
- $pubmail_enabled = !empty($mailacct['pubmail']);
- }
- }
-
- if (empty($user['hidewall'])) {
- if ($mail_enabled) {
- $jotnets_fields[] = [
- 'type' => 'checkbox',
- 'field' => [
- 'pubmail_enable',
- L10n::t('Post to Email'),
- $pubmail_enabled
- ]
- ];
- }
-
- Hook::callAll('jot_networks', $jotnets_fields);
- }
$jotplugins = '';
Hook::callAll('jot_tool', $jotplugins);
// Output
-
- $a->registerFooterScript('view/js/ajaxupload.js');
- $a->registerFooterScript('view/js/linkPreview.js');
- $a->registerFooterScript('view/asset/typeahead.js/dist/typeahead.bundle.js');
- $a->registerFooterScript('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput.js');
- $a->registerStylesheet('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput.css');
- $a->registerStylesheet('view/theme/frio/frameworks/friendica-tagsinput/friendica-tagsinput-typeahead.css');
-
- $tpl = Renderer::getMarkupTemplate('item/compose-footer.tpl');
- $a->page['footer'] .= Renderer::replaceMacros($tpl, [
- '$acl_contacts' => $acl_contacts,
- '$acl_groups' => $acl_groups,
- '$acl' => $acl,
- ]);
+ $a->page->registerFooterScript(Theme::getPathForFile('js/ajaxupload.js'));
+ $a->page->registerFooterScript(Theme::getPathForFile('js/linkPreview.js'));
+ $a->page->registerFooterScript(Theme::getPathForFile('js/compose.js'));
$tpl = Renderer::getMarkupTemplate('item/compose.tpl');
return Renderer::replaceMacros($tpl, [
'$compose_title'=> $compose_title,
+ '$visibility_title'=> L10n::t('Visibility'),
'$id' => 0,
'$posttype' => $posttype,
'$type' => $type,
'$wait' => L10n::t('Please wait'),
'$placeholdertitle' => L10n::t('Set title'),
'$placeholdercategory' => (Feature::isEnabled(local_user(),'categories') ? L10n::t('Categories (comma-separated list)') : ''),
- '$public_title' => L10n::t('Public'),
- '$public_desc' => L10n::t('This post will be sent to all your followers and can be seen in the community pages and by anyone with its link.'),
- '$custom_title' => L10n::t('Limited/Private'),
- '$custom_desc' => L10n::t('This post will be sent only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.'),
- '$emailcc' => L10n::t('CC: email addresses'),
+
'$title' => $title,
'$category' => $category,
'$body' => $body,
'$location' => $location,
- '$visibility' => $visibility,
- '$contact_allow'=> $contact_allow,
- '$group_allow' => $group_allow,
- '$contact_deny' => $contact_deny,
- '$group_deny' => $group_deny,
+
+ '$contact_allow'=> implode(',', $contact_allow_list),
+ '$group_allow' => implode(',', $group_allow_list),
+ '$contact_deny' => implode(',', $contact_deny_list),
+ '$group_deny' => implode(',', $group_deny_list),
+
'$jotplugins' => $jotplugins,
- '$doesFederate' => $doesFederate,
- '$jotnets_fields'=> $jotnets_fields,
'$sourceapp' => L10n::t($a->sourcename),
- '$rand_num' => Crypto::randomDigits(12)
+ '$rand_num' => Crypto::randomDigits(12),
+ '$acl_selector' => ACL::getFullSelectorHTML($a->page, $a->user, $doesFederate, [
+ 'allow_cid' => $contact_allow_list,
+ 'allow_gid' => $group_allow_list,
+ 'deny_cid' => $contact_deny_list,
+ 'deny_gid' => $group_deny_list,
+ ]),
]);
}
}
}
// the json doesn't really matter, it will either be 0 or 1
- System::jsonExit([$ignored]);
+ System::jsonExit($ignored);
}
}
*/
class NodeInfo extends BaseModule
{
- public static function init(array $parameters = [])
+ public static function rawContent(array $parameters = [])
{
- $config = self::getApp()->getConfig();
+ $app = self::getApp();
- if (!$config->get('system', 'nodeinfo')) {
+ if ($parameters['version'] == '1.0') {
+ self::printNodeInfo1($app);
+ } elseif ($parameters['version'] == '2.0') {
+ self::printNodeInfo2($app);
+ } else {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
}
- public static function rawContent(array $parameters = [])
+ /**
+ * Return the supported services
+ *
+ * @param App $app
+ *
+ * @return array with supported services
+ */
+ private static function getUsage(App $app)
{
- $app = self::getApp();
+ $config = $app->getConfig();
- // @TODO: Replace with parameter from router
- // if the first argument is ".well-known", print the well-known text
- if (($app->argc > 1) && ($app->argv[0] == '.well-known')) {
- self::printWellKnown($app);
- // otherwise print the nodeinfo
- } else {
- self::printNodeInfo($app);
+ $usage = [];
+
+ if (!empty($config->get('system', 'nodeinfo'))) {
+ $usage['users'] = [
+ 'total' => intval($config->get('nodeinfo', 'total_users')),
+ 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')),
+ 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly'))
+ ];
+ $usage['localPosts'] = intval($config->get('nodeinfo', 'local_posts'));
+ $usage['localComments'] = intval($config->get('nodeinfo', 'local_comments'));
}
+
+ return $usage;
}
/**
- * Prints the well-known nodeinfo redirect
+ * Return the supported services
*
* @param App $app
*
- * @throws \Friendica\Network\HTTPException\NotFoundException
- */
- private static function printWellKnown(App $app)
+ * @return array with supported services
+ */
+ private static function getServices(App $app)
{
- $config = $app->getConfig();
+ $services = [
+ 'inbound' => [],
+ 'outbound' => [],
+ ];
- if (!$config->get('system', 'nodeinfo')) {
- throw new \Friendica\Network\HTTPException\NotFoundException();
+ if (Addon::isEnabled('blogger')) {
+ $services['outbound'][] = 'blogger';
+ }
+ if (Addon::isEnabled('dwpost')) {
+ $services['outbound'][] = 'dreamwidth';
+ }
+ if (Addon::isEnabled('statusnet')) {
+ $services['inbound'][] = 'gnusocial';
+ $services['outbound'][] = 'gnusocial';
+ }
+ if (Addon::isEnabled('ijpost')) {
+ $services['outbound'][] = 'insanejournal';
+ }
+ if (Addon::isEnabled('libertree')) {
+ $services['outbound'][] = 'libertree';
+ }
+ if (Addon::isEnabled('buffer')) {
+ $services['outbound'][] = 'linkedin';
+ }
+ if (Addon::isEnabled('ljpost')) {
+ $services['outbound'][] = 'livejournal';
+ }
+ if (Addon::isEnabled('buffer')) {
+ $services['outbound'][] = 'pinterest';
+ }
+ if (Addon::isEnabled('posterous')) {
+ $services['outbound'][] = 'posterous';
+ }
+ if (Addon::isEnabled('pumpio')) {
+ $services['inbound'][] = 'pumpio';
+ $services['outbound'][] = 'pumpio';
}
- $nodeinfo = [
- 'links' => [[
- 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0',
- 'href' => $app->getBaseURL() . '/nodeinfo/1.0']]
- ];
+ $services['outbound'][] = 'smtp';
- header('Content-type: application/json; charset=utf-8');
- echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
- exit;
+ if (Addon::isEnabled('tumblr')) {
+ $services['outbound'][] = 'tumblr';
+ }
+ if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) {
+ $services['outbound'][] = 'twitter';
+ }
+ if (Addon::isEnabled('wppost')) {
+ $services['outbound'][] = 'wordpress';
+ }
+
+ return $services;
}
/**
- * Print the nodeinfo
+ * Print the nodeinfo version 1
*
* @param App $app
*/
- private static function printNodeInfo(App $app)
+ private static function printNodeInfo1(App $app)
{
$config = $app->getConfig();
- $smtp = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only'));
-
$nodeinfo = [
- 'version' => 1.0,
+ 'version' => '1.0',
'software' => [
'name' => 'friendica',
'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
],
'protocols' => [
'inbound' => [
- 'friendica',
+ 'friendica', 'activitypub'
],
'outbound' => [
- 'friendica',
+ 'friendica', 'activitypub'
],
],
- 'services' => [
- 'inbound' => [],
- 'outbound' => [],
- ],
+ 'services' => [],
'usage' => [],
'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED,
'metadata' => [
$nodeinfo['protocols']['outbound'][] = 'gnusocial';
}
- if (!empty($config->get('system', 'nodeinfo'))) {
+ $nodeinfo['usage'] = self::getUsage($app);
- $nodeinfo['usage']['users'] = [
- 'total' => intval($config->get('nodeinfo', 'total_users')),
- 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')),
- 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly'))
- ];
- $nodeinfo['usage']['localPosts'] = intval($config->get('nodeinfo', 'local_posts'));
- $nodeinfo['usage']['localComments'] = intval($config->get('nodeinfo', 'local_comments'));
-
- if (Addon::isEnabled('blogger')) {
- $nodeinfo['services']['outbound'][] = 'blogger';
- }
- if (Addon::isEnabled('dwpost')) {
- $nodeinfo['services']['outbound'][] = 'dreamwidth';
- }
- if (Addon::isEnabled('statusnet')) {
- $nodeinfo['services']['inbound'][] = 'gnusocial';
- $nodeinfo['services']['outbound'][] = 'gnusocial';
- }
- if (Addon::isEnabled('ijpost')) {
- $nodeinfo['services']['outbound'][] = 'insanejournal';
- }
- if (Addon::isEnabled('libertree')) {
- $nodeinfo['services']['outbound'][] = 'libertree';
- }
- if (Addon::isEnabled('buffer')) {
- $nodeinfo['services']['outbound'][] = 'linkedin';
- }
- if (Addon::isEnabled('ljpost')) {
- $nodeinfo['services']['outbound'][] = 'livejournal';
- }
- if (Addon::isEnabled('buffer')) {
- $nodeinfo['services']['outbound'][] = 'pinterest';
- }
- if (Addon::isEnabled('posterous')) {
- $nodeinfo['services']['outbound'][] = 'posterous';
- }
- if (Addon::isEnabled('pumpio')) {
- $nodeinfo['services']['inbound'][] = 'pumpio';
- $nodeinfo['services']['outbound'][] = 'pumpio';
- }
-
- if ($smtp) {
- $nodeinfo['services']['outbound'][] = 'smtp';
- }
- if (Addon::isEnabled('tumblr')) {
- $nodeinfo['services']['outbound'][] = 'tumblr';
- }
- if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) {
- $nodeinfo['services']['outbound'][] = 'twitter';
- }
- if (Addon::isEnabled('wppost')) {
- $nodeinfo['services']['outbound'][] = 'wordpress';
- }
- $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols'];
- $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0';
- $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0';
- $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0';
-
- $nodeinfo['metadata']['services'] = $nodeinfo['services'];
-
- if (Addon::isEnabled('twitter')) {
- $nodeinfo['metadata']['services']['inbound'][] = 'twitter';
- }
-
- $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true;
+ $nodeinfo['services'] = self::getServices($app);
+
+ $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols'];
+ $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0';
+ $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0';
+ $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0';
+
+ $nodeinfo['metadata']['services'] = $nodeinfo['services'];
+
+ if (Addon::isEnabled('twitter')) {
+ $nodeinfo['metadata']['services']['inbound'][] = 'twitter';
}
+ $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true;
+
+ header('Content-type: application/json; charset=utf-8');
+ echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ exit;
+ }
+
+ /**
+ * Print the nodeinfo version 2
+ *
+ * @param App $app
+ */
+ private static function printNodeInfo2(App $app)
+ {
+ $config = $app->getConfig();
+
+ $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only'));
+
+ $nodeinfo = [
+ 'version' => '2.0',
+ 'software' => [
+ 'name' => 'friendica',
+ 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
+ ],
+ 'protocols' => ['dfrn', 'activitypub'],
+ 'services' => [],
+ 'usage' => [],
+ 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED,
+ 'metadata' => [
+ 'nodeName' => $config->get('config', 'sitename'),
+ ],
+ ];
+
+ if (!empty($config->get('system', 'diaspora_enabled'))) {
+ $nodeinfo['protocols'][] = 'diaspora';
+ }
+
+ if (empty($config->get('system', 'ostatus_disabled'))) {
+ $nodeinfo['protocols'][] = 'ostatus';
+ }
+
+ $nodeinfo['usage'] = self::getUsage($app);
+
+ $nodeinfo['services'] = self::getServices($app);
+
+ if (Addon::isEnabled('twitter')) {
+ $nodeinfo['services']['inbound'][] = 'twitter';
+ }
+
+ $nodeinfo['services']['inbound'][] = 'atom1.0';
+ $nodeinfo['services']['inbound'][] = 'rss2.0';
+ $nodeinfo['services']['outbound'][] = 'atom1.0';
+
+ if ($imap) {
+ $nodeinfo['services']['inbound'][] = 'imap';
+ }
+
+ $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true;
+
header('Content-type: application/json; charset=utf-8');
echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
exit;
}
}
- $data = ActivityPub\Transmitter::createObjectFromItemID($item['id']);
+ $activity = ActivityPub\Transmitter::createActivityFromItem($item['id'], true);
+ // Only display "Create" activity objects here, no reshares or anything else
+ if (!is_array($activity['object']) || ($activity['type'] != 'Create')) {
+ throw new \Friendica\Network\HTTPException\NotFoundException();
+ }
+
+ $data = ['@context' => ActivityPub::CONTEXT];
+ $data = array_merge($data, $activity['object']);
header('Content-Type: application/activity+json');
echo json_encode($data);
|| strlen($a->user['deny_cid'])
|| strlen($a->user['deny_gid'])
) ? 'lock' : 'unlock',
- 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->user, true) : '',
+ 'acl' => $is_owner ? ACL::getFullSelectorHTML($a->page, $a->user, true) : '',
'bang' => '',
'visitor' => $is_owner || $commvisitor ? 'block' : 'none',
'profile_uid' => $a->profile['profile_uid'],
--- /dev/null
+<?php
+
+namespace Friendica\Module\WellKnown;
+
+use Friendica\App;
+use Friendica\BaseModule;
+
+/**
+ * Standardized way of exposing metadata about a server running one of the distributed social networks.
+ * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md
+ */
+class NodeInfo extends BaseModule
+{
+ public static function rawContent(array $parameters = [])
+ {
+ $app = self::getApp();
+
+ self::printWellKnown($app);
+ }
+
+ /**
+ * Prints the well-known nodeinfo redirect
+ *
+ * @param App $app
+ *
+ * @throws \Friendica\Network\HTTPException\NotFoundException
+ */
+ private static function printWellKnown(App $app)
+ {
+ $nodeinfo = [
+ 'links' => [
+ ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0',
+ 'href' => $app->getBaseURL() . '/nodeinfo/1.0'],
+ ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0',
+ 'href' => $app->getBaseURL() . '/nodeinfo/2.0'],
+ ]
+ ];
+
+ header('Content-type: application/json; charset=utf-8');
+ echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+ exit;
+ }
+}
$tags = Term::populateTagsFromItem($item);
+ $ago = Temporal::getRelativeDate($item['created']);
+ $ago_received = Temporal::getRelativeDate($item['received']);
+ if (Config::get('system', 'show_received') && (abs(strtotime($item['created']) - strtotime($item['received'])) > Config::get('system', 'show_received_seconds')) && ($ago != $ago_received)) {
+ $ago = L10n::t('%s (Received %s)', $ago, $ago_received);
+ }
+
$tmp_item = [
'template' => $this->getTemplate(),
'type' => implode("", array_slice(explode("/", $item['verb']), -1)),
'sparkle' => $sparkle,
'title' => $title_e,
'localtime' => DateTimeFormat::local($item['created'], 'r'),
- 'ago' => $item['app'] ? L10n::t('%s from %s', Temporal::getRelativeDate($item['created']), $item['app']) : Temporal::getRelativeDate($item['created']),
+ 'ago' => $item['app'] ? L10n::t('%s from %s', $ago, $item['app']) : $ago,
'app' => $item['app'],
'created' => Temporal::getRelativeDate($item['created']),
'lock' => $lock,
$items = Item::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]);
while ($item = Item::fetch($items)) {
- $object = self::createObjectFromItemID($item['id']);
- unset($object['@context']);
- $list[] = $object;
+ $activity = self::createActivityFromItem($item['id'], true);
+ // Only list "Create" activity objects here, no reshares
+ if (is_array($activity['object']) && ($activity['type'] == 'Create')) {
+ $list[] = $activity['object'];
+ }
}
if (!empty($list)) {
$terms = Term::tagArrayFromItemId($item['id'], [Term::MENTION, Term::IMPLICIT_MENTION]);
+ // Directly mention the original author upon a quoted reshare.
+ // Else just ensure that the original author receives the reshare.
+ $announce = self::getAnnounceArray($item);
+ if (!empty($announce['comment'])) {
+ $data['to'][] = $announce['actor']['url'];
+ } elseif (!empty($announce)) {
+ $data['cc'][] = $announce['actor']['url'];
+ }
+
if (!$item['private']) {
$data = array_merge($data, self::fetchPermissionBlockFromConversation($item));
// Only check for a reshare, if it is a real reshare and no quoted reshare
if (strpos($item['body'], "[share") === 0) {
- $announce = api_share_as_retweet($item);
- $reshared = !empty($announce['plink']);
+ $announce = self::getAnnounceArray($item);
+ $reshared = !empty($announce);
}
if ($reshared) {
/// @todo Create "conversation" entry
}
- /**
- * Creates an object array for a given item id
- *
- * @param integer $item_id
- *
- * @return array with the object data
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- * @throws \ImagickException
- */
- public static function createObjectFromItemID($item_id)
- {
- $item = Item::selectFirst([], ['id' => $item_id, 'parent-network' => Protocol::NATIVE_SUPPORT]);
-
- if (!DBA::isResult($item)) {
- return false;
- }
-
- $data = ['@context' => ActivityPub::CONTEXT];
- $data = array_merge($data, self::createNote($item));
-
- return $data;
- }
-
/**
* Creates a location entry for a given item array
*
$tags[] = ['type' => 'Mention', 'href' => $term['url'], 'name' => $mention];
}
}
+
+ $announce = self::getAnnounceArray($item);
+ // Mention the original author upon commented reshares
+ if (!empty($announce['comment'])) {
+ $tags[] = ['type' => 'Mention', 'href' => $announce['actor']['url'], 'name' => '@' . $announce['actor']['addr']];
+ }
+
return $tags;
}
private static function createAnnounce($item, $data)
{
$orig_body = $item['body'];
- $announce = api_share_as_retweet($item);
- if (empty($announce['plink'])) {
+ $announce = self::getAnnounceArray($item);
+ if (empty($announce)) {
$data['type'] = 'Create';
$data['object'] = self::createNote($item);
return $data;
}
- // Fetch the original id of the object
- $activity = ActivityPub::fetchContent($announce['plink'], $item['uid']);
- if (!empty($activity)) {
- $ldactivity = JsonLD::compact($activity);
- $id = JsonLD::fetchElement($ldactivity, '@id');
- $type = str_replace('as:', '', JsonLD::fetchElement($ldactivity, '@type'));
- if (!empty($id)) {
- if (empty($announce['share-pre-body'])) {
- // Pure announce, without a quote
- $data['type'] = 'Announce';
- $data['object'] = $id;
- return $data;
- }
-
- // Quote
- $data['type'] = 'Create';
- $item['body'] = trim($announce['share-pre-body']) . "\n" . $id;
- $data['object'] = self::createNote($item);
-
- /// @todo Finally descide how to implement this in AP. This is a possible way:
- $data['object']['attachment'][] = ['type' => $type, 'id' => $id];
-
- $data['object']['source']['content'] = $orig_body;
- return $data;
- }
+ if (empty($announce['comment'])) {
+ // Pure announce, without a quote
+ $data['type'] = 'Announce';
+ $data['object'] = $announce['object']['uri'];
+ return $data;
}
- $item['body'] = $orig_body;
+ // Quote
$data['type'] = 'Create';
+ $item['body'] = $announce['comment'] . "\n" . $announce['object']['plink'];
$data['object'] = self::createNote($item);
+
+ /// @todo Finally descide how to implement this in AP. This is a possible way:
+ $data['object']['attachment'][] = self::createNote($announce['object']);
+
+ $data['object']['source']['content'] = $orig_body;
return $data;
}
+ /**
+ * Return announce related data if the item is an annunce
+ *
+ * @param array $item
+ *
+ * @return array
+ */
+ public static function getAnnounceArray($item)
+ {
+ if (!preg_match("/(.*?)\[share(.*?)\]\s?.*?\s?\[\/share\]\s?/ism", $item['body'], $matches)) {
+ return [];
+ }
+
+ $attributes = $matches[2];
+ $comment = $matches[1];
+
+ preg_match("/guid='(.*?)'/ism", $attributes, $matches);
+ if (empty($matches[1])) {
+ preg_match('/guid="(.*?)"/ism', $attributes, $matches);
+ }
+
+ if (empty($matches[1])) {
+ return [];
+ }
+
+ $reshared_item = Item::selectFirst([], ['guid' => $matches[1]]);
+ if (!DBA::isResult($reshared_item)) {
+ return [];
+ }
+
+ if (!in_array($reshared_item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) {
+ return [];
+ }
+
+ $profile = APContact::getByURL($reshared_item['author-link'], false);
+ if (empty($profile)) {
+ return [];
+ }
+
+ return ['object' => $reshared_item, 'actor' => $profile, 'comment' => trim($comment)];
+ }
+
/**
* Creates an activity id for a given contact id
*
// When the global community page is enabled, use this option to display a hint above the stream, that this is a collection of all public top-level postings that arrive on your node.
'show_global_community_hint' => false,
+ // show_received (Boolean)
+ // Show the receive data along with the post creation date
+ 'show_received' => true,
+
+ // show_received_seconds (Integer)
+ // Display the received date when the difference between received and created is higher than this.
+ 'show_received_seconds' => 500,
+
// show_unsupported_addons (Boolean)
// Show all addons including the unsupported ones.
'show_unsupported_addons' => false,
'/.well-known' => [
'/host-meta' => [Module\WellKnown\HostMeta::class, [R::GET]],
- '/nodeinfo[/1.0]' => [Module\NodeInfo::class, [R::GET]],
+ '/nodeinfo' => [Module\WellKnown\NodeInfo::class, [R::GET]],
'/webfinger' => [Module\Xrd::class, [R::GET]],
'/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]],
],
'/ignore/{id}' => [Module\Item\Ignore::class, [R::GET]],
],
- '/like/{item:\d+}' => [Module\Like::class, [R::GET]],
- '/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]],
- '/login' => [Module\Login::class, [R::GET, R::POST]],
- '/logout' => [Module\Logout::class, [R::GET, R::POST]],
- '/magic' => [Module\Magic::class, [R::GET]],
- '/maintenance' => [Module\Maintenance::class, [R::GET]],
- '/manifest' => [Module\Manifest::class, [R::GET]],
- '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]],
- '/newmember' => [Module\Welcome::class, [R::GET]],
- '/nodeinfo/1.0' => [Module\NodeInfo::class, [R::GET]],
- '/nogroup' => [Module\Group::class, [R::GET]],
+ '/like/{item:\d+}' => [Module\Like::class, [R::GET]],
+ '/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]],
+ '/login' => [Module\Login::class, [R::GET, R::POST]],
+ '/logout' => [Module\Logout::class, [R::GET, R::POST]],
+ '/magic' => [Module\Magic::class, [R::GET]],
+ '/maintenance' => [Module\Maintenance::class, [R::GET]],
+ '/manifest' => [Module\Manifest::class, [R::GET]],
+ '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]],
+ '/newmember' => [Module\Welcome::class, [R::GET]],
+ '/nodeinfo/{version}' => [Module\NodeInfo::class, [R::GET]],
+ '/nogroup' => [Module\Group::class, [R::GET]],
'/notify' => [
'[/]' => [Module\Notifications\Notify::class, [R::GET]],
$this->assertEquals('some_text [url="some_url"]"some_url"[/url]', $result);
}
- /**
- * Test the api_clean_attachments() function.
- *
- * @return void
- */
- public function testApiCleanAttachments()
- {
- $this->markTestIncomplete();
- }
-
/**
* Test the api_best_nickname() function.
*
+++ /dev/null
-// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPLv3-or-later
-function ACL(backend_url, preset, automention, is_mobile){
-
- this.url = backend_url;
- this.automention = automention;
- this.is_mobile = is_mobile;
-
-
- this.kp_timer = null;
-
- if (preset == undefined) {
- preset = [];
- }
- this.allow_cid = (preset[0] || []);
- this.allow_gid = (preset[1] || []);
- this.deny_cid = (preset[2] || []);
- this.deny_gid = (preset[3] || []);
- this.group_uids = [];
- this.forumCache = null;
-
- if (this.is_mobile) {
- this.nw = 1;
- } else {
- this.nw = 4;
- }
-
-
- this.list_content = $("#acl-list-content");
- this.item_tpl = unescape($(".acl-list-item[rel=acl-template]").html());
- this.showall = $("#acl-showall");
-
- if (preset.length==0) {
- this.showall.addClass("selected");
- }
-
- /*events*/
- this.showall.click(this.on_showall.bind(this));
- $(document).on("click", ".acl-button-show", this.on_button_show.bind(this));
- $(document).on("click", ".acl-button-hide", this.on_button_hide.bind(this));
- $("#acl-search").keypress(this.on_search.bind(this));
- $("#acl-wrapper").parents("form").submit(this.on_submit.bind(this));
-
- /* add/remove mentions */
- this.element = $("#profile-jot-text");
- this.htmlelm = this.element.get()[0];
-}
-
-ACL.prototype.remove_mention = function(id) {
- if (!this.automention) {
- return;
- }
- var nick = this.data[id].nick;
- var addr = this.data[id].addr;
-
- if (addr != "") {
- var searchText = "!" + addr + " ";
- } else {
- var searchText = "!" + nick + "+" + id + " ";
- }
-
- var start = this.element.val().indexOf(searchText);
- if (start < 0) {
- return;
- }
- var end = start + searchText.length;
- this.element.setSelection(start, end).replaceSelectedText('').collapseSelection(false);
-};
-
-ACL.prototype.add_mention = function(id) {
- if (!this.automention) {
- return;
- }
- var nick = this.data[id].nick;
- var addr = this.data[id].addr;
-
- if (addr != "") {
- var searchText = "!" + addr + " ";
- } else {
- var searchText = "!" + nick + "+" + id + " ";
- }
-
- if (this.element.val().indexOf( searchText) >= 0 ) {
- return;
- }
- this.element.val(searchText + this.element.val()).trigger('change');
-}
-
-ACL.prototype.on_submit = function(){
- var aclfields = $("#acl-fields").html("");
- $(this.allow_gid).each(function(i,v){
- aclfields.append("<input type='hidden' name='group_allow[]' value='"+v+"'>");
- });
- $(this.allow_cid).each(function(i,v){
- aclfields.append("<input type='hidden' name='contact_allow[]' value='"+v+"'>");
- });
- $(this.deny_gid).each(function(i,v){
- aclfields.append("<input type='hidden' name='group_deny[]' value='"+v+"'>");
- });
- $(this.deny_cid).each(function(i,v){
- aclfields.append("<input type='hidden' name='contact_deny[]' value='"+v+"'>");
- });
-};
-
-ACL.prototype.search = function(){
- var srcstr = $("#acl-search").val();
- this.list_content.html("");
- this.get(0,100, srcstr);
-};
-
-ACL.prototype.on_search = function(event){
- if (this.kp_timer) clearTimeout(this.kp_timer);
-
- // Triggers an immediate search while preventing form submission
- if (event.key === 'Enter') {
- this.search();
- event.preventDefault();
- } else {
- this.kp_timer = setTimeout( this.search.bind(this), 500);
- }
-};
-
-ACL.prototype.on_showall = function(event){
- event.preventDefault()
- event.stopPropagation();
-
- if (this.showall.hasClass("selected")){
- return false;
- }
- this.showall.addClass("selected");
-
- this.allow_cid = [];
- this.allow_gid = [];
- this.deny_cid = [];
- this.deny_gid = [];
-
- this.update_view();
-
- return false;
-};
-
-ACL.prototype.on_button_show = function(event){
- event.preventDefault()
- event.stopImmediatePropagation()
- event.stopPropagation();
-
- this.set_allow($(event.target).parent().attr('id'));
-
- return false;
-};
-
-ACL.prototype.on_button_hide = function(event){
- event.preventDefault()
- event.stopImmediatePropagation()
- event.stopPropagation();
-
- this.set_deny($(event.target).parent().attr('id'));
-
- return false;
-};
-
-ACL.prototype.set_allow = function(itemid) {
- type = itemid[0];
- id = parseInt(itemid.substr(1));
-
- switch (type){
- case "g":
- if (this.allow_gid.indexOf(id) < 0) {
- this.allow_gid.push(id);
- }else {
- this.allow_gid.remove(id);
- }
- if (this.deny_gid.indexOf(id) >= 0) {
- this.deny_gid.remove(id);
- }
- break;
- case "c":
- if (this.allow_cid.indexOf(id) < 0){
- this.allow_cid.push(id);
- if (this.data[id].forum == "1") {
- // If we have select already a forum,
- // we need to remove the old one (because friendica does
- // allow only one forum as receiver).
- if (this.forumCache !== null && this.forumCache !== id) {
- this.deselectCid(this.forumCache);
- }
- // Update the forum cache.
- this.forumCache = id;
- this.add_mention(id);
- }
- } else {
- this.allow_cid.remove(id);
- if (this.data[id].forum == "1") {
- this.remove_mention(id);
- }
- }
- if (this.deny_cid.indexOf(id) >=0 ) {
- this.deny_cid.remove(id);
- }
- break;
- }
- this.update_view();
-};
-
-ACL.prototype.set_deny = function(itemid){
- type = itemid[0];
- id = parseInt(itemid.substr(1));
-
- switch(type){
- case "g":
- if (this.deny_gid.indexOf(id)<0){
- this.deny_gid.push(id)
- } else {
- this.deny_gid.remove(id);
- }
- if (this.allow_gid.indexOf(id)>=0) this.allow_gid.remove(id);
- break;
- case "c":
- if (this.data[id].forum=="1") this.remove_mention(id);
- if (this.deny_cid.indexOf(id)<0){
- this.deny_cid.push(id)
- } else {
- this.deny_cid.remove(id);
- }
- if (this.allow_cid.indexOf(id)>=0) this.allow_cid.remove(id);
- break;
- }
- this.update_view();
-};
-
-ACL.prototype.is_show_all = function() {
- return (this.allow_gid.length==0 && this.allow_cid.length==0 &&
- this.deny_gid.length==0 && this.deny_cid.length==0);
-};
-
-ACL.prototype.update_view = function () {
- if (this.is_show_all()) {
- this.showall.addClass("selected");
- /* jot acl */
- $('#jot-perms-icon').removeClass('lock').addClass('unlock');
- $('#jot-public').show();
- $('.profile-jot-net input[type=checkbox]').each(function() {
- // Restores checkbox state if it had been saved
- if ($(this).attr('data-checked') !== undefined) {
- $(this).prop('checked', $(this).attr('data-checked') === 'true');
- }
- });
-
- $('.profile-jot-net input').attr('disabled', false);
- if (typeof editor != 'undefined' && editor != false) {
- $('#profile-jot-desc').html(ispublic);
- }
- } else {
- this.showall.removeClass("selected");
- /* jot acl */
- $('#jot-perms-icon').removeClass('unlock').addClass('lock');
- $('#jot-public').hide();
- $('.profile-jot-net input[type=checkbox]').each(function() {
- // Saves current checkbox state
- $(this)
- .attr('data-checked', $(this).prop('checked'))
- .prop('checked', false);
- });
- $('.profile-jot-net input').attr('disabled', 'disabled');
- $('#profile-jot-desc').html(' ');
- }
-
- $("#acl-list-content .acl-list-item").each(function (index, element) {
- $(this).removeClass("groupshow grouphide");
-
- itemid = $(element).attr('id');
- type = itemid[0];
- id = parseInt(itemid.substr(1));
-
- btshow = $(element).children(".acl-button-show").removeClass("selected");
- bthide = $(element).children(".acl-button-hide").removeClass("selected");
-
- switch (type) {
- case "g":
- var uclass = "";
- if (this.allow_gid.indexOf(id) >= 0) {
- btshow.addClass("selected");
- bthide.removeClass("selected");
- uclass = "groupshow";
- }
- if (this.deny_gid.indexOf(id) >= 0) {
- btshow.removeClass("selected");
- bthide.addClass("selected");
- uclass = "grouphide";
- }
-
- $(this.group_uids[id]).each(function (i, v) {
- if (uclass == "grouphide")
- $("#c" + v).removeClass("groupshow");
- if (uclass != "") {
- var cls = $("#c" + v).attr('class');
- if (cls == undefined)
- return true;
- var hiding = cls.indexOf('grouphide');
- if (hiding == -1)
- $("#c" + v).addClass(uclass);
- }
- });
-
- break;
- case "c":
- if (this.allow_cid.indexOf(id) >= 0) {
- btshow.addClass("selected");
- bthide.removeClass("selected");
- }
- if (this.deny_cid.indexOf(id) >= 0) {
- btshow.removeClass("selected");
- bthide.addClass("selected");
- }
- }
-
- }.bind(this));
-
-};
-
-ACL.prototype.get = function(start,count, search){
- var postdata = {
- start:start,
- count:count,
- search:search,
- }
-
- $.ajax({
- type:'POST',
- url: this.url,
- data: postdata,
- dataType: 'json',
- success:this.populate.bind(this)
- });
-};
-
-ACL.prototype.populate = function(data){
- var height = Math.ceil(data.tot / this.nw) * 42;
- this.list_content.height(height);
- this.data = {};
- $(data.items).each(function(index, item) {
- if (item.separator != undefined) {
- html = "<hr class='clear'>";
- } else {
- html = "<div class='acl-list-item {4} {5} type{2}' title='{6}' id='{2}{3}'>"+this.item_tpl+"</div>";
- html = html.format(item.photo, item.name, item.type, item.id, (item.forum=='1'?'forum':''), item.network, item.link);
- if (item.uids != undefined) {
- this.group_uids[item.id] = item.uids;
- }
- }
- this.list_content.append(html);
- this.data[item.id] = item;
- }.bind(this));
- $(".acl-list-item img[data-src]", this.list_content).each(function(i, el){
- // Add src attribute for images with a data-src attribute
- $(el).attr('src', $(el).data("src"));
- });
-
- this.update_view();
-};
-
-/**
- * @brief Deselect previous selected contact.
- *
- * @param {int} id The contact ID.
- * @returns {void}
- */
-ACL.prototype.deselectCid = function(id) {
- if (this.allow_cid.indexOf(id) >= 0) {
- this.allow_cid.remove(id);
- }
- if (this.deny_cid.indexOf(id) >=0 ) {
- this.deny_cid.remove(id);
- }
- this.remove_mention(id);
-};
-// @license-end
--- /dev/null
+The MIT License (MIT)
+
+Copyright (c) 2013 Tim Schlechter
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ *
+ */
+
+.twitter-typeahead .tt-query,
+.twitter-typeahead .tt-hint {
+ margin-bottom: 0;
+}
+
+.twitter-typeahead .tt-hint
+{
+ display: none;
+}
+
+.tt-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 160px;
+ padding: 5px 0;
+ margin: 2px 0 0;
+ list-style: none;
+ font-size: 14px;
+ background-color: #ffffff;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 4px;
+ -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ background-clip: padding-box;
+ cursor: pointer;
+}
+
+.tt-suggestion {
+ display: block;
+ padding: 3px 20px;
+ clear: both;
+ font-weight: normal;
+ line-height: 1.428571429;
+ color: #333333;
+ white-space: nowrap;
+}
+
+.tt-suggestion:hover,
+.tt-suggestion:focus {
+ color: #ffffff;
+ text-decoration: none;
+ outline: 0;
+ background-color: #428bca;
+}
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ *
+ * Non-Bootstrap edition
+ */
+
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: 700;
+ line-height: 1;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
+}
+
+.label-default {
+ background-color: #777777;
+}
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #5e5e5e;
+}
+.label-primary {
+ background-color: #337ab7;
+}
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #286090;
+}
+.label-success {
+ background-color: #5cb85c;
+}
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
+}
+.label-info {
+ background-color: #5bc0de;
+}
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
+}
+.label-warning {
+ background-color: #f0ad4e;
+}
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
+}
+.label-danger {
+ background-color: #d9534f;
+}
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
+}
+
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ background-color: #eeeeee;
+ opacity: 1;
+}
+.form-control[disabled],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+}
+
+
+
+
+.friendica-tagsinput {
+ background-color: #fff;
+ border: 1px solid #ccc;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ display: inline-block;
+ padding: 4px 6px;
+ color: #555;
+ vertical-align: middle;
+ border-radius: 4px;
+ max-width: 100%;
+ line-height: 22px;
+ cursor: text;
+ height: auto;
+}
+
+.friendica-tagsinput.input-lg {
+ line-height: 27px;
+}
+
+.friendica-tagsinput input {
+ border: none;
+ box-shadow: none;
+ outline: none;
+ background-color: transparent;
+ padding: 0 6px;
+ margin: 0;
+ width: auto;
+ max-width: inherit;
+}
+
+.friendica-tagsinput.form-control input::-moz-placeholder {
+ color: #777;
+ opacity: 1;
+}
+
+.friendica-tagsinput.form-control input:-ms-input-placeholder {
+ color: #777;
+}
+
+.friendica-tagsinput.form-control input::-webkit-input-placeholder {
+ color: #777;
+}
+
+.friendica-tagsinput input:focus {
+ border: none;
+ box-shadow: none;
+}
+
+.friendica-tagsinput .tag {
+ margin: 0 2px 2px 0;
+ color: white;
+ font-weight: normal;
+}
+
+.friendica-tagsinput .tag img {
+ width: auto;
+ height: 1.5em;
+ vertical-align: text-top;
+ margin-right: 8px;
+}
+
+.friendica-tagsinput .tag [data-role="remove"] {
+ margin-left: 8px;
+ cursor: pointer;
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:after {
+ content: "x";
+ padding: 0px 2px;
+ font-weight: bold;
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:hover {
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.friendica-tagsinput .tag [data-role="remove"]:hover:active {
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
--- /dev/null
+/*
+ * friendica-tagsinput v0.8.0
+ * Based on bootstrap-tagsinput v0.8.0
+ *
+ * Adds:
+ * - optional thumbnail
+ * - copying source input element class to the pseudo-input element
+ *
+ */
+
+(function ($) {
+ "use strict";
+
+ var defaultOptions = {
+ tagClass: function(item) {
+ return 'label label-info';
+ },
+ focusClass: 'focus',
+ itemValue: function(item) {
+ return item ? item.toString() : item;
+ },
+ itemText: function(item) {
+ return this.itemValue(item);
+ },
+ itemTitle: function(item) {
+ return null;
+ },
+ itemThumb: function(item) {
+ return null;
+ },
+ freeInput: true,
+ addOnBlur: true,
+ maxTags: undefined,
+ maxChars: undefined,
+ confirmKeys: [13, 44],
+ delimiter: ',',
+ delimiterRegex: null,
+ cancelConfirmKeysOnEmpty: false,
+ onTagExists: function(item, $tag) {
+ $tag.hide().fadeIn();
+ },
+ trimValue: false,
+ allowDuplicates: false,
+ triggerChange: true
+ };
+
+ /**
+ * Constructor function
+ */
+ function TagsInput(element, options) {
+ this.isInit = true;
+ this.itemsArray = [];
+
+ this.$element = $(element);
+ this.$element.hide();
+
+ this.isSelect = (element.tagName === 'SELECT');
+ this.multiple = (this.isSelect && element.hasAttribute('multiple'));
+ this.objectItems = options && options.itemValue;
+ this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
+ this.inputSize = Math.max(1, this.placeholderText.length);
+
+ this.$container = $('<div class="friendica-tagsinput"></div>');
+ this.$container.addClass(this.$element.attr('class'));
+ this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
+
+ this.$element.before(this.$container);
+
+ this.build(options);
+ this.isInit = false;
+ }
+
+ TagsInput.prototype = {
+ constructor: TagsInput,
+
+ /**
+ * Adds the given item as a new tag. Pass true to dontPushVal to prevent
+ * updating the elements val()
+ */
+ add: function(item, dontPushVal, options) {
+ let self = this;
+
+ if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
+ return;
+
+ // Ignore falsey values, except false
+ if (item !== false && !item)
+ return;
+
+ // Trim value
+ if (typeof item === "string" && self.options.trimValue) {
+ item = $.trim(item);
+ }
+
+ // Throw an error when trying to add an object while the itemValue option was not set
+ if (typeof item === "object" && !self.objectItems)
+ throw("Can't add objects when itemValue option is not set");
+
+ // Ignore strings only containg whitespace
+ if (item.toString().match(/^\s*$/))
+ return;
+
+ // If SELECT but not multiple, remove current tag
+ if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
+ self.remove(self.itemsArray[0]);
+
+ if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
+ var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
+ var items = item.split(delimiter);
+ if (items.length > 1) {
+ for (var i = 0; i < items.length; i++) {
+ this.add(items[i], true);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+ return;
+ }
+ }
+
+ var itemValue = self.options.itemValue(item),
+ itemText = self.options.itemText(item),
+ tagClass = self.options.tagClass(item),
+ itemTitle = self.options.itemTitle(item),
+ itemThumb = self.options.itemThumb(item);
+
+ // Ignore items allready added
+ var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
+ if (existing && !self.options.allowDuplicates) {
+ // Invoke onTagExists
+ if (self.options.onTagExists) {
+ var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
+ self.options.onTagExists(item, $existingTag);
+ }
+ return;
+ }
+
+ // if length greater than limit
+ if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
+ return;
+
+ // raise beforeItemAdd arg
+ var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
+ self.$element.trigger(beforeItemAddEvent);
+ if (beforeItemAddEvent.cancel)
+ return;
+
+ // register item in internal array and map
+ self.itemsArray.push(item);
+
+ // add a tag element
+ var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' +
+ (itemThumb !== null ? '<img src="' + itemThumb + '" alt="">' : '') +
+ htmlEncode(itemText) + '<span data-role="remove"></span>' +
+ '</span>');
+ $tag.data('item', item);
+ self.findInputWrapper().before($tag);
+ $tag.after(' ');
+
+ // Check to see if the tag exists in its raw or uri-encoded form
+ var optionExists = (
+ $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
+ $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
+ );
+
+ // add <option /> if item represents a value not present in one of the <select />'s options
+ if (self.isSelect && !optionExists) {
+ var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
+ $option.data('item', item);
+ $option.attr('value', itemValue);
+ self.$element.append($option);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+
+ // Add class when reached maxTags
+ if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
+ self.$container.addClass('friendica-tagsinput-max');
+
+ // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
+ if ($('.typeahead, .twitter-typeahead', self.$container).length) {
+ self.$input.typeahead('val', '');
+ }
+
+ if (this.isInit) {
+ self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
+ } else {
+ self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
+ }
+ },
+
+ /**
+ * Removes the given item. Pass true to dontPushVal to prevent updating the
+ * elements val()
+ */
+ remove: function(item, dontPushVal, options) {
+ var self = this;
+
+ if (self.objectItems) {
+ if (typeof item === "object")
+ item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
+ else
+ item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
+
+ item = item[item.length-1];
+ }
+
+ if (item) {
+ var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
+ self.$element.trigger(beforeItemRemoveEvent);
+ if (beforeItemRemoveEvent.cancel)
+ return;
+
+ $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
+ $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
+ if($.inArray(item, self.itemsArray) !== -1)
+ self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
+ }
+
+ if (!dontPushVal)
+ self.pushVal(self.options.triggerChange);
+
+ // Remove class when reached maxTags
+ if (self.options.maxTags > self.itemsArray.length)
+ self.$container.removeClass('friendica-tagsinput-max');
+
+ self.$element.trigger($.Event('itemRemoved', { item: item, options: options }));
+ },
+
+ /**
+ * Removes all items
+ */
+ removeAll: function() {
+ var self = this;
+
+ $('.tag', self.$container).remove();
+ $('option', self.$element).remove();
+
+ while(self.itemsArray.length > 0)
+ self.itemsArray.pop();
+
+ self.pushVal(self.options.triggerChange);
+ },
+
+ /**
+ * Refreshes the tags so they match the text/value of their corresponding
+ * item.
+ */
+ refresh: function() {
+ var self = this;
+ $('.tag', self.$container).each(function() {
+ var $tag = $(this),
+ item = $tag.data('item'),
+ itemValue = self.options.itemValue(item),
+ itemText = self.options.itemText(item),
+ tagClass = self.options.tagClass(item);
+
+ // Update tag's class and inner text
+ $tag.attr('class', null);
+ $tag.addClass('tag ' + htmlEncode(tagClass));
+ $tag.contents().filter(function() {
+ return this.nodeType == 3;
+ })[0].nodeValue = htmlEncode(itemText);
+
+ if (self.isSelect) {
+ var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
+ option.attr('value', itemValue);
+ }
+ });
+ },
+
+ /**
+ * Returns the items added as tags
+ */
+ items: function() {
+ return this.itemsArray;
+ },
+
+ /**
+ * Assembly value by retrieving the value of each item, and set it on the
+ * element.
+ */
+ pushVal: function() {
+ var self = this,
+ val = $.map(self.items(), function(item) {
+ return self.options.itemValue(item).toString();
+ });
+
+ self.$element.val(val, true);
+
+ if (self.options.triggerChange)
+ self.$element.trigger('change');
+ },
+
+ /**
+ * Initializes the tags input behaviour on the element
+ */
+ build: function(options) {
+ var self = this;
+
+ self.options = $.extend({}, defaultOptions, options);
+ // When itemValue is set, freeInput should always be false
+ if (self.objectItems)
+ self.options.freeInput = false;
+
+ makeOptionItemFunction(self.options, 'itemValue');
+ makeOptionItemFunction(self.options, 'itemText');
+ makeOptionItemFunction(self.options, 'itemThumb');
+ makeOptionFunction(self.options, 'tagClass');
+
+ // Typeahead Bootstrap version 2.3.2
+ if (self.options.typeahead) {
+ var typeahead = self.options.typeahead || {};
+
+ makeOptionFunction(typeahead, 'source');
+
+ self.$input.typeahead($.extend({}, typeahead, {
+ source: function (query, process) {
+ function processItems(items) {
+ var texts = [];
+
+ for (var i = 0; i < items.length; i++) {
+ var text = self.options.itemText(items[i]);
+ map[text] = items[i];
+ texts.push(text);
+ }
+ process(texts);
+ }
+
+ this.map = {};
+ var map = this.map,
+ data = typeahead.source(query);
+
+ if ($.isFunction(data.success)) {
+ // support for Angular callbacks
+ data.success(processItems);
+ } else if ($.isFunction(data.then)) {
+ // support for Angular promises
+ data.then(processItems);
+ } else {
+ // support for functions and jquery promises
+ $.when(data)
+ .then(processItems);
+ }
+ },
+ updater: function (text) {
+ self.add(this.map[text]);
+ return this.map[text];
+ },
+ matcher: function (text) {
+ return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
+ },
+ sorter: function (texts) {
+ return texts.sort();
+ },
+ highlighter: function (text) {
+ var regex = new RegExp( '(' + this.query + ')', 'gi' );
+ return text.replace( regex, "<strong>$1</strong>" );
+ }
+ }));
+ }
+
+ // typeahead.js
+ if (self.options.typeaheadjs) {
+ var typeaheadConfig = null;
+ var typeaheadDatasets = {};
+
+ // Determine if main configurations were passed or simply a dataset
+ var typeaheadjs = self.options.typeaheadjs;
+ if ($.isArray(typeaheadjs)) {
+ typeaheadConfig = typeaheadjs[0];
+ typeaheadDatasets = typeaheadjs[1];
+ } else {
+ typeaheadDatasets = typeaheadjs;
+ }
+
+ self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
+ if (typeaheadDatasets.valueKey)
+ self.add(datum[typeaheadDatasets.valueKey]);
+ else
+ self.add(datum);
+ self.$input.typeahead('val', '');
+ }, self));
+ }
+
+ self.$container.on('click', $.proxy(function(event) {
+ if (! self.$element.attr('disabled')) {
+ self.$input.removeAttr('disabled');
+ }
+ self.$input.focus();
+ }, self));
+
+ if (self.options.addOnBlur && self.options.freeInput) {
+ self.$input.on('focusout', $.proxy(function(event) {
+ // HACK: only process on focusout when no typeahead opened, to
+ // avoid adding the typeahead text as tag
+ if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
+ self.add(self.$input.val());
+ self.$input.val('');
+ }
+ }, self));
+ }
+
+ // Toggle the 'focus' css class on the container when it has focus
+ self.$container.on({
+ focusin: function() {
+ self.$container.addClass(self.options.focusClass);
+ },
+ focusout: function() {
+ self.$container.removeClass(self.options.focusClass);
+ },
+ });
+
+ self.$container.on('keydown', 'input', $.proxy(function(event) {
+ var $input = $(event.target),
+ $inputWrapper = self.findInputWrapper();
+
+ if (self.$element.attr('disabled')) {
+ self.$input.attr('disabled', 'disabled');
+ return;
+ }
+
+ switch (event.which) {
+ // BACKSPACE
+ case 8:
+ if (doGetCaretPosition($input[0]) === 0) {
+ var prev = $inputWrapper.prev();
+ if (prev.length) {
+ self.remove(prev.data('item'));
+ }
+ }
+ break;
+
+ // DELETE
+ case 46:
+ if (doGetCaretPosition($input[0]) === 0) {
+ var next = $inputWrapper.next();
+ if (next.length) {
+ self.remove(next.data('item'));
+ }
+ }
+ break;
+
+ // LEFT ARROW
+ case 37:
+ // Try to move the input before the previous tag
+ var $prevTag = $inputWrapper.prev();
+ if ($input.val().length === 0 && $prevTag[0]) {
+ $prevTag.before($inputWrapper);
+ $input.focus();
+ }
+ break;
+ // RIGHT ARROW
+ case 39:
+ // Try to move the input after the next tag
+ var $nextTag = $inputWrapper.next();
+ if ($input.val().length === 0 && $nextTag[0]) {
+ $nextTag.after($inputWrapper);
+ $input.focus();
+ }
+ break;
+ default:
+ // ignore
+ }
+
+ // Reset internal input's size
+ var textLength = $input.val().length,
+ wordSpace = Math.ceil(textLength / 5),
+ size = textLength + wordSpace + 1;
+ $input.attr('size', Math.max(this.inputSize, $input.val().length));
+ }, self));
+
+ self.$container.on('keypress', 'input', $.proxy(function(event) {
+ var $input = $(event.target);
+
+ if (self.$element.attr('disabled')) {
+ self.$input.attr('disabled', 'disabled');
+ return;
+ }
+
+ var text = $input.val(),
+ maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
+ if (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached) {
+ // Only attempt to add a tag if there is data in the field
+ if (self.options.freeInput && text.length !== 0) {
+ self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
+ $input.val('');
+ }
+
+ // If the field is empty, let the event triggered fire as usual
+ if (self.options.cancelConfirmKeysOnEmpty === false) {
+ event.preventDefault();
+ }
+ }
+
+ // Reset internal input's size
+ var textLength = $input.val().length,
+ wordSpace = Math.ceil(textLength / 5),
+ size = textLength + wordSpace + 1;
+ $input.attr('size', Math.max(this.inputSize, $input.val().length));
+ }, self));
+
+ // Remove icon clicked
+ self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
+ if (self.$element.attr('disabled')) {
+ return;
+ }
+ self.remove($(event.target).closest('.tag').data('item'));
+ }, self));
+
+ // Only add existing value as tags when using strings as tags
+ if (self.options.itemValue === defaultOptions.itemValue) {
+ if (self.$element[0].tagName === 'INPUT') {
+ self.add(self.$element.val());
+ } else {
+ $('option', self.$element).each(function() {
+ self.add($(this).attr('value'), true);
+ });
+ }
+ }
+ },
+
+ /**
+ * Removes all tagsinput behaviour and unregsiter all event handlers
+ */
+ destroy: function() {
+ var self = this;
+
+ // Unbind events
+ self.$container.off('keypress', 'input');
+ self.$container.off('click', '[role=remove]');
+
+ self.$container.remove();
+ self.$element.removeData('tagsinput');
+ self.$element.show();
+ },
+
+ /**
+ * Sets focus on the tagsinput
+ */
+ focus: function() {
+ this.$input.focus();
+ },
+
+ /**
+ * Returns the internal input element
+ */
+ input: function() {
+ return this.$input;
+ },
+
+ /**
+ * Returns the element which is wrapped around the internal input. This
+ * is normally the $container, but typeahead.js moves the $input element.
+ */
+ findInputWrapper: function() {
+ var elt = this.$input[0],
+ container = this.$container[0];
+ while(elt && elt.parentNode !== container)
+ elt = elt.parentNode;
+
+ return $(elt);
+ }
+ };
+
+ /**
+ * Register JQuery plugin
+ */
+ $.fn.tagsinput = function(arg1, arg2, arg3) {
+ var results = [];
+
+ this.each(function() {
+ var tagsinput = $(this).data('tagsinput');
+ // Initialize a new tags input
+ if (!tagsinput) {
+ tagsinput = new TagsInput(this, arg1);
+ $(this).data('tagsinput', tagsinput);
+ results.push(tagsinput);
+
+ if (this.tagName === 'SELECT') {
+ $('option', $(this)).attr('selected', 'selected');
+ }
+
+ // Init tags from $(this).val()
+ $(this).val($(this).val());
+ } else if (!arg1 && !arg2) {
+ // tagsinput already exists
+ // no function, trying to init
+ results.push(tagsinput);
+ } else if(tagsinput[arg1] !== undefined) {
+ // Invoke function on existing tags input
+ if(tagsinput[arg1].length === 3 && arg3 !== undefined){
+ var retVal = tagsinput[arg1](arg2, null, arg3);
+ }else{
+ var retVal = tagsinput[arg1](arg2);
+ }
+ if (retVal !== undefined)
+ results.push(retVal);
+ }
+ });
+
+ if ( typeof arg1 == 'string') {
+ // Return the results from the invoked function calls
+ return results.length > 1 ? results : results[0];
+ } else {
+ return results;
+ }
+ };
+
+ $.fn.tagsinput.Constructor = TagsInput;
+
+ /**
+ * Most options support both a string or number as well as a function as
+ * option value. This function makes sure that the option with the given
+ * key in the given options is wrapped in a function
+ */
+ function makeOptionItemFunction(options, key) {
+ if (typeof options[key] !== 'function') {
+ var propertyName = options[key];
+ options[key] = function(item) { return item[propertyName]; };
+ }
+ }
+ function makeOptionFunction(options, key) {
+ if (typeof options[key] !== 'function') {
+ var value = options[key];
+ options[key] = function() { return value; };
+ }
+ }
+ /**
+ * HtmlEncodes the given value
+ */
+ var htmlEncodeContainer = $('<div />');
+ function htmlEncode(value) {
+ if (value) {
+ return htmlEncodeContainer.text(value).html();
+ } else {
+ return '';
+ }
+ }
+
+ /**
+ * Returns the position of the caret in the given input field
+ * http://flightschool.acylt.com/devnotes/caret-position-woes/
+ */
+ function doGetCaretPosition(oField) {
+ var iCaretPos = 0;
+ if (document.selection) {
+ oField.focus ();
+ var oSel = document.selection.createRange();
+ oSel.moveStart ('character', -oField.value.length);
+ iCaretPos = oSel.text.length;
+ } else if (oField.selectionStart || oField.selectionStart == '0') {
+ iCaretPos = oField.selectionStart;
+ }
+ return (iCaretPos);
+ }
+
+ /**
+ * Returns boolean indicates whether user has pressed an expected key combination.
+ * @param object keyPressEvent: JavaScript event object, refer
+ * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+ * @param object lookupList: expected key combinations, as in:
+ * [13, {which: 188, shiftKey: true}]
+ */
+ function keyCombinationInList(keyPressEvent, lookupList) {
+ var found = false;
+ $.each(lookupList, function (index, keyCombination) {
+ if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
+ found = true;
+ return false;
+ }
+
+ if (keyPressEvent.which === keyCombination.which) {
+ var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
+ shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
+ ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
+ if (alt && shift && ctrl) {
+ found = true;
+ return false;
+ }
+ }
+ });
+
+ return found;
+ }
+
+ /**
+ * Initialize tagsinput behaviour on inputs and selects which have
+ * data-role=tagsinput
+ */
+ $(function() {
+ $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
+ });
+})(window.jQuery);
$.fn.linkPreview = function (options) {
var opts = jQuery.extend({}, $.fn.linkPreview.defaults, options);
- var selector = $(this).selector;
- selector = selector.substr(1);
+ var id = $(this).attr('id');
var previewTpl = '\
- <div id="preview_' + selector + '" class="preview {0}">\
+ <div id="preview_' + id + '" class="preview {0}">\
{1}\
- <input type="hidden" name="has_attachment" id="hasAttachment_' + selector + '" value="{2}" />\
- <input type="hidden" name="attachment_url" id="attachmentUrl_' + selector + '" value="{3}" />\
- <input type="hidden" name="attachment_type" id="attachmentType_' + selector + '" value="{4}" />\
+ <input type="hidden" name="has_attachment" id="hasAttachment_' + id + '" value="{2}" />\
+ <input type="hidden" name="attachment_url" id="attachmentUrl_' + id + '" value="{3}" />\
+ <input type="hidden" name="attachment_type" id="attachmentType_' + id + '" value="{4}" />\
</div>';
var attachmentTpl = '\
<hr class="previewseparator">\
- <div id="closePreview_' + selector + '" title="Remove" class="closePreview" >\
+ <div id="closePreview_' + id + '" title="Remove" class="closePreview" >\
<button type="button" class="previewActionBtn">×</button>\
</div>\
- <div id="previewImages_' + selector + '" class="previewImages">\
- <div id="previewImgBtn_' + selector + '" class="previewImgBtn">\
- <button type="button" id="previewChangeImg_' + selector + '" class="buttonChangeDeactive previewActionBtn" style="display: none">\
+ <div id="previewImages_' + id + '" class="previewImages">\
+ <div id="previewImgBtn_' + id + '" class="previewImgBtn">\
+ <button type="button" id="previewChangeImg_' + id + '" class="buttonChangeDeactive previewActionBtn" style="display: none">\
<i class="fa fa-exchange" aria-hidden="true"></i>\
</button>\
</div>\
- <div id="previewImage_' + selector + '" class="previewImage">\
+ <div id="previewImage_' + id + '" class="previewImage">\
</div>\
- <input type="hidden" id="photoNumber_' + selector + '" class="photoNumber" value="0" />\
- <input type="hidden" name="attachment_img_src" id="attachmentImageSrc_' + selector + '" value="" />\
- <input type="hidden" name="attachment_img_width" id="attachmentImageWidth_' + selector + '" value="0" />\
- <input type="hidden" name="attachment_img_height" id="attachmentImageHeight_' + selector + '" value="0" />\
+ <input type="hidden" id="photoNumber_' + id + '" class="photoNumber" value="0" />\
+ <input type="hidden" name="attachment_img_src" id="attachmentImageSrc_' + id + '" value="" />\
+ <input type="hidden" name="attachment_img_width" id="attachmentImageWidth_' + id + '" value="0" />\
+ <input type="hidden" name="attachment_img_height" id="attachmentImageHeight_' + id + '" value="0" />\
</div>\
- <div id="previewContent_' + selector + '" class="previewContent">\
- <h4 id="previewTitle_' + selector + '" class="previewTitle"></h4>\
- <blockquote id="previewDescription_' + selector + '" class="previewDescription"></blockquote>\
- <div id="hiddenDescription_' + selector + '" class="hiddenDescription"></div>\
- <sup id="previewUrl_' + selector + '" class="previewUrl"></sup>\
+ <div id="previewContent_' + id + '" class="previewContent">\
+ <h4 id="previewTitle_' + id + '" class="previewTitle"></h4>\
+ <blockquote id="previewDescription_' + id + '" class="previewDescription"></blockquote>\
+ <div id="hiddenDescription_' + id + '" class="hiddenDescription"></div>\
+ <sup id="previewUrl_' + id + '" class="previewUrl"></sup>\
</div>\
<div class="clear"></div>\
<hr class="previewseparator">';
* @returns {void}
*/
var init = function() {
- $('#' + selector).bind({
+ $('#' + id).bind({
paste: function () {
setTimeout(function () {
crawlText();
// Check if we have already attachment bbcode in the textarea
// and add it to the attachment preview.
- var content = $('#' + selector).val();
+ var content = $('#' + id).val();
addBBCodeToPreview(content);
};
* @returns {void}
*/
var resetPreview = function() {
- $('#hasAttachment_' + selector).val(0);
+ $('#hasAttachment_' + id).val(0);
photoNumber = 0;
images = "";
};
// If no text is passed to crawlText() we
// take the previous word before the cursor.
if (typeof text === 'undefined') {
- text = getPrevWord(selector);
+ text = getPrevWord(id);
} else {
isExtern = true;
}
return;
}
- $('#photoNumber_' + selector).val(0);
+ $('#photoNumber_' + id).val(0);
resetPreview();
processAttachmentTpl(data, 'type-' + data.type);
*/
var processAttachmentTpl = function(data) {
// Load and add the template if it isn't allready loaded.
- if ($('#preview_' + selector).length === 0) {
+ if ($('#preview_' + id).length === 0) {
var tpl = previewTpl.format(
'type-' + data.type,
attachmentTpl,
bin2hex(data.url),
data.type
);
- $('#' + selector).after(tpl);
+ $('#' + id).after(tpl);
}
isActive = true;
description = defaultDescription;
}
- $('#previewTitle_' + selector).html("\
- <span id='previewSpanTitle_" + selector + "' class='previewSpanTitle' >" + escapeHTML(data.title) + "</span>\
- <input type='text' name='attachment_title' value='" + escapeHTML(data.title) + "' id='previewInputTitle_" + selector + "' class='previewInputTitle inputPreview' style='display: none;'/>"
+ $('#previewTitle_' + id).html("\
+ <span id='previewSpanTitle_" + id + "' class='previewSpanTitle' >" + escapeHTML(data.title) + "</span>\
+ <input type='text' name='attachment_title' value='" + escapeHTML(data.title) + "' id='previewInputTitle_" + id + "' class='previewInputTitle inputPreview' style='display: none;'/>"
);
- $('#previewDescription_' + selector).html("\
- <span id='previewSpanDescription_" + selector + "' class='previewSpanDescription' >" + escapeHTML(description) + "</span>\n\
- <textarea id='previewInputDescription_" + selector + "' name='attachment_text' class='previewInputDescription' style='display: none;' class='inputPreview' >" + escapeHTML(data.text) + "</textarea>"
+ $('#previewDescription_' + id).html("\
+ <span id='previewSpanDescription_" + id + "' class='previewSpanDescription' >" + escapeHTML(description) + "</span>\n\
+ <textarea id='previewInputDescription_" + id + "' name='attachment_text' class='previewInputDescription' style='display: none;' class='inputPreview' >" + escapeHTML(data.text) + "</textarea>"
);
};
var regexpr = "(https?://)([^:^/]*)(:\\d*)?(.*)?";
var regResult = url.match(regexpr);
var urlHost = regResult[1] + regResult[2];
- $('#previewUrl_' + selector).html("<a href='" + url + "'>" + urlHost + "</a>");
+ $('#previewUrl_' + id).html("<a href='" + url + "'>" + urlHost + "</a>");
}
};
var imageClass = 'attachment-preview';
if (Array.isArray(images)) {
- $('#previewImages_' + selector).show();
- $('#attachmentImageSrc_' + selector).val(bin2hex(images[photoNumber].src));
- $('#attachmentImageWidth_' + selector).val(images[photoNumber].width);
- $('#attachmentImageHeight_' + selector).val(images[photoNumber].height);
+ $('#previewImages_' + id).show();
+ $('#attachmentImageSrc_' + id).val(bin2hex(images[photoNumber].src));
+ $('#attachmentImageWidth_' + id).val(images[photoNumber].width);
+ $('#attachmentImageHeight_' + id).val(images[photoNumber].height);
} else {
- $('#previewImages_' + selector).hide();
+ $('#previewImages_' + id).hide();
}
images.length = parseInt(images.length);
}
if (i === 0) {
- appendImage += "<img id='imagePreview_" + selector + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' ></img>";
+ appendImage += "<img id='imagePreview_" + id + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' ></img>";
} else {
- appendImage += "<img id='imagePreview_" + selector + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' style='display: none;'></img>";
+ appendImage += "<img id='imagePreview_" + id + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' style='display: none;'></img>";
}
}
- $('#previewImage_' + selector).html(appendImage + "<div id='whiteImage' style='color: transparent; display:none;'>...</div>");
+ $('#previewImage_' + id).html(appendImage + "<div id='whiteImage' style='color: transparent; display:none;'>...</div>");
// More than just one image.
if (images.length > 1) {
// Enable the the button to change the preview pictures.
- $('#previewChangeImg_' + selector).show();
+ $('#previewChangeImg_' + id).show();
if (firstPosted === false) {
firstPosted = true;
- $('#previewChangeImg_' + selector).unbind('click').click(function (e) {
+ $('#previewChangeImg_' + id).unbind('click').click(function (e) {
e.stopPropagation();
if (images.length > 1) {
- $('#imagePreview_' + selector + '_' + photoNumber).css({
+ $('#imagePreview_' + id + '_' + photoNumber).css({
'display': 'none'
});
photoNumber += 1;
photoNumber = 0;
}
- $('#imagePreview_' + selector + '_' + photoNumber).css({
+ $('#imagePreview_' + id + '_' + photoNumber).css({
'display': 'block'
});
- $('#photoNumber_' + selector).val(photoNumber);
- $('#attachmentImageSrc_' + selector).val(bin2hex(images[photoNumber].src));
- $('#attachmentImageWidth_' + selector).val(images[photoNumber].width);
- $('#attachmentImageHeight_' + selector).val(images[photoNumber].height);
+ $('#photoNumber_' + id).val(photoNumber);
+ $('#attachmentImageSrc_' + id).val(bin2hex(images[photoNumber].src));
+ $('#attachmentImageWidth_' + id).val(images[photoNumber].width);
+ $('#attachmentImageHeight_' + id).val(images[photoNumber].height);
}
});
}
* @returns {void}
*/
var processEventListener = function() {
- $('#previewSpanTitle_' + selector).unbind('click').click(function (e) {
+ $('#previewSpanTitle_' + id).unbind('click').click(function (e) {
e.stopPropagation();
if (blockTitle === false) {
blockTitle = true;
- $('#previewSpanTitle_' + selector).hide();
- $('#previewInputTitle_' + selector).show();
- $('#previewInputTitle_' + selector).val($('#previewInputTitle_' + selector).val());
- $('#previewInputTitle_' + selector).focus().select();
+ $('#previewSpanTitle_' + id).hide();
+ $('#previewInputTitle_' + id).show();
+ $('#previewInputTitle_' + id).val($('#previewInputTitle_' + id).val());
+ $('#previewInputTitle_' + id).focus().select();
}
});
- $('#previewInputTitle_' + selector).blur(function () {
+ $('#previewInputTitle_' + id).blur(function () {
blockTitle = false;
- $('#previewSpanTitle_' + selector).html($('#previewInputTitle_' + selector).val());
- $('#previewSpanTitle_' + selector).show();
- $('#previewInputTitle_' + selector).hide();
+ $('#previewSpanTitle_' + id).html($('#previewInputTitle_' + id).val());
+ $('#previewSpanTitle_' + id).show();
+ $('#previewInputTitle_' + id).hide();
});
- $('#previewInputTitle_' + selector).keypress(function (e) {
+ $('#previewInputTitle_' + id).keypress(function (e) {
if (e.which === 13) {
blockTitle = false;
- $('#previewSpanTitle_' + selector).html($('#previewInputTitle_' + selector).val());
- $('#previewSpanTitle_' + selector).show();
- $('#previewInputTitle_' + selector).hide();
+ $('#previewSpanTitle_' + id).html($('#previewInputTitle_' + id).val());
+ $('#previewSpanTitle_' + id).show();
+ $('#previewInputTitle_' + id).hide();
}
});
- $('#previewSpanDescription_' + selector).unbind('click').click(function (e) {
+ $('#previewSpanDescription_' + id).unbind('click').click(function (e) {
e.stopPropagation();
if (blockDescription === false) {
blockDescription = true;
- $('#previewSpanDescription_' + selector).hide();
- $('#previewInputDescription_' + selector).show();
- $('#previewInputDescription_' + selector).val($('#previewInputDescription_' + selector).val());
- $('#previewInputDescription_' + selector).focus().select();
+ $('#previewSpanDescription_' + id).hide();
+ $('#previewInputDescription_' + id).show();
+ $('#previewInputDescription_' + id).val($('#previewInputDescription_' + id).val());
+ $('#previewInputDescription_' + id).focus().select();
}
});
- $('#previewInputDescription_' + selector).blur(function () {
+ $('#previewInputDescription_' + id).blur(function () {
blockDescription = false;
- $('#previewSpanDescription_' + selector).html($('#previewInputDescription_' + selector).val());
- $('#previewSpanDescription_' + selector).show();
- $('#previewInputDescription_' + selector).hide();
+ $('#previewSpanDescription_' + id).html($('#previewInputDescription_' + id).val());
+ $('#previewSpanDescription_' + id).show();
+ $('#previewInputDescription_' + id).hide();
});
- $('#previewInputDescription_' + selector).keypress(function (e) {
+ $('#previewInputDescription_' + id).keypress(function (e) {
if (e.which === 13) {
blockDescription = false;
- $('#previewSpanDescription_' + selector).html($('#previewInputDescription_' + selector).val());
- $('#previewSpanDescription_' + selector).show();
- $('#previewInputDescription_' + selector).hide();
+ $('#previewSpanDescription_' + id).html($('#previewInputDescription_' + id).val());
+ $('#previewSpanDescription_' + id).show();
+ $('#previewInputDescription_' + id).hide();
}
});
- $('#previewSpanTitle_' + selector).mouseover(function () {
- $('#previewSpanTitle_' + selector).css({
+ $('#previewSpanTitle_' + id).mouseover(function () {
+ $('#previewSpanTitle_' + id).css({
"background-color": "#ff9"
});
});
- $('#previewSpanTitle_' + selector).mouseout(function () {
- $('#previewSpanTitle_' + selector).css({
+ $('#previewSpanTitle_' + id).mouseout(function () {
+ $('#previewSpanTitle_' + id).css({
"background-color": "transparent"
});
});
- $('#previewSpanDescription_' + selector).mouseover(function () {
- $('#previewSpanDescription_' + selector).css({
+ $('#previewSpanDescription_' + id).mouseover(function () {
+ $('#previewSpanDescription_' + id).css({
"background-color": "#ff9"
});
});
- $('#previewSpanDescription_' + selector).mouseout(function () {
- $('#previewSpanDescription_' + selector).css({
+ $('#previewSpanDescription_' + id).mouseout(function () {
+ $('#previewSpanDescription_' + id).css({
"background-color": "transparent"
});
});
- $('#closePreview_' + selector).unbind('click').click(function (e) {
+ $('#closePreview_' + id).unbind('click').click(function (e) {
e.stopPropagation();
block = false;
images = '';
isActive = false;
firstPosted = false;
- $('#preview_' + selector).fadeOut("fast", function () {
- $('#preview_' + selector).remove();
+ $('#preview_' + id).fadeOut("fast", function () {
+ $('#preview_' + id).remove();
$('#profile-rotator').hide();
- $('#' + selector).focus();
+ $('#' + id).focus();
});
});
reAddAttachment(attachmentData);
// Remove the attachment bbcode from the textarea.
var content = content.replace(/\[attachment[\s\S]*\[\/attachment]/im, '');
- $('#' + selector).val(content);
- $('#' + selector).focus();
+ $('#' + id).val(content);
+ $('#' + id).focus();
}
};
}
if (image !== '') {
- var appendImage = "<img id='imagePreview_" + selector + "' src='" + image + "' class='" + imageClass + "' ></img>"
- $('#previewImage_' + selector).html(appendImage);
- $('#attachmentImageSrc_' + selector).val(bin2hex(image));
+ var appendImage = "<img id='imagePreview_" + id + "' src='" + image + "' class='" + imageClass + "' ></img>"
+ $('#previewImage_' + id).html(appendImage);
+ $('#attachmentImageSrc_' + id).val(bin2hex(image));
// We need to add the image widht and height when it is
// loaded.
$('<img/>' ,{
load : function(){
- $('#attachmentImageWidth_' + selector).val(this.width);
- $('#attachmentImageHeight_' + selector).val(this.height);
+ $('#attachmentImageWidth_' + id).val(this.width);
+ $('#attachmentImageHeight_' + id).val(this.height);
},
src : image
});
* @returns {void}
*/
var destroy = function() {
- $('#' + selector).unbind();
- $('#preview_' + selector).remove();
+ $('#' + id).unbind();
+ $('#preview_' + id).remove();
binurl;
block = false;
blockTitle = false;
firstPosted = false;
isActive = false;
isCrawling = false;
- selector = "";
+ id = "";
};
var trim = function(str) {
ident = ident.toString();
$('#like-rotator-' + ident).show();
$.get('item/ignore/' + ident, function(data) {
- if (data.match(/1/)) {
- $('#ignored-' + ident).addClass('ignored');
- $('#ignored-' + ident).removeClass('unignored');
+ if (data === 1) {
+ $('#ignored-' + ident)
+ .addClass('ignored')
+ .removeClass('unignored');
$('#ignore-' + ident).addClass('hidden');
$('#unignore-' + ident).removeClass('hidden');
} else {
- $('#ignored-' + ident).addClass('unignored');
- $('#ignored-' + ident).removeClass('ignored');
+ $('#ignored-' + ident)
+ .addClass('unignored')
+ .removeClass('ignored');
$('#ignore-' + ident).removeClass('hidden');
$('#unignore-' + ident).addClass('hidden');
}
}
function preview_post() {
- $("#jot-preview").val("1");
$("#jot-preview-content").show();
$.post(
"item",
- $("#profile-jot-form").serialize(),
+ $("#profile-jot-form").serialize() + '&preview=1',
function(data) {
if (data.preview) {
$("#jot-preview-content").html(data.preview);
},
"json"
);
- $("#jot-preview").val("0");
return true;
}
-
<div id="acl-wrapper">
- <input id="acl-search" autocomplete="off">
- <a id="acl-showall">{{$showall}}</a>
- <div id="acl-list">
- <div id="acl-list-content">
+ <div class="panel-group" id="visibility-accordion" role="tablist" aria-multiselectable="true">
+ <div class="panel panel-success">
+ <label class="panel-heading{{if $visibility != 'public'}} collapsed{{/if}}" id="visibility-public-heading" aria-expanded="{{if $visibility == 'public'}}true{{else}}false{{/if}}">
+ <input type="radio" name="visibility" id="visibility-public" value="public" tabindex="14" {{if $visibility == 'public'}}checked{{/if}}>
+ <i class="fa fa-globe"></i> {{$public_title}}
+ </label>
+ <fieldset id="visibility-public-panel" class="panel-collapse collapse{{if $visibility == 'public'}} in{{/if}}" role="tabpanel" aria-labelledby="visibility-public-heading" {{if $visibility != 'public'}}disabled{{/if}}>
+ <div class="panel-body">
+ <p>{{$public_desc}}</p>
+ {{if $for_federation}}
+ {{if $user_hidewall}}
+ <h4>{{$jotnets_summary}}</h4>
+ {{$jotnets_disabled_label}}
+ {{elseif $jotnets_fields}}
+ {{if $jotnets_fields|count < 3}}
+ <div class="profile-jot-net">
+ {{else}}
+ <details class="profile-jot-net">
+ <summary>{{$jotnets_summary}}</summary>
+ {{/if}}
+
+ {{foreach $jotnets_fields as $jotnets_field}}
+ {{if $jotnets_field.type == 'checkbox'}}
+ {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
+ {{elseif $jotnets_field.type == 'select'}}
+ {{include file="field_select.tpl" field=$jotnets_field.field}}
+ {{/if}}
+ {{/foreach}}
+
+ {{if $jotnets_fields|count >= 3}}
+ </details>
+ {{else}}
+ </div>
+ {{/if}}
+ {{/if}}
+ {{/if}}
+ </div>
+ </fieldset>
+ </div>
+ <div class="panel panel-info">
+ <label class="panel-heading{{if $visibility != 'custom'}} collapsed{{/if}}" id="visibility-custom-heading" aria-expanded="{{if $visibility == 'custom'}}true{{else}}false{{/if}}">
+ <input type="radio" name="visibility" id="visibility-custom" value="custom" tabindex="15" {{if $visibility == 'custom'}}checked{{/if}}>
+ <i class="fa fa-lock"></i> {{$custom_title}}
+ </label>
+ <fieldset id="visibility-custom-panel" class="panel-collapse collapse{{if $visibility == 'custom'}} in{{/if}}" role="tabpanel" aria-labelledby="visibility-custom-heading" {{if $visibility != 'custom'}}disabled{{/if}}>
+ <input type="hidden" name="group_allow" value="{{$group_allow}}"/>
+ <input type="hidden" name="contact_allow" value="{{$contact_allow}}"/>
+ <input type="hidden" name="group_deny" value="{{$group_deny}}"/>
+ <input type="hidden" name="contact_deny" value="{{$contact_deny}}"/>
+ <div class="panel-body">
+ <p>{{$custom_desc}}</p>
+
+ <div class="form-group">
+ <label for="acl_allow">{{$allow_label}}</label>
+ <input type="text" class="form-control input-lg" id="acl_allow">
+ </div>
+
+ <div class="form-group">
+ <label for="acl_deny">{{$deny_label}}</label>
+ <input type="text" class="form-control input-lg" id="acl_deny">
+ </div>
+ </div>
+ </fieldset>
</div>
</div>
- <span id="acl-fields"></span>
-</div>
-<div class="acl-list-item" rel="acl-template" style="display:none">
- <img data-src="{0}"><p>{1}</p>
- <a class='acl-button-show'>{{$show}}</a>
- <a class='acl-button-hide'>{{$hide}}</a>
-</div>
-{{if $networks}}
-<hr style="clear:both"/>
-<div id="profile-jot-email-label">{{$emailcc}}</div><input type="text" name="emailcc" id="profile-jot-email" title="{{$emtitle}}" />
-<div id="profile-jot-email-end"></div>
-
- {{if $jotnets_fields}}
- {{if $jotnets_fields|count < 3}}
-<div class="profile-jot-net">
- {{else}}
-<details class="profile-jot-net">
- <summary>{{$jotnets_summary}}</summary>
- {{/if}}
-
- {{foreach $jotnets_fields as $jotnets_field}}
- {{if $jotnets_field.type == 'checkbox'}}
- {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
- {{elseif $jotnets_field.type == 'select'}}
- {{include file="field_select.tpl" field=$jotnets_field.field}}
- {{/if}}
- {{/foreach}}
-
- {{if $jotnets_fields|count >= 3}}
-</details>
- {{else}}
-</div>
- {{/if}}
- {{/if}}
+{{if $for_federation}}
+ <div class="form-group">
+ <label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
+ <input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
+ </div>
+ <div id="profile-jot-email-end"></div>
{{/if}}
+</div>
+<script type="text/javascript">
+ $(function() {
+ let $acl_allow_input = $('#acl_allow');
+ let $contact_allow_input = $('[name=contact_allow]');
+ let $group_allow_input = $('[name=group_allow]');
+ let $acl_deny_input = $('#acl_deny');
+ let $contact_deny_input = $('[name=contact_deny]');
+ let $group_deny_input = $('[name=group_deny]');
+ let $visibility_public_panel = $('#visibility-public-panel');
+ let $visibility_custom_panel = $('#visibility-custom-panel');
+ let $visibility_public_radio = $('#visibility-public');
+ let $visibility_custom_radio = $('#visibility-custom');
+
+ // Frio specific
+ if ($.fn.collapse) {
+ $visibility_public_panel.collapse({parent: '#visibility-accordion', toggle: false});
+ $visibility_custom_panel.collapse({parent: '#visibility-accordion', toggle: false});
+ }
+
+ $visibility_public_radio.on('change', function (e) {
+ if ($.fn.collapse) {
+ $visibility_public_panel.collapse('show');
+ }
+
+ $visibility_public_panel.prop('disabled', false);
+ $visibility_custom_panel.prop('disabled', true);
+
+ $('.profile-jot-net input[type=checkbox]').each(function() {
+ // Restores checkbox state if it had been saved
+ if ($(this).attr('data-checked') !== undefined) {
+ $(this).prop('checked', $(this).attr('data-checked') === 'true');
+ }
+ });
+ $('.profile-jot-net input').attr('disabled', false);
+ });
+
+ $visibility_custom_radio.on('change', function(e) {
+ if ($.fn.collapse) {
+ $visibility_custom_panel.collapse('show');
+ }
+
+ $visibility_public_panel.prop('disabled', true);
+ $visibility_custom_panel.prop('disabled', false);
+
+ $('.profile-jot-net input[type=checkbox]').each(function() {
+ // Saves current checkbox state
+ $(this)
+ .attr('data-checked', $(this).prop('checked'))
+ .prop('checked', false);
+ });
+ $('.profile-jot-net input').attr('disabled', 'disabled');
+ });
+
+ // Custom visibility tags inputs
+ let acl_groups = new Bloodhound({
+ local: {{$acl_groups|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ let acl_contacts = new Bloodhound({
+ local: {{$acl_contacts|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ let acl = new Bloodhound({
+ local: {{$acl_list|@json_encode nofilter}},
+ identify: function(obj) { return obj.id; },
+ datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
+ queryTokenizer: Bloodhound.tokenizers.whitespace,
+ });
+ acl.initialize();
+
+ let suggestionTemplate = function (item) {
+ return '<div><img src="' + item.micro + '" alt="" style="float: left; width: auto; height: 2.8em; margin-right: 0.5em;"> <strong>' + item.name + '</strong><br /><em>' + item.addr + '</em></div>';
+ };
+
+ $acl_allow_input.tagsinput({
+ confirmKeys: [13, 44],
+ freeInput: false,
+ tagClass: function(item) {
+ switch (item.type) {
+ case 'group' : return 'label label-primary';
+ case 'contact' :
+ default:
+ return 'label label-info';
+ }
+ },
+ itemValue: 'id',
+ itemText: 'name',
+ itemThumb: 'micro',
+ itemTitle: function(item) {
+ return item.addr;
+ },
+ typeaheadjs: {
+ name: 'contacts',
+ displayKey: 'name',
+ templates: {
+ suggestion: suggestionTemplate
+ },
+ source: acl.ttAdapter()
+ }
+ });
+
+ $acl_deny_input
+ .tagsinput({
+ confirmKeys: [13, 44],
+ freeInput: false,
+ tagClass: function(item) {
+ switch (item.type) {
+ case 'group' : return 'label label-primary';
+ case 'contact' :
+ default:
+ return 'label label-info';
+ }
+ },
+ itemValue: 'id',
+ itemText: 'name',
+ itemThumb: 'micro',
+ itemTitle: function(item) {
+ return item.addr;
+ },
+ typeaheadjs: {
+ name: 'contacts',
+ displayKey: 'name',
+ templates: {
+ suggestion: suggestionTemplate
+ },
+ source: acl.ttAdapter()
+ }
+ });
+
+ // Import existing ACL into the tags input fields.
+
+ $group_allow_input.val().split(',').forEach(function (val) {
+ $acl_allow_input.tagsinput('add', acl_groups.get(val)[0]);
+ });
+ $contact_allow_input.val().split(',').forEach(function (val) {
+ $acl_allow_input.tagsinput('add', acl_contacts.get(val)[0]);
+ });
+ $group_deny_input.val().split(',').forEach(function (val) {
+ $acl_deny_input.tagsinput('add', acl_groups.get(val)[0]);
+ });
+ $contact_deny_input.val().split(',').forEach(function (val) {
+ $acl_deny_input.tagsinput('add', acl_contacts.get(val)[0]);
+ });
+
+ // Anti-duplicate callback + acl fields value generation
+
+ $acl_allow_input.on('itemAdded', function (event) {
+ // Removes duplicate in the opposite acl box
+ $acl_deny_input.tagsinput('remove', event.item);
+
+ // Update the real acl field
+ $group_allow_input.val('');
+ $contact_allow_input.val('');
+ [].forEach.call($acl_allow_input.tagsinput('items'), function (item) {
+ if (item.type === 'group') {
+ $group_allow_input.val($group_allow_input.val() + ',' + item.id);
+ } else {
+ $contact_allow_input.val($contact_allow_input.val() + ',' + item.id);
+ }
+ });
+ });
+
+ $acl_deny_input.on('itemAdded', function (event) {
+ // Removes duplicate in the opposite acl box
+ $acl_allow_input.tagsinput('remove', event.item);
-<script>
-$(document).ready(function() {
- if(typeof acl=="undefined"){
- acl = new ACL(
- baseurl + '/search/acl',
- [ {{$allowcid nofilter}},{{$allowgid nofilter}},{{$denycid nofilter}},{{$denygid nofilter}} ],
- {{$features.aclautomention}},
- {{if $APP->is_mobile}}true{{else}}false{{/if}}
- );
- }
-});
+ // Update the real acl field
+ $group_deny_input.val('');
+ $contact_deny_input.val('');
+ [].forEach.call($acl_deny_input.tagsinput('items'), function (item) {
+ if (item.type === 'group') {
+ $group_deny_input.val($group_allow_input.val() + ',' + item.id);
+ } else {
+ $contact_deny_input.val($contact_allow_input.val() + ',' + item.id);
+ }
+ });
+ });
+ });
</script>
<script type="text/javascript" src="view/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
<script type="text/javascript" src="view/asset/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js" ></script>
<script type="text/javascript" src="view/asset/imagesloaded/imagesloaded.pkgd.min.js"></script>
-<script type="text/javascript" src="view/js/acl.js" ></script>
<script type="text/javascript" src="view/asset/base64/base64.min.js" ></script>
<script type="text/javascript" src="view/asset/dompurify/dist/purify.min.js"></script>
<script type="text/javascript" src="view/js/main.js" ></script>
+++ /dev/null
-<script type="text/javascript">
- function updateLocationButtonDisplay(location_button, location_input)
- {
- location_button.classList.remove('btn-primary');
- if (location_input.value) {
- location_button.disabled = false;
- location_button.classList.add('btn-primary');
- location_button.title = location_button.dataset.titleClear;
- } else if (!"geolocation" in navigator) {
- location_button.disabled = true;
- location_button.title = location_button.dataset.titleUnavailable;
- } else if (location_button.disabled) {
- location_button.title = location_button.dataset.titleDisabled;
- } else {
- location_button.title = location_button.dataset.titleSet;
- }
- }
-
- $(function() {
- // Jot attachment live preview.
- let $textarea = $('#comment-edit-text-0');
- $textarea.linkPreview();
- $textarea.keyup(function(){
- var textlen = $(this).val().length;
- $('#character-counter').text(textlen);
- });
- $textarea.editor_autocomplete(baseurl + '/search/acl');
- $textarea.bbco_autocomplete('bbcode');
-
- let $acl_allow_input = $('#acl_allow');
- let $group_allow_input = $('[name=group_allow]');
- let $contact_allow_input = $('[name=contact_allow]');
- let $acl_deny_input = $('#acl_deny');
- let $group_deny_input = $('[name=group_deny]');
- let $contact_deny_input = $('[name=contact_deny]');
-
- // Visibility accordion
-
- // Prevents open panel to collapse
- // @see https://stackoverflow.com/a/43593116
- $('[data-toggle="collapse"]').click(function(e) {
- target = $(this).attr('href');
- if ($(target).hasClass('in')) {
- e.preventDefault(); // to stop the page jump to the anchor target.
- e.stopPropagation()
- }
- });
- // Accessibility: enable space and enter to open a panel when focused
- $('body').on('keyup', '[data-toggle="collapse"]:focus', function (e) {
- if (e.key === ' ' || e.key === 'Enter') {
- $(this).click();
- e.preventDefault();
- e.stopPropagation();
- }
- });
-
- $('#visibility-public-panel').on('show.bs.collapse', function() {
- $('#visibility-public').prop('checked', true);
- $group_allow_input.prop('disabled', true);
- $contact_allow_input.prop('disabled', true);
- $group_deny_input.prop('disabled', true);
- $contact_deny_input.prop('disabled', true);
-
- $('.profile-jot-net input[type=checkbox]').each(function() {
- // Restores checkbox state if it had been saved
- if ($(this).attr('data-checked') !== undefined) {
- $(this).prop('checked', $(this).attr('data-checked') === 'true');
- }
- });
- $('.profile-jot-net input').attr('disabled', false);
- });
-
- $('#visibility-custom-panel').on('show.bs.collapse', function() {
- $('#visibility-custom').prop('checked', true);
- $group_allow_input.prop('disabled', false);
- $contact_allow_input.prop('disabled', false);
- $group_deny_input.prop('disabled', false);
- $contact_deny_input.prop('disabled', false);
-
- $('.profile-jot-net input[type=checkbox]').each(function() {
- // Saves current checkbox state
- $(this)
- .attr('data-checked', $(this).prop('checked'))
- .prop('checked', false);
- });
- $('.profile-jot-net input').attr('disabled', 'disabled');
- });
-
- if (document.querySelector('input[name="visibility"]:checked').value === 'custom') {
- $('#visibility-custom-panel').collapse({parent: '#visibility-accordion'});
- }
-
- // Custom visibility tags inputs
-
- let acl_groups = new Bloodhound({
- local: {{$acl_groups|@json_encode nofilter}},
- identify: function(obj) { return obj.id; },
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name']),
- queryTokenizer: Bloodhound.tokenizers.whitespace,
- });
- let acl_contacts = new Bloodhound({
- local: {{$acl_contacts|@json_encode nofilter}},
- identify: function(obj) { return obj.id; },
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
- queryTokenizer: Bloodhound.tokenizers.whitespace,
- });
- let acl = new Bloodhound({
- local: {{$acl|@json_encode nofilter}},
- identify: function(obj) { return obj.id; },
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace(['name', 'addr']),
- queryTokenizer: Bloodhound.tokenizers.whitespace,
- });
- acl.initialize();
-
- let suggestionTemplate = function (item) {
- return '<div><img src="' + item.micro + '" alt="" style="float: left; width: auto; height: 2.8em; margin-right: 0.5em;"> <strong>' + item.name + '</strong><br /><em>' + item.addr + '</em></div>';
- };
-
- $acl_allow_input.tagsinput({
- confirmKeys: [13, 44],
- freeInput: false,
- tagClass: function(item) {
- switch (item.type) {
- case 'group' : return 'label label-primary';
- case 'contact' :
- default:
- return 'label label-info';
- }
- },
- itemValue: 'id',
- itemText: 'name',
- itemThumb: 'micro',
- itemTitle: function(item) {
- return item.addr;
- },
- typeaheadjs: {
- name: 'contacts',
- displayKey: 'name',
- templates: {
- suggestion: suggestionTemplate
- },
- source: acl.ttAdapter()
- }
- });
-
- $acl_deny_input
- .tagsinput({
- confirmKeys: [13, 44],
- freeInput: false,
- tagClass: function(item) {
- switch (item.type) {
- case 'group' : return 'label label-primary';
- case 'contact' :
- default:
- return 'label label-info';
- }
- },
- itemValue: 'id',
- itemText: 'name',
- itemThumb: 'micro',
- itemTitle: function(item) {
- return item.addr;
- },
- typeaheadjs: {
- name: 'contacts',
- displayKey: 'name',
- templates: {
- suggestion: suggestionTemplate
- },
- source: acl.ttAdapter()
- }
- });
-
- // Import existing ACL into the tags input fields.
-
- $group_allow_input.val().split(',').forEach(function (val) {
- $acl_allow_input.tagsinput('add', acl_groups.get(val)[0]);
- });
- $contact_allow_input.val().split(',').forEach(function (val) {
- $acl_allow_input.tagsinput('add', acl_contacts.get(val)[0]);
- });
- $group_deny_input.val().split(',').forEach(function (val) {
- $acl_deny_input.tagsinput('add', acl_groups.get(val)[0]);
- });
- $contact_deny_input.val().split(',').forEach(function (val) {
- $acl_deny_input.tagsinput('add', acl_contacts.get(val)[0]);
- });
-
- // Anti-duplicate callback + acl fields value generation
-
- $acl_allow_input.on('itemAdded', function (event) {
- // Removes duplicate in the opposite acl box
- $acl_deny_input.tagsinput('remove', event.item);
-
- // Update the real acl field
- $group_allow_input.val('');
- $contact_allow_input.val('');
- [].forEach.call($acl_allow_input.tagsinput('items'), function (item) {
- if (item.type === 'group') {
- $group_allow_input.val($group_allow_input.val() + '<' + item.id + '>');
- } else {
- $contact_allow_input.val($contact_allow_input.val() + '<' + item.id + '>');
- }
- });
- });
-
- $acl_deny_input.on('itemAdded', function (event) {
- // Removes duplicate in the opposite acl box
- $acl_allow_input.tagsinput('remove', event.item);
-
- // Update the real acl field
- $group_deny_input.val('');
- $contact_deny_input.val('');
- [].forEach.call($acl_deny_input.tagsinput('items'), function (item) {
- if (item.type === 'group') {
- $group_deny_input.val($group_allow_input.val() + '<' + item.id + '>');
- } else {
- $contact_deny_input.val($contact_allow_input.val() + '<' + item.id + '>');
- }
- });
- });
-
- let location_button = document.getElementById('profile-location');
- let location_input = document.getElementById('jot-location');
-
- updateLocationButtonDisplay(location_button, location_input);
-
- location_input.addEventListener('change', function () {
- updateLocationButtonDisplay(location_button, location_input);
- });
- location_input.addEventListener('keyup', function () {
- updateLocationButtonDisplay(location_button, location_input);
- });
-
- location_button.addEventListener('click', function() {
- if (location_input.value) {
- location_input.value = '';
- updateLocationButtonDisplay(location_button, location_input);
- } else if ("geolocation" in navigator) {
- navigator.geolocation.getCurrentPosition(function(position) {
- location_input.value = position.coords.latitude + ', ' + position.coords.longitude;
- updateLocationButtonDisplay(location_button, location_input);
- }, function (error) {
- location_button.disabled = true;
- updateLocationButtonDisplay(location_button, location_input);
- });
- }
- });
- })
-</script>
<div id="comment-edit-preview-{{$id}}" class="comment-edit-preview" style="display:none;"></div>
- <input type="hidden" name="group_allow" value="{{$group_allow}}" {{if $visibility == 'public'}}disabled{{/if}}/>
- <input type="hidden" name="contact_allow" value="{{$contact_allow}}" {{if $visibility == 'public'}}disabled{{/if}}/>
- <input type="hidden" name="group_deny" value="{{$group_deny}}" {{if $visibility == 'public'}}disabled{{/if}}/>
- <input type="hidden" name="contact_deny" value="{{$contact_deny}}" {{if $visibility == 'public'}}disabled{{/if}}/>
{{if $type == 'post'}}
- <h3>Visibility</h3>
- <div class="panel-group" id="visibility-accordion" role="tablist" aria-multiselectable="true">
- <div class="panel panel-success">
- <div class="panel-heading" id="visibility-public-heading" role="button" data-toggle="collapse" data-parent="#visibility-accordion" href="#visibility-public-panel" aria-expanded="true" aria-controls="visibility-public-panel" tabindex="14">
- <label>
- <input type="radio" name="visibility" id="visibility-public" value="public" {{if $visibility == 'public'}}checked{{/if}} style="display:none">
- <i class="fa fa-globe"></i> {{$public_title}}
- </label>
- </div>
- <div id="visibility-public-panel" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="visibility-public-heading">
- <div class="panel-body">
- <p>{{$public_desc}}</p>
- {{if $doesFederate && $jotnets_fields}}
- {{if $jotnets_fields|count < 3}}
- <div class="profile-jot-net">
- {{else}}
- <details class="profile-jot-net">
- <summary>{{$jotnets_summary}}</summary>
- {{/if}}
+ <h3>{{$visibility_title}}</h3>
+ {{$acl_selector nofilter}}
- {{foreach $jotnets_fields as $jotnets_field}}
- {{if $jotnets_field.type == 'checkbox'}}
- {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
- {{elseif $jotnets_field.type == 'select'}}
- {{include file="field_select.tpl" field=$jotnets_field.field}}
- {{/if}}
- {{/foreach}}
-
- {{if $jotnets_fields|count >= 3}}
- </details>
- {{else}}
- </div>
- {{/if}}
- {{/if}}
- </div>
- </div>
- </div>
- <div class="panel panel-info">
- <div class="panel-heading collapsed" id="visibility-custom-heading" role="button" data-toggle="collapse" data-parent="#visibility-accordion" href="#visibility-custom-panel" aria-expanded="true" aria-controls="visibility-custom-panel" tabindex="15">
- <label>
- <input type="radio" name="visibility" id="visibility-custom" value="custom" {{if $visibility == 'custom'}}checked{{/if}} style="display:none">
- <i class="fa fa-lock"></i> {{$custom_title}}
- </label>
- </div>
- <div id="visibility-custom-panel" class="panel-collapse collapse" role="tabpanel" aria-labelledby="visibility-custom-heading">
- <div class="panel-body">
- <p>{{$custom_desc}}</p>
-
- <div class="form-group">
- <label for="acl_allow">Deliver to:</label>
- <input type="text" class="form-control input-lg" id="acl_allow">
- </div>
-
- <div class="form-group">
- <label for="acl_deny">Except to:</label>
- <input type="text" class="form-control input-lg" id="acl_deny">
- </div>
- </div>
- </div>
- </div>
- </div>
- {{if $doesFederate}}
- <div class="form-group">
- <label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
- <input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
- </div>
- <div id="profile-jot-email-end"></div>
- {{/if}}
<div class="jotplugins">
{{$jotplugins nofilter}}
</div>
+{{else}}
+ <input type="hidden" name="group_allow" value="{{$group_allow}}"/>
+ <input type="hidden" name="contact_allow" value="{{$contact_allow}}"/>
+ <input type="hidden" name="group_deny" value="{{$group_deny}}"/>
+ <input type="hidden" name="contact_deny" value="{{$contact_deny}}"/>
{{/if}}
</form>
</div>
border: 1px solid #cccccc;
}
-.acl-list-item p, #profile-jot-email-label, div#jot-preview-content, div.profile-jot-net {
+#profile-jot-email-label, div#jot-preview-content, div.profile-jot-net {
color: #eec;
}
#fancybox-content{
background:#444;
}
-input#acl-search {
- background-color: #aaa;
-}
-
-
.notify-seen {
background:#111;
}
margin-bottom: 15px;
}
-
#acl-wrapper {
width: 690px;
float:left;
}
-#acl-search {
- float:right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right:20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0px 30px;
- -webkit-border-radius: 5px ;
- -moz-border-radius: 5px;
- border-radius: 5px;
- color: #999999;
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-#acl-list-content {
-
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img{
- width:22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p { height: 12px; font-size: 10px; margin: 0px; padding: 2px 0px 1px; overflow: hidden;}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color:#000000;
}
-.acl-button-show { background-image: url("../../../images/show_off.png"); }
-.acl-button-hide { background-image: url("../../../images/hide_off.png"); }
-
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow { border-color: #9ade00; }
-.acl-list-item.grouphide { border-color: #ff4141; }
/** /acl **/
overflow-y: overlay;
}
/* ACL */
-/*#jot-modal-body {
- height: auto;
- max-height: calc(100vh - 130px);
- overflow-y: hidden;
-}*/
-#acl-search {
- /*margin-top: 20px;*/
- /*padding: 8px;*/
- /*border: 1px solid #ccc;*/
- width: 100%;
-}
-#acl-list {
- display: block;
- border: 1px solid #ccc;
- clear: both;
- min-height: 62px;
- margin-top: 20px;
- padding: 10px 10px 0px 0px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
- overflow-y: auto;
-}
-#acl-list-content {
- overflow-y: hidden;
- height: auto !important;
-}
-.acl-list-item {
- width: 48%;
- width: calc(50% - 10px);
- border: 1px solid #ccc;
- margin: 0px 0px 10px 10px;
- padding: 5px;
- float: left;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-.acl-list-item img {
- width: 40px;
- height: 40px;
- float: left;
- margin-right: 5px;
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-.acl-list-item p {
- margin: 0px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-.acl-list-item.groupshow {
- background-color: #8DB255
-}
-.acl-list-item.grouphide {
- background-color: #E68364;
-}
-.acl-button-show, .acl-button-hide {
- float: right;
- margin-left: 5px;
-}
-#acl-showall.selected {
- background-color: #4CAF50;
- color: #fff;
-}
-.acl-button-show.selected {
- background-color: #4CAF50;
- color: #fff;
-}
-.acl-button-hide.selected {
- background-color: #F44336;
- color: #fff;
-}
-
.fa.lock:before {
font-family: ForkAwesome;
content: "\f023";
content: "\f09c";
}
+#acl-wrapper label.panel-heading {
+ display: block;
+ margin-bottom: 0;
+ cursor: pointer;
+}
+
/* Filebrowser */
.fbrowser .breadcrumb {
margin-bottom: 0px;
.section-subtitle-wrapper {
padding: 1px 10px;
}
-.panel .section-subtitle-wrapper a.accordion-toggle:before {
+details.profile-jot-net[open] summary:before, .panel .section-subtitle-wrapper a.accordion-toggle:before {
font-family: ForkAwesome;
content: "\f0d7";
padding-right: 5px;
}
-.panel .section-subtitle-wrapper a.accordion-toggle.collapsed:before {
+details.profile-jot-net summary:before, .panel .section-subtitle-wrapper a.accordion-toggle.collapsed:before {
font-family: ForkAwesome;
content: "\f0da";
}
+details.profile-jot-net summary:before {
+ padding-right: 5px;
+ padding-left: 3px;
+}
+details.profile-jot-net[open] summary:before {
+ padding-right: 5px;
+ padding-left: 0px;
+}
#settings-nick-wrapper {
margin-bottom: 20px;
}
+++ /dev/null
-The MIT License (MIT)
-
-Copyright (c) 2013 Tim Schlechter
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- *
- */
-
-.twitter-typeahead .tt-query,
-.twitter-typeahead .tt-hint {
- margin-bottom: 0;
-}
-
-.twitter-typeahead .tt-hint
-{
- display: none;
-}
-
-.tt-menu {
- position: absolute;
- top: 100%;
- left: 0;
- z-index: 1000;
- display: none;
- float: left;
- min-width: 160px;
- padding: 5px 0;
- margin: 2px 0 0;
- list-style: none;
- font-size: 14px;
- background-color: #ffffff;
- border: 1px solid #cccccc;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 4px;
- -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- background-clip: padding-box;
- cursor: pointer;
-}
-
-.tt-suggestion {
- display: block;
- padding: 3px 20px;
- clear: both;
- font-weight: normal;
- line-height: 1.428571429;
- color: #333333;
- white-space: nowrap;
-}
-
-.tt-suggestion:hover,
-.tt-suggestion:focus {
- color: #ffffff;
- text-decoration: none;
- outline: 0;
- background-color: #428bca;
-}
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- *
- */
-
-.friendica-tagsinput {
- background-color: #fff;
- border: 1px solid #ccc;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- display: inline-block;
- padding: 4px 6px;
- color: #555;
- vertical-align: middle;
- border-radius: 4px;
- max-width: 100%;
- line-height: 22px;
- cursor: text;
- height: auto;
-}
-.friendica-tagsinput.input-lg {
- line-height: 27px;
-}
-.friendica-tagsinput input {
- border: none;
- box-shadow: none;
- outline: none;
- background-color: transparent;
- padding: 0 6px;
- margin: 0;
- width: auto;
- max-width: inherit;
-}
-.friendica-tagsinput.form-control input::-moz-placeholder {
- color: #777;
- opacity: 1;
-}
-.friendica-tagsinput.form-control input:-ms-input-placeholder {
- color: #777;
-}
-.friendica-tagsinput.form-control input::-webkit-input-placeholder {
- color: #777;
-}
-.friendica-tagsinput input:focus {
- border: none;
- box-shadow: none;
-}
-.friendica-tagsinput .tag {
- margin: 0 2px 2px 0;
- color: white;
- font-weight: normal;
-}
-.friendica-tagsinput .tag img {
- width: auto;
- height: 1.5em;
- vertical-align: text-top;
- margin-right: 8px;
-}
-.friendica-tagsinput .tag [data-role="remove"] {
- margin-left: 8px;
- cursor: pointer;
-}
-.friendica-tagsinput .tag [data-role="remove"]:after {
- content: "x";
- padding: 0px 2px;
- font-weight: bold;
-}
-.friendica-tagsinput .tag [data-role="remove"]:hover {
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
-}
-.friendica-tagsinput .tag [data-role="remove"]:hover:active {
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
-}
+++ /dev/null
-/*
- * friendica-tagsinput v0.8.0
- * Based on bootstrap-tagsinput v0.8.0
- *
- * Adds:
- * - optional thumbnail
- * - copying source input element class to the pseudo-input element
- *
- */
-
-(function ($) {
- "use strict";
-
- var defaultOptions = {
- tagClass: function(item) {
- return 'label label-info';
- },
- focusClass: 'focus',
- itemValue: function(item) {
- return item ? item.toString() : item;
- },
- itemText: function(item) {
- return this.itemValue(item);
- },
- itemTitle: function(item) {
- return null;
- },
- itemThumb: function(item) {
- return null;
- },
- freeInput: true,
- addOnBlur: true,
- maxTags: undefined,
- maxChars: undefined,
- confirmKeys: [13, 44],
- delimiter: ',',
- delimiterRegex: null,
- cancelConfirmKeysOnEmpty: false,
- onTagExists: function(item, $tag) {
- $tag.hide().fadeIn();
- },
- trimValue: false,
- allowDuplicates: false,
- triggerChange: true
- };
-
- /**
- * Constructor function
- */
- function TagsInput(element, options) {
- this.isInit = true;
- this.itemsArray = [];
-
- this.$element = $(element);
- this.$element.hide();
-
- this.isSelect = (element.tagName === 'SELECT');
- this.multiple = (this.isSelect && element.hasAttribute('multiple'));
- this.objectItems = options && options.itemValue;
- this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
- this.inputSize = Math.max(1, this.placeholderText.length);
-
- this.$container = $('<div class="friendica-tagsinput"></div>');
- this.$container.addClass(this.$element.attr('class'));
- this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
-
- this.$element.before(this.$container);
-
- this.build(options);
- this.isInit = false;
- }
-
- TagsInput.prototype = {
- constructor: TagsInput,
-
- /**
- * Adds the given item as a new tag. Pass true to dontPushVal to prevent
- * updating the elements val()
- */
- add: function(item, dontPushVal, options) {
- let self = this;
-
- if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
- return;
-
- // Ignore falsey values, except false
- if (item !== false && !item)
- return;
-
- // Trim value
- if (typeof item === "string" && self.options.trimValue) {
- item = $.trim(item);
- }
-
- // Throw an error when trying to add an object while the itemValue option was not set
- if (typeof item === "object" && !self.objectItems)
- throw("Can't add objects when itemValue option is not set");
-
- // Ignore strings only containg whitespace
- if (item.toString().match(/^\s*$/))
- return;
-
- // If SELECT but not multiple, remove current tag
- if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
- self.remove(self.itemsArray[0]);
-
- if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
- var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
- var items = item.split(delimiter);
- if (items.length > 1) {
- for (var i = 0; i < items.length; i++) {
- this.add(items[i], true);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
- return;
- }
- }
-
- var itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item),
- itemTitle = self.options.itemTitle(item),
- itemThumb = self.options.itemThumb(item);
-
- // Ignore items allready added
- var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
- if (existing && !self.options.allowDuplicates) {
- // Invoke onTagExists
- if (self.options.onTagExists) {
- var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
- self.options.onTagExists(item, $existingTag);
- }
- return;
- }
-
- // if length greater than limit
- if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
- return;
-
- // raise beforeItemAdd arg
- var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
- self.$element.trigger(beforeItemAddEvent);
- if (beforeItemAddEvent.cancel)
- return;
-
- // register item in internal array and map
- self.itemsArray.push(item);
-
- // add a tag element
- var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' +
- (itemThumb !== null ? '<img src="' + itemThumb + '" alt="">' : '') +
- htmlEncode(itemText) + '<span data-role="remove"></span>' +
- '</span>');
- $tag.data('item', item);
- self.findInputWrapper().before($tag);
- $tag.after(' ');
-
- // Check to see if the tag exists in its raw or uri-encoded form
- var optionExists = (
- $('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element).length ||
- $('option[value="' + htmlEncode(itemValue) + '"]', self.$element).length
- );
-
- // add <option /> if item represents a value not present in one of the <select />'s options
- if (self.isSelect && !optionExists) {
- var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
- $option.data('item', item);
- $option.attr('value', itemValue);
- self.$element.append($option);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
-
- // Add class when reached maxTags
- if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
- self.$container.addClass('friendica-tagsinput-max');
-
- // If using typeahead, once the tag has been added, clear the typeahead value so it does not stick around in the input.
- if ($('.typeahead, .twitter-typeahead', self.$container).length) {
- self.$input.typeahead('val', '');
- }
-
- if (this.isInit) {
- self.$element.trigger($.Event('itemAddedOnInit', { item: item, options: options }));
- } else {
- self.$element.trigger($.Event('itemAdded', { item: item, options: options }));
- }
- },
-
- /**
- * Removes the given item. Pass true to dontPushVal to prevent updating the
- * elements val()
- */
- remove: function(item, dontPushVal, options) {
- var self = this;
-
- if (self.objectItems) {
- if (typeof item === "object")
- item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
- else
- item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
-
- item = item[item.length-1];
- }
-
- if (item) {
- var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options });
- self.$element.trigger(beforeItemRemoveEvent);
- if (beforeItemRemoveEvent.cancel)
- return;
-
- $('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
- $('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
- if($.inArray(item, self.itemsArray) !== -1)
- self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
- }
-
- if (!dontPushVal)
- self.pushVal(self.options.triggerChange);
-
- // Remove class when reached maxTags
- if (self.options.maxTags > self.itemsArray.length)
- self.$container.removeClass('friendica-tagsinput-max');
-
- self.$element.trigger($.Event('itemRemoved', { item: item, options: options }));
- },
-
- /**
- * Removes all items
- */
- removeAll: function() {
- var self = this;
-
- $('.tag', self.$container).remove();
- $('option', self.$element).remove();
-
- while(self.itemsArray.length > 0)
- self.itemsArray.pop();
-
- self.pushVal(self.options.triggerChange);
- },
-
- /**
- * Refreshes the tags so they match the text/value of their corresponding
- * item.
- */
- refresh: function() {
- var self = this;
- $('.tag', self.$container).each(function() {
- var $tag = $(this),
- item = $tag.data('item'),
- itemValue = self.options.itemValue(item),
- itemText = self.options.itemText(item),
- tagClass = self.options.tagClass(item);
-
- // Update tag's class and inner text
- $tag.attr('class', null);
- $tag.addClass('tag ' + htmlEncode(tagClass));
- $tag.contents().filter(function() {
- return this.nodeType == 3;
- })[0].nodeValue = htmlEncode(itemText);
-
- if (self.isSelect) {
- var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
- option.attr('value', itemValue);
- }
- });
- },
-
- /**
- * Returns the items added as tags
- */
- items: function() {
- return this.itemsArray;
- },
-
- /**
- * Assembly value by retrieving the value of each item, and set it on the
- * element.
- */
- pushVal: function() {
- var self = this,
- val = $.map(self.items(), function(item) {
- return self.options.itemValue(item).toString();
- });
-
- self.$element.val(val, true);
-
- if (self.options.triggerChange)
- self.$element.trigger('change');
- },
-
- /**
- * Initializes the tags input behaviour on the element
- */
- build: function(options) {
- var self = this;
-
- self.options = $.extend({}, defaultOptions, options);
- // When itemValue is set, freeInput should always be false
- if (self.objectItems)
- self.options.freeInput = false;
-
- makeOptionItemFunction(self.options, 'itemValue');
- makeOptionItemFunction(self.options, 'itemText');
- makeOptionItemFunction(self.options, 'itemThumb');
- makeOptionFunction(self.options, 'tagClass');
-
- // Typeahead Bootstrap version 2.3.2
- if (self.options.typeahead) {
- var typeahead = self.options.typeahead || {};
-
- makeOptionFunction(typeahead, 'source');
-
- self.$input.typeahead($.extend({}, typeahead, {
- source: function (query, process) {
- function processItems(items) {
- var texts = [];
-
- for (var i = 0; i < items.length; i++) {
- var text = self.options.itemText(items[i]);
- map[text] = items[i];
- texts.push(text);
- }
- process(texts);
- }
-
- this.map = {};
- var map = this.map,
- data = typeahead.source(query);
-
- if ($.isFunction(data.success)) {
- // support for Angular callbacks
- data.success(processItems);
- } else if ($.isFunction(data.then)) {
- // support for Angular promises
- data.then(processItems);
- } else {
- // support for functions and jquery promises
- $.when(data)
- .then(processItems);
- }
- },
- updater: function (text) {
- self.add(this.map[text]);
- return this.map[text];
- },
- matcher: function (text) {
- return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
- },
- sorter: function (texts) {
- return texts.sort();
- },
- highlighter: function (text) {
- var regex = new RegExp( '(' + this.query + ')', 'gi' );
- return text.replace( regex, "<strong>$1</strong>" );
- }
- }));
- }
-
- // typeahead.js
- if (self.options.typeaheadjs) {
- var typeaheadConfig = null;
- var typeaheadDatasets = {};
-
- // Determine if main configurations were passed or simply a dataset
- var typeaheadjs = self.options.typeaheadjs;
- if ($.isArray(typeaheadjs)) {
- typeaheadConfig = typeaheadjs[0];
- typeaheadDatasets = typeaheadjs[1];
- } else {
- typeaheadDatasets = typeaheadjs;
- }
-
- self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function (obj, datum) {
- if (typeaheadDatasets.valueKey)
- self.add(datum[typeaheadDatasets.valueKey]);
- else
- self.add(datum);
- self.$input.typeahead('val', '');
- }, self));
- }
-
- self.$container.on('click', $.proxy(function(event) {
- if (! self.$element.attr('disabled')) {
- self.$input.removeAttr('disabled');
- }
- self.$input.focus();
- }, self));
-
- if (self.options.addOnBlur && self.options.freeInput) {
- self.$input.on('focusout', $.proxy(function(event) {
- // HACK: only process on focusout when no typeahead opened, to
- // avoid adding the typeahead text as tag
- if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
- self.add(self.$input.val());
- self.$input.val('');
- }
- }, self));
- }
-
- // Toggle the 'focus' css class on the container when it has focus
- self.$container.on({
- focusin: function() {
- self.$container.addClass(self.options.focusClass);
- },
- focusout: function() {
- self.$container.removeClass(self.options.focusClass);
- },
- });
-
- self.$container.on('keydown', 'input', $.proxy(function(event) {
- var $input = $(event.target),
- $inputWrapper = self.findInputWrapper();
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- switch (event.which) {
- // BACKSPACE
- case 8:
- if (doGetCaretPosition($input[0]) === 0) {
- var prev = $inputWrapper.prev();
- if (prev.length) {
- self.remove(prev.data('item'));
- }
- }
- break;
-
- // DELETE
- case 46:
- if (doGetCaretPosition($input[0]) === 0) {
- var next = $inputWrapper.next();
- if (next.length) {
- self.remove(next.data('item'));
- }
- }
- break;
-
- // LEFT ARROW
- case 37:
- // Try to move the input before the previous tag
- var $prevTag = $inputWrapper.prev();
- if ($input.val().length === 0 && $prevTag[0]) {
- $prevTag.before($inputWrapper);
- $input.focus();
- }
- break;
- // RIGHT ARROW
- case 39:
- // Try to move the input after the next tag
- var $nextTag = $inputWrapper.next();
- if ($input.val().length === 0 && $nextTag[0]) {
- $nextTag.after($inputWrapper);
- $input.focus();
- }
- break;
- default:
- // ignore
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- self.$container.on('keypress', 'input', $.proxy(function(event) {
- var $input = $(event.target);
-
- if (self.$element.attr('disabled')) {
- self.$input.attr('disabled', 'disabled');
- return;
- }
-
- var text = $input.val(),
- maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
- if (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached) {
- // Only attempt to add a tag if there is data in the field
- if (self.options.freeInput && text.length !== 0) {
- self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
- $input.val('');
- }
-
- // If the field is empty, let the event triggered fire as usual
- if (self.options.cancelConfirmKeysOnEmpty === false) {
- event.preventDefault();
- }
- }
-
- // Reset internal input's size
- var textLength = $input.val().length,
- wordSpace = Math.ceil(textLength / 5),
- size = textLength + wordSpace + 1;
- $input.attr('size', Math.max(this.inputSize, $input.val().length));
- }, self));
-
- // Remove icon clicked
- self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
- if (self.$element.attr('disabled')) {
- return;
- }
- self.remove($(event.target).closest('.tag').data('item'));
- }, self));
-
- // Only add existing value as tags when using strings as tags
- if (self.options.itemValue === defaultOptions.itemValue) {
- if (self.$element[0].tagName === 'INPUT') {
- self.add(self.$element.val());
- } else {
- $('option', self.$element).each(function() {
- self.add($(this).attr('value'), true);
- });
- }
- }
- },
-
- /**
- * Removes all tagsinput behaviour and unregsiter all event handlers
- */
- destroy: function() {
- var self = this;
-
- // Unbind events
- self.$container.off('keypress', 'input');
- self.$container.off('click', '[role=remove]');
-
- self.$container.remove();
- self.$element.removeData('tagsinput');
- self.$element.show();
- },
-
- /**
- * Sets focus on the tagsinput
- */
- focus: function() {
- this.$input.focus();
- },
-
- /**
- * Returns the internal input element
- */
- input: function() {
- return this.$input;
- },
-
- /**
- * Returns the element which is wrapped around the internal input. This
- * is normally the $container, but typeahead.js moves the $input element.
- */
- findInputWrapper: function() {
- var elt = this.$input[0],
- container = this.$container[0];
- while(elt && elt.parentNode !== container)
- elt = elt.parentNode;
-
- return $(elt);
- }
- };
-
- /**
- * Register JQuery plugin
- */
- $.fn.tagsinput = function(arg1, arg2, arg3) {
- var results = [];
-
- this.each(function() {
- var tagsinput = $(this).data('tagsinput');
- // Initialize a new tags input
- if (!tagsinput) {
- tagsinput = new TagsInput(this, arg1);
- $(this).data('tagsinput', tagsinput);
- results.push(tagsinput);
-
- if (this.tagName === 'SELECT') {
- $('option', $(this)).attr('selected', 'selected');
- }
-
- // Init tags from $(this).val()
- $(this).val($(this).val());
- } else if (!arg1 && !arg2) {
- // tagsinput already exists
- // no function, trying to init
- results.push(tagsinput);
- } else if(tagsinput[arg1] !== undefined) {
- // Invoke function on existing tags input
- if(tagsinput[arg1].length === 3 && arg3 !== undefined){
- var retVal = tagsinput[arg1](arg2, null, arg3);
- }else{
- var retVal = tagsinput[arg1](arg2);
- }
- if (retVal !== undefined)
- results.push(retVal);
- }
- });
-
- if ( typeof arg1 == 'string') {
- // Return the results from the invoked function calls
- return results.length > 1 ? results : results[0];
- } else {
- return results;
- }
- };
-
- $.fn.tagsinput.Constructor = TagsInput;
-
- /**
- * Most options support both a string or number as well as a function as
- * option value. This function makes sure that the option with the given
- * key in the given options is wrapped in a function
- */
- function makeOptionItemFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var propertyName = options[key];
- options[key] = function(item) { return item[propertyName]; };
- }
- }
- function makeOptionFunction(options, key) {
- if (typeof options[key] !== 'function') {
- var value = options[key];
- options[key] = function() { return value; };
- }
- }
- /**
- * HtmlEncodes the given value
- */
- var htmlEncodeContainer = $('<div />');
- function htmlEncode(value) {
- if (value) {
- return htmlEncodeContainer.text(value).html();
- } else {
- return '';
- }
- }
-
- /**
- * Returns the position of the caret in the given input field
- * http://flightschool.acylt.com/devnotes/caret-position-woes/
- */
- function doGetCaretPosition(oField) {
- var iCaretPos = 0;
- if (document.selection) {
- oField.focus ();
- var oSel = document.selection.createRange();
- oSel.moveStart ('character', -oField.value.length);
- iCaretPos = oSel.text.length;
- } else if (oField.selectionStart || oField.selectionStart == '0') {
- iCaretPos = oField.selectionStart;
- }
- return (iCaretPos);
- }
-
- /**
- * Returns boolean indicates whether user has pressed an expected key combination.
- * @param object keyPressEvent: JavaScript event object, refer
- * http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
- * @param object lookupList: expected key combinations, as in:
- * [13, {which: 188, shiftKey: true}]
- */
- function keyCombinationInList(keyPressEvent, lookupList) {
- var found = false;
- $.each(lookupList, function (index, keyCombination) {
- if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
- found = true;
- return false;
- }
-
- if (keyPressEvent.which === keyCombination.which) {
- var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
- shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
- ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
- if (alt && shift && ctrl) {
- found = true;
- return false;
- }
- }
- });
-
- return found;
- }
-
- /**
- * Initialize tagsinput behaviour on inputs and selects which have
- * data-role=tagsinput
- */
- $(function() {
- $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
- });
-})(window.jQuery);
--- /dev/null
+$(function() {
+ // Jot attachment live preview.
+ let $textarea = $('textarea[name=body]');
+ $textarea.linkPreview();
+ $textarea.keyup(function(){
+ var textlen = $(this).val().length;
+ $('#character-counter').text(textlen);
+ });
+ $textarea.editor_autocomplete(baseurl + '/search/acl');
+ $textarea.bbco_autocomplete('bbcode');
+
+ let location_button = document.getElementById('profile-location');
+ let location_input = document.getElementById('jot-location');
+
+ if (location_button && location_input) {
+ updateLocationButtonDisplay(location_button, location_input);
+
+ location_input.addEventListener('change', function () {
+ updateLocationButtonDisplay(location_button, location_input);
+ });
+ location_input.addEventListener('keyup', function () {
+ updateLocationButtonDisplay(location_button, location_input);
+ });
+
+ location_button.addEventListener('click', function() {
+ if (location_input.value) {
+ location_input.value = '';
+ updateLocationButtonDisplay(location_button, location_input);
+ } else if ("geolocation" in navigator) {
+ navigator.geolocation.getCurrentPosition(function(position) {
+ location_input.value = position.coords.latitude + ', ' + position.coords.longitude;
+ updateLocationButtonDisplay(location_button, location_input);
+ }, function (error) {
+ location_button.disabled = true;
+ updateLocationButtonDisplay(location_button, location_input);
+ });
+ }
+ });
+ }
+});
+
+function updateLocationButtonDisplay(location_button, location_input)
+{
+ location_button.classList.remove('btn-primary');
+ if (location_input.value) {
+ location_button.disabled = false;
+ location_button.classList.add('btn-primary');
+ location_button.title = location_button.dataset.titleClear;
+ } else if (!"geolocation" in navigator) {
+ location_button.disabled = true;
+ location_button.title = location_button.dataset.titleUnavailable;
+ } else if (location_button.disabled) {
+ location_button.title = location_button.dataset.titleDisabled;
+ } else {
+ location_button.title = location_button.dataset.titleSet;
+ }
+}
$("#event-preview").empty();
e.preventDefault();
});
-
- // Construct a new ACL. We need this everytime the 'event-edit-form' is loaded
- // without page reloading (e.g. closing an old modal and open a new modal).
- // Otherwise we wouldn't get the ACL data.
- /// @todo: Try to implement some kind of ACL reloading in acl.js.
-
- var eventPerms = document.getElementById('event-edit-form');
-
- acl = new ACL(
- baseurl + '/search/acl',
- [
- JSON.parse(eventPerms.dataset.allow_cid),
- JSON.parse(eventPerms.dataset.allow_gid),
- JSON.parse(eventPerms.dataset.deny_cid),
- JSON.parse(eventPerms.dataset.deny_gid)
- ]
- );
- acl.get(0, 100);
});
// Load the html of the actual event and incect the output to the
// Init the filebrowser after page load.
Dialog._load = function(url) {
// Get nickname & filebrowser type from the modal content.
- var nickname = $("#fb-nickname").attr("value");
- var type = $("#fb-type").attr("value");
+ let filebrowser = document.getElementById('filebrowser');
// Try to fetch the hash form the url.
- var match = url.match(/fbrowser\/[a-z]+\/.*(#.*)/);
- if (match===null) return; //not fbrowser
- var hash = match[1];
+ let match = url.match(/fbrowser\/[a-z]+\/.*(#.*)/);
+ if (!filebrowser || match === null) {
+ return; //not fbrowser
+ }
// Initialize the filebrowser.
- var jsbrowser = function() {
- FileBrowser.init(nickname, type, hash);
- };
loadScript("view/js/ajaxupload.js");
- loadScript("view/theme/frio/js/filebrowser.js", jsbrowser);
+ loadScript("view/theme/frio/js/filebrowser.js", function() {
+ FileBrowser.init(filebrowser.dataset.nickname, filebrowser.dataset.type, match[1]);
+ });
};
/**
// Minimize all tab content wrapper and activate only the selected
// tab panel.
- $('#jot-modal [role=tabpanel]').addClass("minimize").attr("aria-hidden" ,"true");
- $('#jot-modal #' + tabpanel).removeClass("minimize").attr("aria-hidden" ,"false");
+ $('#profile-jot-form > [role=tabpanel]').addClass("minimize").attr("aria-hidden" ,"true");
+ $('#' + tabpanel).removeClass("minimize").attr("aria-hidden" ,"false");
// Set the aria-selected states
- $("#jot-modal .nav-tabs .jot-nav-lnk").attr("aria-selected", "false");
+ $("#jot-modal .modal-header .nav-tabs .jot-nav-lnk").attr("aria-selected", "false");
elm.setAttribute("aria-selected", "true");
// For some some tab panels we need to execute other js functions.
});
</script>
<script src="<?=$frio?>/js/theme.js"></script>
-<script src="<?=$frio?>/js/acl.js"></script>
<script src="<?=$frio?>/frameworks/bootstrap/js/bootstrap.min.js"></script>
<script src="<?=$frio?>/frameworks/jasny/js/jasny-bootstrap.min.js"></script>
<script src="<?=$frio?>/frameworks/bootstrap-select/js/bootstrap-select.min.js"></script>
text-decoration-color: black;
text-decoration-style: wavy;
}
+
+/* New compose popup */
+.profile-jot-net {
+ background: #dff0d8;
+}
+
+.profile-jot-net summary {
+ color: #3c763d;
+ font-weight: bold;
+}
+++ /dev/null
-
-<div id="acl-wrapper">
- <div class="form-group form-group-search">
- <button id="acl-showall" class="btn btn-block btn-default"><i class="fa fa-globe"></i> {{$showall}}</button>
- </div>
- <div class="form-group form-group-search">
- <input type="text" id="acl-search" class="form-control form-search" autocomplete="off">
- </div>
- <div id="acl-list">
- <div id="acl-list-content"></div>
- </div>
- <span id="acl-fields"></span>
-</div>
-
-<div class="acl-list-item" rel="acl-template" style="display:none">
- <img data-src="{0}" alt="{1}"><p>{1}</p>
- <button class='acl-button-hide btn btn-sm btn-default'>{{$hide}}</button>
- <button class='acl-button-show btn btn-sm btn-default'>{{$show}}</button>
-</div>
-
-{{if $networks}}
-<hr style="clear:both"/>
-<div class="form-group">
- <label for="profile-jot-email" id="profile-jot-email-label">{{$emailcc}}</label>
- <input type="text" name="emailcc" id="profile-jot-email" class="form-control" title="{{$emtitle}}" />
-</div>
-<div id="profile-jot-email-end"></div>
-
- {{if $jotnets_fields}}
- {{if $jotnets_fields|count < 3}}
-<div class="profile-jot-net">
- {{else}}
-<details class="profile-jot-net">
- <summary>{{$jotnets_summary}}</summary>
- {{/if}}
-
- {{foreach $jotnets_fields as $jotnets_field}}
- {{if $jotnets_field.type == 'checkbox'}}
- {{include file="field_checkbox.tpl" field=$jotnets_field.field}}
- {{elseif $jotnets_field.type == 'select'}}
- {{include file="field_select.tpl" field=$jotnets_field.field}}
- {{/if}}
- {{/foreach}}
-
- {{if $jotnets_fields|count >= 3}}
-</details>
- {{else}}
-</div>
- {{/if}}
- {{/if}}
-{{/if}}
-
-<script type="text/javascript">
-$(document).ready(function() {
- if(typeof acl=="undefined"){
- acl = new ACL(
- baseurl + '/search/acl',
- [ {{$allowcid nofilter}},{{$allowgid nofilter}},{{$denycid nofilter}},{{$denygid nofilter}} ],
- {{$features.aclautomention}},
- {{if $APP->is_mobile}}true{{else}}false{{/if}}
- );
- }
-});
-</script>
</ul>
<div id="event-edit-form-wrapper">
- <form id="event-edit-form" action="{{$post}}" method="post" data-allow_cid="{{$allow_cid}}" data-allow_gid="{{$allow_gid}}" data-deny_cid="{{$deny_cid}}" data-deny_gid="{{$deny_gid}}">
+ <form id="event-edit-form" action="{{$post}}" method="post">
<input type="hidden" name="event_id" value="{{$eid}}" />
<input type="hidden" name="cid" value="{{$cid}}" />
<!--
This is the template used by mod/fbrowser.php
-->
-<div class="fbrowser {{$type}}">
+<div id="filebrowser" class="fbrowser {{$type}}" data-nickname="{{$nickname}}" data-type="{{$type}}">
<div class="fbrowser-content">
- <input id="fb-nickname" type="hidden" name="type" value="{{$nickname}}" />
- <input id="fb-type" type="hidden" name="type" value="{{$type}}" />
-
<div class="error hidden">
- <span></span> <button type="button" class="btn btn-link close" aria-label="Close">X</a>
+ <span></span> <button type="button" class="btn btn-link close" aria-label="Close">X</button>
</div>
{{* The breadcrumb navigation *}}
<script type="text/javascript" src="view/asset/jquery-datetimepicker/build/jquery.datetimepicker.full.min.js"></script>
<script type="text/javascript" src="view/asset/perfect-scrollbar/js/perfect-scrollbar.jquery.min.js"></script>
<script type="text/javascript" src="view/asset/imagesloaded/imagesloaded.pkgd.min.js"></script>
-<script type="text/javascript" src="view/js/acl.js"></script>
<script type="text/javascript" src="view/asset/base64/base64.min.js"></script>
<script type="text/javascript" src="view/asset/dompurify/dist/purify.min.js"></script>
<script type="text/javascript" src="view/js/main.js"></script>
</ul>
</div>
- </form>
+ <div id="jot-fbrowser-wrapper" class="minimize" aria-labelledby="jot-browser-link" role="tabpanel" aria-hidden="true"></div>
- <div id="jot-fbrowser-wrapper" class="minimize" aria-labelledby="jot-browser-link" role="tabpanel" aria-hidden="true"></div>
+ </form>
{{if $content}}<script type="text/javascript">initEditor();</script>{{/if}}
</div>
width: 690px;
float: left;
}
-#acl-search {
- float: right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right: 20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0 30px;
- color: #999999;
- -moz-border-radius: 5px 5px 5px 5px;
- -webkit-border-radius: 5px 5px 5px 5px;
- border-radius: 5px 5px 5px 5px;
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img {
- width: 22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p {
- height: 12px;
- font-size: 10px;
- margin: 0;
- padding: 2px 0 1px;
- overflow: hidden;
-}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color: #000000;
}
-.acl-button-show {
- background-image: url("../../../images/show_off.png");
-}
-.acl-button-hide {
- background-image: url("../../../images/hide_off.png");
-}
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow {
- border-color: #9ade00;
-}
-.acl-list-item.grouphide {
- border-color: #ff4141;
-}
/** /acl **/
/** tab buttons **/
ul.tabs {
width: 690px;
float: left;
}
-#acl-search {
- float: right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right: 20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0 30px;
- color: #999999;
- -moz-border-radius: 5px 5px 5px 5px;
- -webkit-border-radius: 5px 5px 5px 5px;
- border-radius: 5px 5px 5px 5px;
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img {
- width: 22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p {
- height: 12px;
- font-size: 10px;
- margin: 0;
- padding: 2px 0 1px;
- overflow: hidden;
-}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color: #000000;
}
-.acl-button-show {
- background-image: url("../../../images/show_off.png");
-}
-.acl-button-hide {
- background-image: url("../../../images/hide_off.png");
-}
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow {
- border-color: #9ade00;
-}
-.acl-list-item.grouphide {
- border-color: #ff4141;
-}
/** /acl **/
/** tab buttons **/
ul.tabs {
width: 690px;
float: left;
}
-#acl-search {
- float: right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right: 20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0 30px;
- color: #999999;
- -moz-border-radius: 5px 5px 5px 5px;
- -webkit-border-radius: 5px 5px 5px 5px;
- border-radius: 5px 5px 5px 5px;
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img {
- width: 22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p {
- height: 12px;
- font-size: 10px;
- margin: 0;
- padding: 2px 0 1px;
- overflow: hidden;
-}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color: #000000;
}
-.acl-button-show {
- background-image: url("../../../images/show_off.png");
-}
-.acl-button-hide {
- background-image: url("../../../images/hide_off.png");
-}
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow {
- border-color: #9ade00;
-}
-.acl-list-item.grouphide {
- border-color: #ff4141;
-}
/** /acl **/
/** tab buttons **/
ul.tabs {
display:block!important;
}
-
-
#acl-wrapper {
width: 690px;
float:left;
}
-#acl-search {
- float:right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right:20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0 30px;
- color: #999999;
- .rounded(5px);
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-#acl-list-content {
-
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img{
- width:22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p { height: 12px; font-size: 10px; margin: 0; padding: 2px 0 1px; overflow: hidden;}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color:#000000;
}
-.acl-button-show { background-image: url("../../../images/show_off.png"); }
-.acl-button-hide { background-image: url("../../../images/hide_off.png"); }
-
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow { border-color: #9ade00; }
-.acl-list-item.grouphide { border-color: #ff4141; }
/** /acl **/
/** tab buttons **/
width: 690px;
float:left;
}
-#acl-search {
- float:right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right: 20px;
-}
-
-#acl-showall {
- float: left;
- display: block;
- font-size: 1em;
- font-style: bold;
- text-align: center;
- padding: 3px;
- margin-bottom: 5px;
- background-color: #cccccc;
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 5px;
- border-radius: 5px;
- -webkit-border-radius: 5px ;
- -moz-border-radius: 5px;
- color: #999999;
-}
-
-#acl-showall.selected {
- color: #ffffff;
- background-color: #1873a2;
-}
-
-#acl-list {
- height: 400px;
- border: 1px solid #cccccc;
- background-color: #efefef;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-
-#acl-list-content {
-}
-
-.acl-list-item {
- display: block;
- width: 155px;
- height: 50px;
- border: 1px solid #cccccc;
- background-color: #fff;
- margin: 5px;
- float: left;
- box-shadow: 2px 2px 3px #c1c1c1;
- -moz-box-shadow: 2px 2px 3px #c1c1c1;
- -webkit-box-shadow: 2px 2px 3px #c1c1c1;
-}
-.acl-list-item img {
- width: 30px;
- height: 30px;
- float: left;
- margin: 5px;
-}
-
-.acl-list-item p {
- color: #999999;
- height: 12px;
- font-size: 0.7em;
- margin: 0px;
- padding: 2px 0px 1px;
- overflow: hidden;
-}
-
-.acl-list-item a {
- font-size: 10px;
- display: block;
- float: left;
- color: #efefef;
- background-color: #898989;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin: 10px 0 0 5px;
- border-radius: 2px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- padding: 3px;
-}
-
#acl-wrapper a:hover {
text-decoration: none;
background-color:#1873a2;
}
-.acl-button-show.selected {
- color: #efefef;
- background-color: #1873a2;
-}
-
-.acl-button-hide .selected {
- color: #efefef;
- background-color: #a2a2a2;
-}
-
-.acl-list-item.groupshow { border-color: #1873a2; }
-.acl-list-item.grouphide { border-color: #a2a2a2; }
-
/* ========================= */
/* = Global Directory Link = */
/* ========================= */
<div style="display: none;">
<div id="profile-jot-acl-wrapper" style="width:auto;height:auto;overflow:auto;">
{{$acl nofilter}}
- <hr style="clear:both"/>
- <div id="profile-jot-email-label">{{$emailcc}}</div><input type="text" name="emailcc" id="profile-jot-email" title="{{$emtitle}}" />
- <div id="profile-jot-email-end"></div>
{{$jotnets nofilter}}
</div>
</div>
background-color: #252C33 !important;
}
-/* ACL permission popup */
- .acl-list-item.groupshow {
- border-color: #9ade00 !important;
-}
-
-.acl-list-item.grouphide {
- border-color: #ff4141 !important;
-}
-
/* Notifications */
li.notify-unseen {
background-color: #252C33;
/* ACL window */
#profile-jot-acl-wrapper, #profile-jot-acl-wrapper * { box-sizing: border-box; }
#acl-wrapper { width: 100%; float: none; }
-#acl-search { width: 100%; float: none; padding-right: 0px; margin-bottom: 1em; }
-#acl-showall { width: 100%; height: 48px; margin-bottom: 1em; }
-.acl-list-item { width: auto; float: none; height: auto; overflow: hidden; position: relative;}
-.acl-list-item img { width: 48px; height: 48px; }
-.acl-list-item p { height: auto; font-size: inherit; }
-.acl-list-item a {
- float: none;
- position: absolute;
- top: 5px;
- right: 5px;
- height: 48px;
- padding: 10px 2px 2px 2px;
- font-size: 12px;
- width: 20%;
- text-align: center;
- background-position: center 5px;
-}
-.acl-list-item a.acl-button-hide { right: 25%; }
/* flexbox for ACL window */
#cboxLoadedContent,
#cboxLoadedContent > div,
-ms-flex: 1 100%;
flex: 1 100%;
}
-#acl-list {
- -webkit-flex: 1 1 auto;
- -moz-flex: 1 1 auto;
- -ms-flex: 1 1 auto;
- flex: 1 1 auto;
-}
/** input elements **/
input,
width: 690px;
float: left;
}
-#acl-search {
- float: right;
- background: #ffffff url("../../../images/search_18.png") no-repeat right center;
- padding-right: 20px;
-}
-#acl-showall {
- float: left;
- display: block;
- width: auto;
- height: 18px;
- background-color: #cccccc;
- background-image: url("../../../images/show_all_off.png");
- background-position: 7px 7px;
- background-repeat: no-repeat;
- padding: 7px 5px 0px 30px;
- color: #999999;
- -moz-border-radius: 5px 5px 5px 5px;
- -webkit-border-radius: 5px 5px 5px 5px;
- border-radius: 5px 5px 5px 5px;
-}
-#acl-showall.selected {
- color: #000000;
- background-color: #ff9900;
- background-image: url("../../../images/show_all_on.png");
-}
-#acl-list {
- height: 210px;
- border: 1px solid #cccccc;
- clear: both;
- margin-top: 30px;
- overflow: auto;
-}
-.acl-list-item {
- display: block;
- width: 150px;
- height: 30px;
- border: 1px solid #cccccc;
- margin: 5px;
- float: left;
-}
-.acl-list-item img {
- width: 22px;
- height: 22px;
- float: left;
- margin: 4px;
-}
-.acl-list-item p {
- height: 12px;
- font-size: 10px;
- margin: 0px;
- padding: 2px 0px 1px;
- overflow: hidden;
-}
-.acl-list-item a {
- font-size: 8px;
- display: block;
- width: 40px;
- height: 10px;
- float: left;
- color: #999999;
- background-color: #cccccc;
- background-position: 3px 3px;
- background-repeat: no-repeat;
- margin-right: 5px;
- -webkit-border-radius: 2px ;
- -moz-border-radius: 2px;
- border-radius: 2px;
- padding-left: 15px;
-}
#acl-wrapper a:hover {
text-decoration: none;
color: #000000;
}
-.acl-button-show {
- background-image: url("../../../images/show_off.png");
-}
-.acl-button-hide {
- background-image: url("../../../images/hide_off.png");
-}
-.acl-button-show.selected {
- color: #000000;
- background-color: #9ade00;
- background-image: url("../../../images/show_on.png");
-}
-.acl-button-hide.selected {
- color: #000000;
- background-color: #ff4141;
- background-image: url("../../../images/hide_on.png");
-}
-.acl-list-item.groupshow {
- border-color: #9ade00;
-}
-.acl-list-item.grouphide {
- border-color: #ff4141;
-}
/** /acl **/
/** tab buttons **/
div.pager, ul.tabs {