and save your changes.
+9. (Optional) Reverse-proxying and HTTPS
+
+Friendica looks for some well-known HTTP headers indicating a reverse-proxy
+terminating an HTTPS connection. While the standard from RFC 7239 specifies
+the use of the `Forwaded` header.
+
+ Forwarded: for=192.0.2.1; proto=https; by=192.0.2.2
+
+Friendica also supports a number on non-standard headers in common use.
+
+
+ X-Forwarded-Proto: https
+
+ Front-End-Https: on
+
+ X-Forwarded-Ssl: on
+
+It is however preferable to use the standard approach if configuring a new server.
+In Nginx, this can be done as follows (assuming Friendica runs on port 8080).
+
+ location / {
+ if ( $scheme != https ) { # Force Redirect to HTTPS
+ return 302 https://$host$uri;
+ }
+ proxy_pass http://localhost:8080;
+ proxy_redirect off;
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Forwarded "for=$proxy_add_x_forwarded_for; proto=$scheme";
+ }
-
#####################################################################
If things don't work...
$this->scheme = 'http';
- if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS'])
- $this->scheme = 'https';
- elseif(x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443))
+ if((x($_SERVER,'HTTPS') && $_SERVER['HTTPS']) ||
+ (x($_SERVER['HTTP_FORWARDED']) && preg_match("/proto=https/", $_SERVER['HTTP_FORWARDED'])) ||
+ (x($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') ||
+ (x($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') ||
+ (x($_SERVER['FRONT_END_HTTPS']) && $_SERVER['FRONT_END_HTTPS'] == 'on') ||
+ (x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443)) // XXX: reasonable assumption, but isn't this hardcoding too much?
+ ) {
$this->scheme = 'https';
+ }
if(x($_SERVER,'SERVER_NAME')) {
$this->hostname = $_SERVER['SERVER_NAME'];
$o .= replace_macros($tpl, array(
- '$dest_url' => $dest_url,
- '$logout' => t('Logout'),
- '$login' => t('Login'),
+ '$dest_url' => $dest_url,
+ '$logout' => t('Logout'),
+ '$login' => t('Login'),
- '$lname' => array('username', t('Nickname or Email address: ') , '', ''),
+ '$lname' => array('username', t('Nickname or Email: ') , '', ''),
'$lpassword' => array('password', t('Password: '), '', ''),
'$lremember' => array('remember', t('Remember me'), 0, ''),
- '$openid' => !$noid,
- '$lopenid' => array('openid_url', t('Or login using OpenID: '),'',''),
+ '$openid' => !$noid,
+ '$lopenid' => array('openid_url', t('Or login using OpenID: '),'',''),
- '$hiddens' => $hiddens,
+ '$hiddens' => $hiddens,
- '$register' => $reg,
+ '$register' => $reg,
'$lostpass' => t('Forgot your password?'),
'$lostlink' => t('Password Reset'),
The second is the messages which could for various reasons not being delivered.
They will be resend later.
You can have a quick glance into that second queus in the "Inspect Queue" section of the admin panel.
+If you have activated the background workers, there might be a third number representing the count of jobs queued for the workers.
+
Then you get an overview of the accounts on your node, which can be moderated in the "Users" section of the panel.
As well as an overview of the currently active addons
The list is linked, so you can have quick access to the plugin settings.
Die zweite Zahl gibt die Anzahl von Nachrichten an, die nicht zugestellt werden konnten.
Die Zustellung wird zu einem späteren Zeitpunkt noch einmal versucht.
Unter dem Punkt "Warteschlange Inspizieren" kannst du einen schnellen Blick auf die zweite Warteschlange werfen.
+Solltest du für die Hintergrundprozesse die Worker aktiviert haben, könntest du eine dritte Zahl angezeigt bekommen.
+Diese repräsentiert die Anzahl der Aufgaben, die die Worker noch vor sich haben.
+
Des weiteren findest du eine Übersicht über die Accounts auf dem Friendica Knoten, die unter dem Punkt "Nutzer" moderiert werden können.
Sowie eine Liste der derzeit aktivierten Addons.
Diese Liste ist verlinkt, so dass du schnellen Zugriff auf die Informationsseiten der einzelnen Addons hast.
dbesc(normalise_link($url)), intval($uid), dbesc($profile["network"]));
// Is the contact present for the user in a different network? (Can happen with OStatus and the "Statusnet" addon)
- if (!count($r) AND !isset($profile))
+ if (!$r)
$r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `thumb`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `bd` AS `birthday`, `self`
FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d",
dbesc(normalise_link($url)), intval($uid));
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
- if (!count($r) AND !isset($profile))
+ if (!$r)
$r = q("SELECT `id`, 0 AS `cid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `thumb`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `bd` AS `birthday`, 0 AS `self`
FROM `contact` WHERE `nurl` = '%s' AND `uid` = 0",
dbesc(normalise_link($url)));
// Fetch the data from the gcontact table
- if (!count($r) AND !isset($profile))
+ if (!$r)
$r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `community` AS `forum`, 0 AS `prv`, `community`, `birthday`, 0 AS `self`
FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
$r = q("select count(*) as total from queue where 1");
$queue = (($r) ? $r[0]['total'] : 0);
+ if (get_config('system','worker')) {
+ $r = q("select count(*) as total from workerqueue where 1");
+ $workerqueue = (($r) ? $r[0]['total'] : 0);
+ } else {
+ $workerqueue = 0;
+ }
+
// We can do better, but this is a quick queue status
- $queues = array('label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue);
+ $queues = array('label' => t('Message queues'), 'deliverq' => $deliverq, 'queue' => $queue, 'workerq' => $workerqueue);
$t = get_markup_template("admin_summary.tpl");
$previewing = (($preview) ? ' preview ' : '');
+ $edited = false;
+ if (strcmp($item['created'], $item['edited'])<>0) {
+ $edited = array(
+ 'label' => t('This entry was edited'),
+ 'date' => datetime_convert('UTC', date_default_timezone_get(), $item['edited'], 'r'),
+ 'relative' => relative_date($item['edited'])
+ );
+ }
+
if($mode === 'network') {
$profile_owner = local_user();
$page_writeable = true;
else
$return_url = $_SESSION['return_url'] = $a->query_string;
- load_contact_links(local_user());
-
$cb = array('items' => $items, 'mode' => $mode, 'update' => $update, 'preview' => $preview);
call_hooks('conversation_start',$cb);
$wallwall = 'wallwall_item.tpl';
$hide_comments_tpl = get_markup_template('hide_comments.tpl');
- $alike = array();
- $dlike = array();
+ $conv_responses = array(
+ 'like' => array('title' => t('Likes','title')), 'dislike' => array('title' => t('Dislikes','title')),
+ 'attendyes' => array('title' => t('Attending','title')), 'attendno' => array('title' => t('Not attending','title')), 'attendmaybe' => array('title' => t('Might attend','title'))
+ );
// array with html for each thread (parent+comments)
$sparkle = '';
if($mode === 'search' || $mode === 'community') {
- if(((activity_match($item['verb'],ACTIVITY_LIKE)) || (activity_match($item['verb'],ACTIVITY_DISLIKE)))
+ if(((activity_match($item['verb'],ACTIVITY_LIKE))
+ || (activity_match($item['verb'],ACTIVITY_DISLIKE))
+ || activity_match($item['verb'],ACTIVITY_ATTEND)
+ || activity_match($item['verb'],ACTIVITY_ATTENDNO)
+ || activity_match($item['verb'],ACTIVITY_ATTENDMAYBE))
&& ($item['id'] != $item['parent']))
continue;
$nickname = $item['nickname'];
$comments[$item['parent']] = 0; // avoid notices later on
}
- // map all the like/dislike activities for each parent item
+ // map all the like/dislike/attendance activities for each parent item
// Store these in the $alike and $dlike arrays
foreach($items as $item) {
- like_puller($a,$item,$alike,'like');
- like_puller($a,$item,$dlike,'dislike');
+ builtin_activity_puller($item, $conv_responses);
}
$comments_collapsed = false;
// We've already parsed out like/dislike for special treatment. We can ignore them now
if(((activity_match($item['verb'],ACTIVITY_LIKE))
- || (activity_match($item['verb'],ACTIVITY_DISLIKE)))
+ || (activity_match($item['verb'],ACTIVITY_DISLIKE)
+ || activity_match($item['verb'],ACTIVITY_ATTEND)
+ || activity_match($item['verb'],ACTIVITY_ATTENDNO)
+ || activity_match($item['verb'],ACTIVITY_ATTENDMAYBE)))
&& ($item['id'] != $item['parent']))
continue;
'tagger' => t("add tag"),
'classtagger' => "",
);
+
+ $r = q("SELECT `ignored` FROM `thread` WHERE `uid` = %d AND `iid` = %d LIMIT 1",
+ intval($item['uid']),
+ intval($item['id'])
+ );
+ if (count($r)) {
+ $ignore = array(
+ 'do' => t("ignore thread"),
+ 'undo' => t("unignore thread"),
+ 'toggle' => t("toggle ignore status"),
+ 'classdo' => (($r[0]['ignored']) ? "hidden" : ""),
+ 'classundo' => (($r[0]['ignored']) ? "" : "hidden"),
+ 'ignored' => t('ignored'),
+ );
+ }
+ $tagger = '';
+ if(feature_enabled($profile_owner,'commtag')) {
+ $tagger = array(
+ 'add' => t("add tag"),
+ 'class' => "",
+ );
+ }
}
$filer = t("save to folder");
}
else
$profile_avatar = $item['author-avatar'];
- $like = ((x($alike,$item['uri'])) ? format_like($alike[$item['uri']],$alike[$item['uri'] . '-l'],'like',$item['uri']) : '');
- $dislike = ((x($dlike,$item['uri'])) ? format_like($dlike[$item['uri']],$dlike[$item['uri'] . '-l'],'dislike',$item['uri']) : '');
+ $like = ((x($conv_responses['like'],$item['uri'])) ? format_like($conv_responses['like'][$item['uri']],$conv_responses['like'][$item['uri'] . '-l'],'like',$item['uri']) : '');
+ $dislike = ((x($conv_responses['dislike'],$item['uri'])) ? format_like($conv_responses['dislike'][$item['uri']],$conv_responses['dislike'][$item['uri'] . '-l'],'dislike',$item['uri']) : '');
+
+ // process action responses - e.g. like/dislike/attend/agree/whatever
+ $response_verbs = array('like');
+ if(feature_enabled($profile_owner,'dislike'))
+ $response_verbs[] = 'dislike';
+ if($item['object-type'] === ACTIVITY_OBJ_EVENT) {
+ $response_verbs[] = 'attendyes';
+ $response_verbs[] = 'attendno';
+ $response_verbs[] = 'attendmaybe';
+ if($page_writeable) {
+ $isevent = true;
+ $attend = array( t('I will attend'), t('I will not attend'), t('I might attend'));
+ }
+ }
+ $responses = get_responses($conv_responses,$response_verbs,'',$item);
$locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => '');
call_hooks('render_location',$locate);
'body' => $body_e,
'text' => $text_e,
'id' => $item['item_id'],
+ 'isevent' => $isevent,
+ 'attend' => $attend,
'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])),
'olinktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['owner-link'])) ? $item['owner-link'] : $item['url'])),
'to' => t('to'),
'osparkle' => $osparkle,
'sparkle' => $sparkle,
'title' => $title_e,
+ 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
'ago' => (($item['app']) ? sprintf( t('%s from %s'),relative_date($item['created']),$item['app']) : relative_date($item['created'])),
+ 'app' => $item['app'],
+ 'created' => relative_date($item['created']),
'lock' => $lock,
'location' => $location_e,
'indent' => $indent,
'edpost' => $edpost,
'isstarred' => $isstarred,
'star' => $star,
- 'filer' => $filer,
+ 'ignore' => ((feature_enabled($profile_owner,'ignore_posts')) ? $ignore : ''),
+ 'tagger' => $tagger,
+ 'filer' => ((feature_enabled($profile_owner,'filing')) ? $filer : ''),
'drop' => $drop,
'vote' => $likebuttons,
+ 'responses' => $responses,
'like' => $like,
'dislike' => $dislike,
+ 'switchcomment' => t('Comment'),
'comment' => $comment,
'previewing' => $previewing,
'wait' => t('Please wait'),
+ 'edited' => $edited,
+ 'network' => $item["item_network"],
+ 'network_name' => network_to_name($item['network'], $profile_link),
);
$to_name_e = $message['name'];
}
+ $contact = get_contact_details_by_url($message['from-url']);
+ if (isset($contact["thumb"]))
+ $from_photo = $contact["thumb"];
+ else
+ $from_photo = $message['from-photo'];
+
$mails[] = array(
'id' => $message['id'],
'from_name' => $from_name_e,
'from_url' => $from_url,
'sparkle' => $sparkle,
- 'from_photo' => proxy_url($message['from-photo'], false, PROXY_SIZE_THUMB),
+ 'from_photo' => proxy_url($from_photo, false, PROXY_SIZE_THUMB),
'subject' => $subject_e,
'body' => $body_e,
'delete' => t('Delete message'),
$to_name_e = $rr['name'];
}
+ $contact = get_contact_details_by_url($rr['url']);
+ if (isset($contact["thumb"]))
+ $from_photo = $contact["thumb"];
+ else
+ $from_photo = (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']);
+
$rslt .= replace_macros($tpl, array(
'$id' => $rr['id'],
'$from_name' => $participants,
'$from_url' => (($rr['network'] === NETWORK_DFRN) ? 'redir/' . $rr['contact-id'] : $rr['url']),
'$sparkle' => ' sparkle',
- '$from_photo' => (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']),
+ '$from_photo' => proxy_url($from_photo, false, PROXY_SIZE_THUMB),
'$subject' => $subject_e,
'$delete' => t('Delete conversation'),
'$body' => $body_e,
// search terms header
if(x($_GET,'search')) {
$a->page['content'] .= replace_macros(get_markup_template("section_title.tpl"),array(
- '$title' => sprintf( t('Search Results For: %s'), $search)
+ '$title' => sprintf( t('Results for: %s'), $search)
));
}
* 'message' => notification message. "{0}" will be replaced by subject name
**/
function xmlize($n){
- $n['photo'] = proxy_url($n['photo'], false, PROXY_SIZE_MICRO);
+
+ $contact = get_contact_details_by_url($n['url']);
+ if (isset($contact["thumb"]))
+ $n['photo'] = proxy_url($contact["thumb"], false, PROXY_SIZE_MICRO);
+ else
+ $n['photo'] = proxy_url($n['photo'], false, PROXY_SIZE_MICRO);
$n['message'] = html_entity_decode($n['message'], ENT_COMPAT | ENT_HTML401, "UTF-8");
$n['name'] = html_entity_decode($n['name'], ENT_COMPAT | ENT_HTML401, "UTF-8");
}
}
if(x($_GET,'remove') && $search) {
- q("delete from `search` where `uid` = %d and `term` = '%s' limit 1",
+ q("DELETE FROM `search` WHERE `uid` = %d AND `term` = '%s' LIMIT 1",
intval(local_user()),
dbesc($search)
);
if($tag)
$title = sprintf( t('Items tagged with: %s'), $search);
else
- $title = sprintf( t('Search results for: %s'), $search);
+ $title = sprintf( t('Results for: %s'), $search);
$o .= replace_macros(get_markup_template("section_title.tpl"),array(
'$title' => $title
$profile_link = zrl($profile_link);
// Don't rely on the author-avatar. It is better to use the data from the contact table
- $author_contact = get_contact_details_by_url($item['author-link'], $profile_owner);
+ $author_contact = get_contact_details_by_url($item['author-link'], $conv->get_profile_owner());
if ($author_contact["thumb"])
$profile_avatar = $author_contact["thumb"];
else
<dl>
<dt>{{$queues.label}}</dt>
- <dd>{{$queues.deliverq}} - <a href="{{$baseurl}}/admin/queue">{{$queues.queue}}</a></dd>
+ <dd>{{$queues.deliverq}} - <a href="{{$baseurl}}/admin/queue">{{$queues.queue}}</a>{{if $queues.workerq}} - {{$queues.workerq}}{{/if}}</dd>
</dl>
<dl>
<dt>{{$pending.0}}</dt>
$(".fbrowser").remove();
});
- // Add Colorbox for viewing Network page images
+ // Clear bs modal on close
+ // We need this to prevent that the modal displays old content
+ $('body').on('hidden.bs.modal', '#jot-modal', function () {
+ // restore cached jot at its hidden position ("#jot-content")
+ $("#jot-content").append(jotcache);
+ // clear the jotcache
+ jotcache = ''
+ });
+
+ // Add Colorbox for viewing Network page images
//var cBoxClasses = new Array();
$("body").on("click", ".wall-item-body a img", function(){
var aElem = $(this).parent();
};
/**
- * @brief Add first h3 element as modal title
+ * @brief Add first element with the class "heading" as modal title
*
* Note: this should be really done in the template
* and is the solution where we havent done it until this
// clear the text of the title
//$("#modal-title").empty();
- // hide the first h3 child element of the modal body
+ // hide the first element with the class "heading" of the modal body
$("#modal-body .heading").first().hide();
- // get the text of the first element with heading class
+ // get the text of the first element with "heading" class
var title = $("#modal-body .heading").first().text();
// and append it to modal title
{
modal.show();
- //Get first h3 element and use it as title
+ //Get first element with the class "heading"
+ //and use it as title
loadModalTitle();
}
});
// function to load the html from the edit post page into
// the jot modal
function editpost(url) {
+ // next to normel posts the post can be an event post. The event posts don't
+ // use the normal Jot modal. For event posts we will use a normal modal
+ // But first we have to test if the url links to an event. So we will split up
+ // the url in its parts
+ var splitURL = parseUrl(url);
+ // Test if in the url path containing "events/event". If the path containing this
+ // expression then we will call the addToModal function and exit this function at
+ // this point
+ if (splitURL.path.indexOf('events/event') > -1) {
+ addToModal(splitURL.path);
+ return;
+ }
+
var modal = $('#jot-modal').modal();
var url = url + " #profile-jot-form";
+
//var rand_num = random_digits(12);
$(".jot-nav #jot-perms-lnk").parent("li").hide();
- // rename the the original div jot-preview-content because the edit function
- // does load the content for the modal from another source and preview won't work
- // if this div would exist twice
- // $("#jot-content #profile-jot-form").attr("id","#profile-jot-form-renamed");
- // $("#jot-content #jot-preview-content").attr("id","#jot-preview-content-renamed");
-
// For editpost we load the modal html form the edit page. So we would have two jot forms in
- // the page html. To avoid js conflicts we move the original jot to the end of the page
- // so the editpost jot would be the first jot in html structure.
- // After closing the modal we move the original jot back to it's orginal position in the html structure.
- //
- // Note: For now it seems to work but this isn't optimal because we have doubled ID names for the jot div's.
- // We need to have a better solution for this in the future.
- $("section #jot-content #profile-jot-form").appendTo("footer #cache-container");
+ // the page html. To avoid js conflicts we store the original jot in the variable jotcache.
+ // After closing the modal original jot should be restored at its orginal position in the html structure.
+ jotcache = $("#jot-content > #profile-jot-form");
+
+ // remove the original Jot as long as the edit Jot is open
+ jotcache.remove();
+
+ // add the class "edit" to the modal to have some kind of identifier to
+ // have the possibility to e.g. put special event-listener
+ $("#jot-modal").addClass("edit-jot");
jotreset();
var type = $(responseText).find("#profile-jot-form input[name='type']").val();
if(type === "wall-comment" || type === "remote-comment")
{
+ // hide title and category input fields because we don't
$("#profile-jot-form #jot-title-wrap").hide();
$("#profile-jot-form #jot-category-wrap").hide();
}
function jotreset() {
// Clear bs modal on close
// We need this to prevent that the modal displays old content
- $('body').on('hidden.bs.modal', '#jot-modal', function () {
+ $('body').on('hidden.bs.modal', '#jot-modal.edit-jot', function () {
$(this).removeData('bs.modal');
$(".jot-nav #jot-perms-lnk").parent("li").show();
$("#profile-jot-form #jot-title-wrap").show();
// $( "#profile-jot-form input[name='type']" ).val("wall");
// $( "#profile-jot-form input[name='post_id']" ).val("");
// $( "#profile-jot-form input[name='post_id_random']" ).val(rand_num);
- $("#jot-modal-body").empty();
- // rename the div #jot-preview-content-renamed back to it's original
- // name. Have a look at function editpost() for further explanation
- //$("#jot-content #profile-jot-form-renamed").attr("id","#profile-jot-form");
- //$("#jot-content #jot-preview-content-renamed").attr("id","#jot-preview-content");
-
- // Move the original jot back to it's old place in the html structure
- // For explaination have a look at function editpost()
- $("footer #cache-container #profile-jot-form").appendTo("section #jot-content");
- });
+ // remove the "edit-jot" class so we can the standard behavior on close
+ $("#jot-modal.edit-jot").removeClass("edit-jot");
+ $("#jot-modal-body").empty();
+ });
}
// Give the active "jot-nav" list element the class "active"
+
+var jotcache = ''; //The jot cache. We use it as cache to restore old/original jot content
+
$(document).ready(function(){
//fade in/out based on scrollTop value
$(window).scroll(function () {
}
function jotShare(id) {
-// if ($('#jot-popup').length != 0) $('#jot-popup').show();
-//
-// $('#like-rotator-' + id).show();
-// $.get('share/' + id, function(data) {
-// if (!editor) $("#profile-jot-text").val("");
-// initEditor(function(){
-// addeditortext(data);
-// $('#like-rotator-' + id).hide();
-// $(window).scrollTop(0);
-// });
-//
-// });
-
$.get('share/' + id, function(data) {
- if (!editor) $("#profile-jot-text").val("");
+ // remove the former content of the text input
+ $("#profile-jot-text").val("");
initEditor(function(){
- addeditortext(data);
+ addeditortext(data);
});
});
function jotShow() {
var modal = $('#jot-modal').modal();
- var jot = $("#profile-jot-form");
-
- // Clear bs modal on close
- // We need this to prevent that the modal displays old content
- $('body').on('hidden.bs.modal', '#jot-modal', function () {
- $(this).removeData('bs.modal');
- $("#jot-content").append(jot);
- });
+ jotcache = $("#profile-jot-form");
modal
.find('#jot-modal-body')
- .append(jot)
+ .append(jotcache)
.modal.show
;
}