diff --git a/doc/Addons.md b/doc/Addons.md index 2465db7307..011bca0737 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -67,55 +67,67 @@ $b can be called anything you like. 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 - -Make sure your JavaScript addon file (addon/*addon_name*/*addon_name*.js) is listed in the document response. - -In your addon install function, add: +If your addon requires adding a stylesheet on all pages of Friendica, add the following hook: ```php -Addon::registerHook('template_vars', __FILE__, '_template_vars'); -``` - -In your addon uninstall function, add: - -```php -Addon::unregisterHook('template_vars', __FILE__, '_template_vars'); -``` - -Then, add your addon name to the *addon_hooks* template variable array: - -```php -function _template_vars($a, &$arr) +function _install() { - if (!array_key_exists('addon_hooks', $arr['vars'])) - { - $arr['vars']['addon_hooks'] = array(); - } - $arr['vars']['addon_hooks'][] = ""; + Addon::registerHook('head', __FILE__, '_head'); + ... +} + + +function _head(App $a) +{ + $a->registerStylesheet(__DIR__ . '/relative/path/to/addon/stylesheet.css'); } ``` -### JavaScript part +`__DIR__` is the folder path of your addon. -Register your addon hooks in file `addon/*addon_name*/*addon_name*.js`. +## JavaScript + +### Global scripts + +If your addon requires adding a script on all pages of Friendica, add the following hook: + + +```php +function _install() +{ + Addon::registerHook('footer', __FILE__, '_footer'); + ... +} + +function _footer(App $a) +{ + $a->registerFooterScript(__DIR__ . '/relative/path/to/addon/script.js'); +} +``` + +`__DIR__` is the folder path of your addon. + +### JavaScript hooks + +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 @@ -260,6 +272,11 @@ Called after conversion of bbcode to HTML. Called after tag conversion of HTML to bbcode (e.g. remote message posting) `$b` is a string converted text +### head +Called when building the `` sections. +Stylesheets should be registered using this hook. +`$b` is an HTML string of the `` tag. + ### page_header Called after building the page navigation section. `$b` is a string HTML of nav region. @@ -294,6 +311,11 @@ No hook data. 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: @@ -389,14 +411,9 @@ Hook data: 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 @@ -571,6 +588,8 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- ### src/App.php Addon::callHooks('load_config'); + Addon::callHooks('head'); + Addon::callHooks('footer'); ### src/Model/Item.php @@ -704,4 +723,4 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- ### view/js/main.js - callAddonHooks("postprocess_liveupdate"); + document.dispatchEvent(new Event('postprocess_liveupdate')); diff --git a/include/conversation.php b/include/conversation.php index e41b697b54..59ee5ed824 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1091,21 +1091,6 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) '$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 everybody'), - '$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); diff --git a/include/text.php b/include/text.php index 97baee7f60..7013c2c91b 100644 --- a/include/text.php +++ b/include/text.php @@ -1191,9 +1191,6 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) $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); diff --git a/index.php b/index.php index 8b0bd47251..184a195924 100644 --- a/index.php +++ b/index.php @@ -91,7 +91,7 @@ if (!$a->is_backend()) { * 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'])) { @@ -102,7 +102,7 @@ if (x($_SESSION, 'authenticated') && !x($_SESSION, 'language')) { } } -if (x($_SESSION, 'language') && ($_SESSION['language'] !== $lang)) { +if (!empty($_SESSION['language']) && $_SESSION['language'] !== $lang) { $lang = $_SESSION['language']; L10n::loadTranslationTable($lang); } @@ -125,12 +125,12 @@ if (!empty($_GET['zrl']) && $a->mode == App::MODE_NORMAL) { logger("Invalid ZRL parameter " . $_GET['zrl'], LOGGER_DEBUG); header('HTTP/1.1 403 Forbidden'); echo "

403 Forbidden

"; - 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); @@ -149,14 +149,10 @@ if ((x($_GET,'owt')) && $a->mode == App::MODE_NORMAL) { 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', []); @@ -295,11 +291,11 @@ if (strlen($a->module)) { 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']); } @@ -307,11 +303,9 @@ if (strlen($a->module)) { 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.') + ]); } } @@ -326,10 +320,6 @@ if (file_exists($theme_info_file)) { /* 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']); } @@ -405,24 +395,13 @@ if ($a->module_loaded) { * 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 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'] .= ''; -} +$a->initFooter(); /* * now that we've been through the module content, see if the page reported @@ -444,36 +423,9 @@ if ($a->module != 'install' && $a->module != 'maintenance') { 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(); @@ -502,7 +454,7 @@ if (isset($_GET["mode"]) && ($_GET["mode"] == "raw")) { echo substr($target->saveHTML(), 6, -8); - killme(); + exit(); } $page = $a->page; @@ -540,5 +492,3 @@ if (empty($template)) { /// @TODO Looks unsafe (remote-inclusion), is maybe not but Theme::getPathForFile() uses file_exists() but does not escape anything require_once $template; - -killme(); diff --git a/mod/cal.php b/mod/cal.php index bdedaaacfa..ae1060c47a 100644 --- a/mod/cal.php +++ b/mod/cal.php @@ -94,11 +94,6 @@ function cal_content(App $a) '$i18n' => $i18n, ]); - $etpl = get_markup_template('event_end.tpl'); - $a->page['end'] .= replace_macros($etpl, [ - '$baseurl' => System::baseUrl(), - ]); - $mode = 'view'; $y = 0; $m = 0; diff --git a/mod/contacts.php b/mod/contacts.php index 1604f0b660..68dbbd59dd 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -117,12 +117,6 @@ function contacts_init(App $a) '$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) @@ -509,9 +503,6 @@ function contacts_content(App $a, $update = 0) $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()); diff --git a/mod/editpost.php b/mod/editpost.php index 9f4aee31d6..d6493b3c0c 100644 --- a/mod/editpost.php +++ b/mod/editpost.php @@ -57,15 +57,6 @@ function editpost_content(App $a) '$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 everybody'), - '$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'])) { diff --git a/mod/events.php b/mod/events.php index 38b632cf49..d04aac3761 100644 --- a/mod/events.php +++ b/mod/events.php @@ -231,11 +231,6 @@ function events_content(App $a) '$i18n' => $i18n, ]); - $etpl = get_markup_template('event_end.tpl'); - $a->page['end'] .= replace_macros($etpl, [ - '$baseurl' => System::baseUrl(), - ]); - $o = ''; $tabs = ''; // tabs diff --git a/mod/message.php b/mod/message.php index d0a583967b..7dc4217763 100644 --- a/mod/message.php +++ b/mod/message.php @@ -46,12 +46,6 @@ function message_init(App $a) '$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) @@ -199,13 +193,6 @@ function message_content(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 = ''; @@ -344,13 +331,6 @@ function message_content(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:') - ]); - $mails = []; $seen = 0; $unknown = false; diff --git a/mod/profile_photo.php b/mod/profile_photo.php index 567a7f3a25..984ebfed6f 100644 --- a/mod/profile_photo.php +++ b/mod/profile_photo.php @@ -317,7 +317,6 @@ function profile_photo_crop_ui_head(App $a, Image $image) } $a->page['htmlhead'] .= replace_macros(get_markup_template("crophead.tpl"), []); - $a->page['end'] .= replace_macros(get_markup_template("cropend.tpl"), []); $imagecrop = [ 'hash' => $hash, diff --git a/mod/profiles.php b/mod/profiles.php index d951a470d7..76491c553e 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -527,9 +527,6 @@ function profiles_content(App $a) { $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,[ diff --git a/mod/settings.php b/mod/settings.php index 84bc230e30..78fa446ce0 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -982,11 +982,6 @@ function settings_content(App $a) '$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; } diff --git a/mod/videos.php b/mod/videos.php index e00df10a24..a0c9d0d16e 100644 --- a/mod/videos.php +++ b/mod/videos.php @@ -105,12 +105,6 @@ function videos_init(App $a) $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; diff --git a/mod/wallmessage.php b/mod/wallmessage.php index 5606b6feed..5e08420ecb 100644 --- a/mod/wallmessage.php +++ b/mod/wallmessage.php @@ -120,13 +120,6 @@ function wallmessage_content(App $a) { '$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'), diff --git a/src/App.php b/src/App.php index 0dc4b86b95..7622a1a0ce 100644 --- a/src/App.php +++ b/src/App.php @@ -96,6 +96,41 @@ class App 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 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