This is information specific to the hook currently being processed, and generally contains information that is being immediately processed or acted on that you can use, display, or alter.
Remember to declare it with `&` if you wish to alter it.
-## JavaScript addon hooks
+## Global stylesheets
-### PHP part
+If your addon requires adding a stylesheet on all pages of Friendica, add the following hook:
-Make sure your JavaScript addon file (addon/*addon_name*/*addon_name*.js) is listed in the document response.
+```php
+function <addon>_install()
+{
+ Addon::registerHook('head', __FILE__, '<addon>_head');
+ ...
+}
-In your addon install function, add:
-```php
-Addon::registerHook('template_vars', __FILE__, '<addon_name>_template_vars');
+function <addon>_head(App $a)
+{
+ $a->registerStylesheet(__DIR__ . '/relative/path/to/addon/stylesheet.css');
+}
```
-In your addon uninstall function, add:
+`__DIR__` is the folder path of your addon.
-```php
-Addon::unregisterHook('template_vars', __FILE__, '<addon_name>_template_vars');
-```
+## JavaScript
+
+### Global scripts
+
+If your addon requires adding a script on all pages of Friendica, add the following hook:
-Then, add your addon name to the *addon_hooks* template variable array:
```php
-function <addon_name>_template_vars($a, &$arr)
+function <addon>_install()
{
- if (!array_key_exists('addon_hooks', $arr['vars']))
- {
- $arr['vars']['addon_hooks'] = array();
- }
- $arr['vars']['addon_hooks'][] = "<addon_name>";
+ Addon::registerHook('footer', __FILE__, '<addon>_footer');
+ ...
+}
+
+function <addon>_footer(App $a)
+{
+ $a->registerFooterScript(__DIR__ . '/relative/path/to/addon/script.js');
}
```
-### JavaScript part
+`__DIR__` is the folder path of your addon.
+
+### JavaScript hooks
-Register your addon hooks in file `addon/*addon_name*/*addon_name*.js`.
+The main Friendica script provides hooks via events dispatched on the `document` property.
+In your Javascript file included as described above, add your event listener like this:
```js
-Addon_registerHook(type, hookfnstr);
+document.addEventListener(name, callback);
```
-*type* is the name of the hook and corresponds to a known Friendica JavaScript hook.
-*hookfnstr* is the name of your JavaScript function to execute.
+- *name* is the name of the hook and corresponds to a known Friendica JavaScript hook.
+- *callback* is a JavaScript anonymous function to execute.
-No arguments are provided to your JavaScript callback function. Example:
+More info about Javascript event listeners: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
-```javascript
-function myhook_function() {
+#### Current JavaScript hooks
-}
-```
+##### postprocess_liveupdate
+Called at the end of the live update process (XmlHttpRequest) and on a post preview.
+No additional data is provided.
## Modules
Called after tag conversion of HTML to bbcode (e.g. remote message posting)
`$b` is a string converted text
+### head
+Called when building the `<head>` sections.
+Stylesheets should be registered using this hook.
+`$b` is an HTML string of the `<head>` tag.
+
### page_header
Called after building the page navigation section.
`$b` is a string HTML of nav region.
Called after HTML content functions have completed.
`$b` is (string) HTML of content div.
+### footer
+Called after HTML content functions have completed.
+Deferred Javascript files should be registered using this hook.
+`$b` is (string) HTML of footer div/element.
+
### avatar_lookup
Called when looking up the avatar. `$b` is an array:
visitor => array with the contact record of the visitor
url => the query string
-## Current JavaScript hooks
-
-### postprocess_liveupdate
-Called at the end of the live update process (XmlHttpRequest)
-
## Complete list of hook callbacks
-Here is a complete list of all hook callbacks with file locations (as of 01-Apr-2018). Please see the source for details of any hooks not documented above.
+Here is a complete list of all hook callbacks with file locations (as of 24-Sep-2018). Please see the source for details of any hooks not documented above.
### index.php
### src/App.php
Addon::callHooks('load_config');
+ Addon::callHooks('head');
+ Addon::callHooks('footer');
### src/Model/Item.php
### view/js/main.js
- callAddonHooks("postprocess_liveupdate");
+ document.dispatchEvent(new Event('postprocess_liveupdate'));
Die Entwickler Version kann nach einem fehlerhaften Commit vorübergehend Probleme haben oder gar nicht mehr funktionieren.
Sollte dir so etwas passieren, lass es uns bitte wissen, damit der Fehler behoben werden kann.
-### Erselle eine Datenbank
+### Erstelle eine Datenbank
Erstelle eine leere Datenbank und notiere alle Zugangsdaten (Adresse der Datenbank, Nutzername, Passwort, Datenbankname).
$status_user["protected"] = defaults($item, 'private', 0);
if (defaults($item, 'thr-parent', '') == defaults($item, 'uri', '')) {
- $owner_user = api_get_user($a, defaults($item, 'author-id', null));
+ $owner_user = api_get_user($a, defaults($item, 'owner-id', null));
} else {
$owner_user = $status_user;
}
// standard meta information
$ret = [
'id' => $item['id'],
- 'sender_id' => $sender['id'] ,
+ 'sender_id' => $sender['id'],
'text' => "",
'recipient_id' => $recipient['id'],
'created_at' => api_date(defaults($item, 'created', DateTimeFormat::utcNow())),
* likes => int count,
* dislikes => int count
*/
-function api_format_items_activities(&$item, $type = "json")
+function api_format_items_activities($item, $type = "json")
{
$a = get_app();
$condition = ['uid' => $item['uid'], 'thr-parent' => $item['uri']];
$ret = Item::selectForUser($item['uid'], ['author-id', 'verb'], $condition);
- while ($item = Item::fetch($ret)) {
+ while ($parent_item = Item::fetch($ret)) {
// not used as result should be structured like other user data
//builtin_activity_puller($i, $activities);
// get user data and add it to the array of the activity
- $user = api_get_user($a, $item['author-id']);
- switch ($item['verb']) {
+ $user = api_get_user($a, $parent_item['author-id']);
+ switch ($parent_item['verb']) {
case ACTIVITY_LIKE:
$activities['like'][] = $user;
break;
'in_reply_to_screen_name' => $in_reply_to['screen_name'],
$geo => null,
'favorited' => $item['starred'] ? true : false,
- 'user' => $status_user ,
+ 'user' => $status_user,
'friendica_owner' => $owner_user,
'friendica_private' => $item['private'] == 1,
//'entities' => NULL,
*/
function api_ff_ids($type)
{
- if (! api_user()) {
+ if (!api_user()) {
throw new ForbiddenException();
}
/// @TODO move to top of file or somewhere better
api_register_func('api/direct_messages/destroy', 'api_direct_messages_destroy', true, API_METHOD_DELETE);
+/**
+ * Unfollow Contact
+ *
+ * @brief unfollow contact
+ *
+ * @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
+ * @return string|array
+ * @see https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/post-friendships-destroy.html
+ */
+function api_friendships_destroy($type)
+{
+ $uid = api_user();
+
+ if ($uid === false) {
+ throw new ForbiddenException();
+ }
+
+ $contact_id = defaults($_REQUEST, 'user_id');
+
+ if (empty($contact_id)) {
+ logger("No user_id specified", LOGGER_DEBUG);
+ throw new BadRequestException("no user_id specified");
+ }
+
+ // Get Contact by given id
+ $contact = DBA::selectFirst('contact', ['url'], ['id' => $contact_id, 'uid' => 0, 'self' => false]);
+
+ if(!DBA::isResult($contact)) {
+ logger("No contact found for ID" . $contact_id, LOGGER_DEBUG);
+ throw new NotFoundException("no contact found to given ID");
+ }
+
+ $url = $contact["url"];
+
+ $condition = ["`uid` = ? AND (`rel` = ? OR `rel` = ?) AND (`nurl` = ? OR `alias` = ? OR `alias` = ?)",
+ $uid, Contact::SHARING, Contact::FRIEND, normalise_link($url),
+ normalise_link($url), $url];
+ $contact = DBA::selectFirst('contact', [], $condition);
+
+ if (!DBA::isResult($contact)) {
+ logger("Not following Contact", LOGGER_DEBUG);
+ throw new NotFoundException("Not following Contact");
+ }
+
+ if (!in_array($contact['network'], Protocol::NATIVE_SUPPORT)) {
+ logger("Not supported", LOGGER_DEBUG);
+ throw new ExpectationFailedException("Not supported");
+ }
+
+ $dissolve = ($contact['rel'] == Contact::SHARING);
+
+ $owner = User::getOwnerDataById($uid);
+ if ($owner) {
+ Contact::terminateFriendship($owner, $contact, $dissolve);
+ }
+ else {
+ logger("No owner found", LOGGER_DEBUG);
+ throw new NotFoundException("Error Processing Request");
+ }
+
+ // Sharing-only contacts get deleted as there no relationship any more
+ if ($dissolve) {
+ Contact::remove($contact['id']);
+ } else {
+ DBA::update('contact', ['rel' => Contact::FOLLOWER], ['id' => $contact['id']]);
+ }
+
+ // "uid" and "self" are only needed for some internal stuff, so remove it from here
+ unset($contact["uid"]);
+ unset($contact["self"]);
+
+ // Set screen_name since Twidere requests it
+ $contact["screen_name"] = $contact["nick"];
+
+ return api_format_data("friendships-destroy", $type, ['user' => $contact]);
+}
+api_register_func('api/friendships/destroy', 'api_friendships_destroy', true, API_METHOD_POST);
+
/**
*
* @param string $type Return type (atom, rss, xml, json)
// create Photo instance with the data of the image
$imagedata = @file_get_contents($src);
$Image = new Image($imagedata, $filetype);
- if (! $Image->isValid()) {
+ if (!$Image->isValid()) {
throw new InternalServerErrorException("unable to process image data");
}
// check max length of images on server
$max_length = Config::get('system', 'max_image_length');
- if (! $max_length) {
+ if (!$max_length) {
$max_length = MAX_IMAGE_LENGTH;
}
if ($max_length > 0) {
logger("photo upload: starting new photo upload", LOGGER_DEBUG);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 0, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: image upload with scale 0 (original size) failed");
}
if ($width > 640 || $height > 640) {
$Image->scaleDown(640);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 1, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: image upload with scale 1 (640x640) failed");
}
}
if ($width > 320 || $height > 320) {
$Image->scaleDown(320);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 2, 0, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: image upload with scale 2 (320x320) failed");
}
}
if ($width > 175 || $height > 175) {
$Image->scaleDown(175);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 4, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: profile image upload with scale 4 (175x175) failed");
}
}
if ($width > 80 || $height > 80) {
$Image->scaleDown(80);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 5, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: profile image upload with scale 5 (80x80) failed");
}
}
if ($width > 48 || $height > 48) {
$Image->scaleDown(48);
$r = Photo::store($Image, local_user(), $visitor, $hash, $filename, $album, 6, $profile, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $desc);
- if (! $r) {
+ if (!$r) {
logger("photo upload: profile image upload with scale 6 (48x48) failed");
}
}
{
$body = trim($item["body"]);
- if (Diaspora::isReshare($body, false)===false) {
- return false;
+ if (Diaspora::isReshare($body, false) === false) {
+ if ($item['author-id'] == $item['owner-id']) {
+ return false;
+ } else {
+ // Reshares from OStatus, ActivityPub and Twitter
+ $reshared_item = $item;
+ $reshared_item['owner-id'] = $reshared_item['author-id'];
+ $reshared_item['owner-link'] = $reshared_item['author-link'];
+ $reshared_item['owner-name'] = $reshared_item['author-name'];
+ $reshared_item['owner-avatar'] = $reshared_item['author-avatar'];
+ return $reshared_item;
+ }
}
/// @TODO "$1" should maybe mean '$1' ?
'$delitems' => L10n::t("Delete item\x28s\x29?")
]);
- $tpl = get_markup_template('jot-end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$newpost' => 'true',
- '$baseurl' => System::baseUrl(true),
- '$geotag' => $geotag,
- '$nickname' => $x['nickname'],
- '$ispublic' => L10n::t('Visible to <strong>everybody</strong>'),
- '$linkurl' => L10n::t('Please enter a link URL:'),
- '$vidurl' => L10n::t("Please enter a video link/URL:"),
- '$audurl' => L10n::t("Please enter an audio link/URL:"),
- '$term' => L10n::t('Tag term:'),
- '$fileas' => L10n::t('Save to Folder:'),
- '$whereareu' => L10n::t('Where are you right now?')
- ]);
-
$jotplugins = '';
Addon::callHooks('jot_tool', $jotplugins);
$a->page['htmlhead'] .= replace_macros(get_markup_template('videos_head.tpl'), [
'$baseurl' => System::baseUrl(),
]);
- $a->page['end'] .= replace_macros(get_markup_template('videos_end.tpl'), [
- '$baseurl' => System::baseUrl(),
- ]);
}
$url_parts = explode('/', $the_url);
* Language was set earlier, but we can over-ride it in the session.
* We have to do it here because the session was just now opened.
*/
-if (x($_SESSION, 'authenticated') && !x($_SESSION, 'language')) {
+if (!empty($_SESSION['authenticated']) && empty($_SESSION['language'])) {
$_SESSION['language'] = $lang;
// we haven't loaded user data yet, but we need user language
if (!empty($_SESSION['uid'])) {
}
}
-if (x($_SESSION, 'language') && ($_SESSION['language'] !== $lang)) {
+if (!empty($_SESSION['language']) && $_SESSION['language'] !== $lang) {
$lang = $_SESSION['language'];
L10n::loadTranslationTable($lang);
}
logger("Invalid ZRL parameter " . $_GET['zrl'], LOGGER_DEBUG);
header('HTTP/1.1 403 Forbidden');
echo "<h1>403 Forbidden</h1>";
- killme();
+ exit();
}
}
}
-if ((x($_GET,'owt')) && $a->mode == App::MODE_NORMAL) {
+if (!empty($_GET['owt']) && $a->mode == App::MODE_NORMAL) {
$token = $_GET['owt'];
$a->query_string = Profile::stripQueryParam($a->query_string, 'owt');
Profile::openWebAuthInit($token);
Login::sessionAuth();
-if (! x($_SESSION, 'authenticated')) {
+if (empty($_SESSION['authenticated'])) {
header('X-Account-Management-Status: none');
}
-/* set up page['htmlhead'] and page['end'] for the modules to use */
-$a->page['htmlhead'] = '';
-$a->page['end'] = '';
-
$_SESSION['sysmsg'] = defaults($_SESSION, 'sysmsg' , []);
$_SESSION['sysmsg_info'] = defaults($_SESSION, 'sysmsg_info' , []);
$_SESSION['last_updated'] = defaults($_SESSION, 'last_updated', []);
if (! $a->module_loaded) {
// Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit.
- if ((x($_SERVER, 'QUERY_STRING')) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
+ if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) {
killme();
}
- if ((x($_SERVER, 'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
+ if (!empty($_SERVER['QUERY_STRING']) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) {
logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']);
goaway(System::baseUrl() . $_SERVER['REQUEST_URI']);
}
logger('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], LOGGER_DEBUG);
header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . L10n::t('Not Found'));
$tpl = get_markup_template("404.tpl");
- $a->page['content'] = replace_macros(
- $tpl,
- [
- '$message' => L10n::t('Page not found.')]
- );
+ $a->page['content'] = replace_macros($tpl, [
+ '$message' => L10n::t('Page not found.')
+ ]);
}
}
/* initialise content region */
-if (! x($a->page, 'content')) {
- $a->page['content'] = '';
-}
-
if ($a->mode == App::MODE_NORMAL) {
Addon::callHooks('page_content_top', $a->page['content']);
}
* theme choices made by the modules can take effect.
*/
-$a->init_pagehead();
+$a->initHead();
/*
* Build the page ending -- this is stuff that goes right before
* the closing </body> tag
*/
-$a->init_page_end();
-
-// If you're just visiting, let javascript take you home
-if (x($_SESSION, 'visitor_home')) {
- $homebase = $_SESSION['visitor_home'];
-} elseif (local_user()) {
- $homebase = 'profile/' . $a->user['nickname'];
-}
-
-if (isset($homebase)) {
- $a->page['content'] .= '<script>var homebase="' . $homebase . '" ; </script>';
-}
+$a->initFooter();
/*
* now that we've been through the module content, see if the page reported
Nav::build($a);
}
-/*
- * Add a "toggle mobile" link if we're using a mobile device
- */
-if ($a->is_mobile || $a->is_tablet) {
- if (isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
- $link = 'toggle_mobile?address=' . curPageURL();
- } else {
- $link = 'toggle_mobile?off=1&address=' . curPageURL();
- }
- $a->page['footer'] = replace_macros(
- get_markup_template("toggle_mobile_footer.tpl"),
- [
- '$toggle_link' => $link,
- '$toggle_text' => L10n::t('toggle mobile')]
- );
-}
-
/**
* Build the page - now that we have all the components
*/
-
-if (!$a->theme['stylesheet']) {
- $stylesheet = $a->getCurrentThemeStylesheetPath();
-} else {
- $stylesheet = $a->theme['stylesheet'];
-}
-
-$a->page['htmlhead'] = str_replace('{{$stylesheet}}', $stylesheet, $a->page['htmlhead']);
-//$a->page['htmlhead'] = replace_macros($a->page['htmlhead'], array('$stylesheet' => $stylesheet));
-
if (isset($_GET["mode"]) && (($_GET["mode"] == "raw") || ($_GET["mode"] == "minimal"))) {
$doc = new DOMDocument();
echo substr($target->saveHTML(), 6, -8);
- killme();
+ exit();
}
$page = $a->page;
/// @TODO Looks unsafe (remote-inclusion), is maybe not but Theme::getPathForFile() uses file_exists() but does not escape anything
require_once $template;
-
-killme();
$queues = ['label' => L10n::t('Message queues'), 'queue' => $queue, 'workerq' => $workerqueue];
+ $r = q("SHOW variables LIKE 'max_allowed_packet'");
+ $max_allowed_packet = (($r) ? $r[0]['Value'] : 0);
+
+ $server_settings = ['label' => L10n::t('Server Settings'),
+ 'php' => ['upload_max_filesize' => ini_get('upload_max_filesize'),
+ 'post_max_size' => ini_get('post_max_size'),
+ 'memory_limit' => ini_get('memory_limit')],
+ 'mysql' => ['max_allowed_packet' => $max_allowed_packet]];
+
$t = get_markup_template('admin/summary.tpl');
return replace_macros($t, [
'$title' => L10n::t('Administration'),
'$codename' => FRIENDICA_CODENAME,
'$build' => Config::get('system', 'build'),
'$addons' => [L10n::t('Active addons'), $a->addons],
+ '$serversettings' => $server_settings,
'$showwarning' => $showwarning,
'$warningtext' => $warningtext
]);
'$i18n' => $i18n,
]);
- $etpl = get_markup_template('event_end.tpl');
- $a->page['end'] .= replace_macros($etpl, [
- '$baseurl' => System::baseUrl(),
- ]);
-
$mode = 'view';
$y = 0;
$m = 0;
'$baseurl' => System::baseUrl(true),
'$base' => $base
]);
-
- $tpl = get_markup_template("contacts-end.tpl");
- $a->page['end'] .= replace_macros($tpl, [
- '$baseurl' => System::baseUrl(true),
- '$base' => $base
- ]);
}
function contacts_batch_actions(App $a)
$a->page['htmlhead'] .= replace_macros(get_markup_template('contact_head.tpl'), [
'$baseurl' => System::baseUrl(true),
]);
- $a->page['end'] .= replace_macros(get_markup_template('contact_end.tpl'), [
- '$baseurl' => System::baseUrl(true),
- ]);
$contact['blocked'] = Contact::isBlockedByUser($contact['id'], local_user());
$contact['readonly'] = Contact::isIgnoredByUser($contact['id'], local_user());
'$nickname' => $a->user['nickname']
]);
- $tpl = get_markup_template('jot-end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$baseurl' => System::baseUrl(),
- '$ispublic' => ' ', // L10n::t('Visible to <strong>everybody</strong>'),
- '$geotag' => $geotag,
- '$nickname' => $a->user['nickname']
- ]);
-
-
$tpl = get_markup_template("jot.tpl");
if (strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) {
'$i18n' => $i18n,
]);
- $etpl = get_markup_template('event_end.tpl');
- $a->page['end'] .= replace_macros($etpl, [
- '$baseurl' => System::baseUrl(),
- ]);
-
$o = '';
$tabs = '';
// tabs
'$baseurl' => System::baseUrl(true),
'$base' => $base
]);
-
- $end_tpl = get_markup_template('message-end.tpl');
- $a->page['end'] .= replace_macros($end_tpl, [
- '$baseurl' => System::baseUrl(true),
- '$base' => $base
- ]);
}
function message_post(App $a)
'$linkurl' => L10n::t('Please enter a link URL:')
]);
- $tpl = get_markup_template('msg-end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$baseurl' => System::baseUrl(true),
- '$nickname' => $a->user['nickname'],
- '$linkurl' => L10n::t('Please enter a link URL:')
- ]);
-
$preselect = isset($a->argv[2]) ? [$a->argv[2]] : [];
$prename = $preurl = $preid = '';
'$linkurl' => L10n::t('Please enter a link URL:')
]);
- $tpl = get_markup_template('msg-end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$baseurl' => System::baseUrl(true),
- '$nickname' => $a->user['nickname'],
- '$linkurl' => L10n::t('Please enter a link URL:')
- ]);
-
$mails = [];
$seen = 0;
$unknown = false;
}
$a->page['htmlhead'] .= replace_macros(get_markup_template("crophead.tpl"), []);
- $a->page['end'] .= replace_macros(get_markup_template("cropend.tpl"), []);
$imagecrop = [
'hash' => $hash,
$a->page['htmlhead'] .= replace_macros(get_markup_template('profed_head.tpl'), [
'$baseurl' => System::baseUrl(true),
]);
- $a->page['end'] .= replace_macros(get_markup_template('profed_end.tpl'), [
- '$baseurl' => System::baseUrl(true),
- ]);
$opt_tpl = get_markup_template("profile-hide-friends.tpl");
$hide_friends = replace_macros($opt_tpl,[
'$theme_config' => $theme_config,
]);
- $tpl = get_markup_template('settings/display_end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$theme' => ['theme', L10n::t('Display Theme:'), $theme_selected, '', $themes]
- ]);
-
return $o;
}
$a->page['htmlhead'] .= replace_macros($tpl,[
'$baseurl' => System::baseUrl(),
]);
-
- $tpl = get_markup_template("videos_end.tpl");
- $a->page['end'] .= replace_macros($tpl,[
- '$baseurl' => System::baseUrl(),
- ]);
-
}
return;
'$linkurl' => L10n::t('Please enter a link URL:')
]);
- $tpl = get_markup_template('wallmsg-end.tpl');
- $a->page['end'] .= replace_macros($tpl, [
- '$baseurl' => System::baseUrl(true),
- '$nickname' => $user['nickname'],
- '$linkurl' => L10n::t('Please enter a link URL:')
- ]);
-
$tpl = get_markup_template('wallmessage.tpl');
$o = replace_macros($tpl, [
'$header' => L10n::t('Send Private Message'),
public $force_max_items = 0;
public $theme_events_in_profile = true;
+ public $stylesheets = [];
+ public $footerScripts = [];
+
+ /**
+ * Register a stylesheet file path to be included in the <head> tag of every page.
+ * Inclusion is done in App->initHead().
+ * The path can be absolute or relative to the Friendica installation base folder.
+ *
+ * @see App->initHead()
+ *
+ * @param string $path
+ */
+ public function registerStylesheet($path)
+ {
+ $url = str_replace($this->get_basepath() . DIRECTORY_SEPARATOR, '', $path);
+
+ $this->stylesheets[] = trim($url, '/');
+ }
+
+ /**
+ * Register a javascript file path to be included in the <footer> tag of every page.
+ * Inclusion is done in App->initFooter().
+ * The path can be absolute or relative to the Friendica installation base folder.
+ *
+ * @see App->initFooter()
+ *
+ * @param string $path
+ */
+ public function registerFooterScript($path)
+ {
+ $url = str_replace($this->get_basepath() . DIRECTORY_SEPARATOR, '', $path);
+
+ $this->footerScripts[] = trim($url, '/');
+ }
+
/**
* @brief An array for all theme-controllable parameters
*
'aside' => '',
'bottom' => '',
'content' => '',
- 'end' => '',
'footer' => '',
'htmlhead' => '',
'nav' => '',
$this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
}
- public function init_pagehead()
+ /**
+ * Initializes App->page['htmlhead'].
+ *
+ * Includes:
+ * - Page title
+ * - Favicons
+ * - Registered stylesheets (through App->registerStylesheet())
+ * - Infinite scroll data
+ * - head.tpl template
+ */
+ public function initHead()
{
$interval = ((local_user()) ? PConfig::get(local_user(), 'system', 'update_interval') : 40000);
$this->page['title'] = $this->config['sitename'];
}
- /* put the head template at the beginning of page['htmlhead']
- * since the code added by the modules frequently depends on it
- * being first
- */
- if (!isset($this->page['htmlhead'])) {
- $this->page['htmlhead'] = '';
- }
-
- // If we're using Smarty, then doing replace_macros() will replace
- // any unrecognized variables with a blank string. Since we delay
- // replacing $stylesheet until later, we need to replace it now
- // with another variable name
- if ($this->theme['template_engine'] === 'smarty3') {
- $stylesheet = $this->get_template_ldelim('smarty3') . '$stylesheet' . $this->get_template_rdelim('smarty3');
+ if (!empty($this->theme['stylesheet'])) {
+ $stylesheet = $this->theme['stylesheet'];
} else {
- $stylesheet = '$stylesheet';
+ $stylesheet = $this->getCurrentThemeStylesheetPath();
}
+ $this->registerStylesheet($stylesheet);
+
$shortcut_icon = Config::get('system', 'shortcut_icon');
if ($shortcut_icon == '') {
$shortcut_icon = 'images/friendica-32.png';
}
// get data wich is needed for infinite scroll on the network page
- $invinite_scroll = infinite_scroll_data($this->module);
+ $infinite_scroll = infinite_scroll_data($this->module);
+
+ Core\Addon::callHooks('head', $this->page['htmlhead']);
$tpl = get_markup_template('head.tpl');
+ /* put the head template at the beginning of page['htmlhead']
+ * since the code added by the modules frequently depends on it
+ * being first
+ */
$this->page['htmlhead'] = replace_macros($tpl, [
'$baseurl' => $this->get_baseurl(),
'$local_user' => local_user(),
'$update_interval' => $interval,
'$shortcut_icon' => $shortcut_icon,
'$touch_icon' => $touch_icon,
- '$stylesheet' => $stylesheet,
- '$infinite_scroll' => $invinite_scroll,
+ '$infinite_scroll' => $infinite_scroll,
'$block_public' => intval(Config::get('system', 'block_public')),
+ '$stylesheets' => $this->stylesheets,
]) . $this->page['htmlhead'];
}
- public function init_page_end()
+ /**
+ * Initializes App->page['footer'].
+ *
+ * Includes:
+ * - Javascript homebase
+ * - Mobile toggle link
+ * - Registered footer scripts (through App->registerFooterScript())
+ * - footer.tpl template
+ */
+ public function initFooter()
{
- if (!isset($this->page['end'])) {
- $this->page['end'] = '';
+ // If you're just visiting, let javascript take you home
+ if (!empty($_SESSION['visitor_home'])) {
+ $homebase = $_SESSION['visitor_home'];
+ } elseif (local_user()) {
+ $homebase = 'profile/' . $this->user['nickname'];
+ }
+
+ if (isset($homebase)) {
+ $this->page['footer'] .= '<script>var homebase="' . $homebase . '";</script>' . "\n";
}
- $tpl = get_markup_template('end.tpl');
- $this->page['end'] = replace_macros($tpl, [
- '$baseurl' => $this->get_baseurl()
- ]) . $this->page['end'];
+
+ /*
+ * Add a "toggle mobile" link if we're using a mobile device
+ */
+ if ($this->is_mobile || $this->is_tablet) {
+ if (isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
+ $link = 'toggle_mobile?address=' . curPageURL();
+ } else {
+ $link = 'toggle_mobile?off=1&address=' . curPageURL();
+ }
+ $this->page['footer'] .= replace_macros(get_markup_template("toggle_mobile_footer.tpl"), [
+ '$toggle_link' => $link,
+ '$toggle_text' => Core\L10n::t('toggle mobile')
+ ]);
+ }
+
+ Core\Addon::callHooks('footer', $this->page['footer']);
+
+ $tpl = get_markup_template('footer.tpl');
+ $this->page['footer'] = replace_macros($tpl, [
+ '$baseurl' => $this->get_baseurl(),
+ '$footerScripts' => $this->footerScripts,
+ ]) . $this->page['footer'];
}
public function set_curl_code($code)
*/
public function testApiShareAsRetweet()
{
- $item = ['body' => ''];
+ $item = ['body' => '', 'author-id' => 1, 'owner-id' => 1];
$result = api_share_as_retweet($item);
$this->assertFalse($result);
}
margin: 0px 0px 0px 0px;
}
-code {
- background-color: #eee;
- color: #666;
- padding: 1em;
- font-size: 0.85em;
- font-family: monospace;
-}
-key {
+code, key {
display: inline;
background-color: #eee;
color: #666;
+++ /dev/null
-/**
- * @file addon-hooks.js
- * @brief Provide a way for add-ons to register a JavaScript hook
- */
-
-var addon_hooks = {};
-
-/**
- * @brief Register a JavaScript hook to be called from other Javascript files
- * @pre the .js file from which the hook will be called is included in the document response
- * @param type which type of hook i.e. where should it be called along with other hooks of the same type
- * @param hookfnstr name of the JavaScript function name that needs to be called
- */
-function Addon_registerHook(type, hookfnstr)
-{
- if (!addon_hooks.hasOwnProperty(type)) {
- addon_hooks[type] = [];
- }
-
- addon_hooks[type].push(hookfnstr);
-}
-
-/**
- * @brief Call all registered hooks of a certain type, i.e. at the same point of the JavaScript code execution
- * @param typeOfHook string indicating which type of hooks to be called among the registered hooks
- */
-function callAddonHooks(typeOfHook)
-{
- if (typeof addon_hooks !== 'undefined') {
- var myTypeOfHooks = addon_hooks[typeOfHook];
- if (typeof myTypeOfHooks !== 'undefined') {
- for (addon_hook_idx = 0; addon_hook_idx < myTypeOfHooks.length; addon_hook_idx++) {
- var hookfnstr = myTypeOfHooks[addon_hook_idx];
- var hookfn = window[hookfnstr];
- if (typeof hookfn === "function") {
- hookfn();
- }
- }
- }
- }
-}
$('.wall-item-body', data).imagesLoaded(function() {
updateConvItems(data);
+ document.dispatchEvent(new Event('postprocess_liveupdate'));
+
// Update the scroll position.
$(window).scrollTop($(window).scrollTop() + $("section").height() - orgHeight);
});
-
- callAddonHooks("postprocess_liveupdate");
-
});
-
}
function imgbright(node) {
if (data.preview) {
$("#jot-preview-content").html(data.preview);
$("#jot-preview-content" + " a").click(function() {return false;});
+ document.dispatchEvent(new Event('postprocess_liveupdate'));
}
},
"json"
} else {
$("#scroll-end").fadeIn('normal');
}
+
+ document.dispatchEvent(new Event('postprocess_liveupdate'));
});
}
<dd> {{$platform}} '{{$codename}}' {{$version.1}} - {{$build}}</dt>
</dl>
+ <dl>
+ <dt>{{$serversettings.label}}</dt>
+ <dd>
+ <table>
+ <tbody>
+ <tr><td colspan="2"><b>PHP</b></td></tr>
+ {{foreach $serversettings.php as $k => $p}}
+ <tr><td>{{$k}}</td><td>{{$p}}</td></tr>
+ {{/foreach}}
+ <tr><td colspan="2"><b>MySQL / MariaDB</b></td></tr>
+ {{foreach $serversettings.mysql as $k => $p}}
+ <tr><td>{{$k}}</td><td>{{$p}}</td></tr>
+ {{/foreach}}
+ </tbody>
+ </table>
+ </dd>
+ </dl>
</div>
--- /dev/null
+{{foreach $footerScripts as $scriptUrl}}
+<script type="text/javascript" src="{{$scriptUrl}}"></script>
+{{/foreach}}
<link rel="stylesheet" href="view/asset/jquery-datetimepicker/build/jquery.datetimepicker.min.css" type="text/css" media="screen" />
<link rel="stylesheet" href="view/asset/perfect-scrollbar/css/perfect-scrollbar.min.css" type="text/css" media="screen" />
-<link rel="stylesheet" type="text/css" href="{{$stylesheet}}" media="all" />
+{{foreach $stylesheets as $stylesheetUrl}}
+<link rel="stylesheet" href="{{$stylesheetUrl}}" type="text/css" media="screen" />
+{{/foreach}}
-<!--
-<link rel="shortcut icon" href="images/friendica-32.png" />
-<link rel="apple-touch-icon" href="images/friendica-128.png"/>
--->
<link rel="shortcut icon" href="{{$shortcut_icon}}" />
<link rel="apple-touch-icon" href="{{$touch_icon}}"/>
<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/js/addon-hooks.js" ></script>
-{{if is_array($addon_hooks)}}
-{{foreach $addon_hooks as $addon_hook}}
-<script type="text/javascript" src="addon/{{$addon_hook}}/{{$addon_hook}}.js"></script>
-{{/foreach}}
-{{/if}}
<script type="text/javascript" src="view/js/main.js" ></script>
<script>
}
/* help page widget */
-aside > .help-content-wrapper code, .help-aside-wrapper code {
- display: inline
-}
aside > .help-aside-wrapper p strong:first-child {
display: block;
margin: 1em 0 0em;
</main>
<footer>
- <?php if (x($page, 'footer')) echo $page['footer']; ?>
- <!-- Modal -->
- <div id="modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
- <div class="modal-dialog modal-full-screen">
- <div class="modal-content">
- <div id="modal-header" class="modal-header">
- <button id="modal-cloase" type="button" class="close" data-dismiss="modal" aria-hidden="true">
- ×
- </button>
- <h4 id="modal-title" class="modal-title"></h4>
- </div>
- <div id="modal-body" class="modal-body">
- <!-- /# content goes here -->
- </div>
- </div>
- </div>
- </div>
-
- <!-- Dummy div to append other div's when needed (e.g. used for js function editpost() -->
- <div id="cache-container"></div>
-
+ <?php echo defaults($page, 'footer', ''); ?>
</footer>
<?php } ?> <!-- End of condition if $minimal else the rest -->
</body>
<div class="col-lg-4 col-md-4 col-sm-4 col-xs-12 admin-summary-label-name text-muted">{{$version.0}}</div>
<div class="col-lg-8 col-md-8 col-sm-8 col-xs-12 admin-summary-entry">{{$platform}} '{{$codename}}' {{$version.1}} - {{$build}}</div>
</div>
+
+ {{* Server Settings. *}}
+ <div id="admin-summary-php" class="col-lg-12 col-md-12 col-sm-12 col-xs-12 admin-summary">
+ <hr class="admin-summary-separator">
+ <div class="col-lg-4 col-md-4 col-sm-4 col-xs-12 admin-summary-label-name text-muted">{{$serversettings.label}}</div>
+ <div class="col-lg-8 col-md-8 col-sm-8 col-xs-12 admin-summary-entry">
+ <table class="table">
+ <tbody>
+ <tr class="info"><td colspan="2">PHP</td></tr>
+ {{foreach $serversettings.php as $k => $p}}
+ <tr><td>{{$k}}</td><td>{{$p}}</td></tr>
+ {{/foreach}}
+ <tr class="info"><td colspan="2">MySQL / MariaDB</td></tr>
+ {{foreach $serversettings.mysql as $k => $p}}
+ <tr><td>{{$k}}</td><td>{{$p}}</td></tr>
+ {{/foreach}}
+ </tbody>
+ </table>
+ </div>
+ </div>
+
</div>
<div class="clear"></div>
--- /dev/null
+ <!-- Modal -->
+ <div id="modal" class="modal fade" tabindex="-1" role="dialog" aria-hidden="true">
+ <div class="modal-dialog modal-full-screen">
+ <div class="modal-content">
+ <div id="modal-header" class="modal-header">
+ <button id="modal-cloase" type="button" class="close" data-dismiss="modal" aria-hidden="true">
+ ×
+ </button>
+ <h4 id="modal-title" class="modal-title"></h4>
+ </div>
+ <div id="modal-body" class="modal-body">
+ <!-- /# content goes here -->
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Dummy div to append other div's when needed (e.g. used for js function editpost() -->
+ <div id="cache-container"></div>
+
+{{foreach $footerScripts as $scriptUrl}}
+ <script type="text/javascript" src="{{$scriptUrl}}"></script>
+{{/foreach}}
<link rel="stylesheet" href="view/theme/frio/frameworks/bootstrap-toggle/css/bootstrap-toggle.min.css" type="text/css" media="screen"/>
<link rel="stylesheet" href="view/theme/frio/font/open_sans/open-sans.css" type="text/css" media="screen"/>
-{{* The own style.css *}}
-<link rel="stylesheet" type="text/css" href="{{$stylesheet}}" media="all" />
+{{foreach $stylesheets as $stylesheetUrl}}
+<link rel="stylesheet" href="{{$stylesheetUrl}}" type="text/css" media="screen" />
+{{/foreach}}
{{* own css files *}}
<link rel="stylesheet" href="view/theme/frio/css/hovercard.css" type="text/css" media="screen"/>
<link rel="stylesheet" href="view/theme/frio/css/font-awesome.custom.css" type="text/css" media="screen"/>
-<!--
-<link rel="shortcut icon" href="images/friendica-32.png" />
-<link rel="apple-touch-icon" href="images/friendica-128.png"/>
--->
<link rel="shortcut icon" href="{{$shortcut_icon}}" />
<link rel="apple-touch-icon" href="{{$touch_icon}}"/>
<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/js/addon-hooks.js" ></script>
-{{if is_array($addon_hooks)}}
-{{foreach $addon_hooks as $addon_hook}}
-<script type="text/javascript" src="addon/{{$addon_hook}}/{{$addon_hook}}.js"></script>
-{{/foreach}}
-{{/if}}
<script type="text/javascript" src="view/js/main.js"></script>
<script type="text/javascript" src="view/theme/frio/frameworks/bootstrap/js/bootstrap.min.js"></script>