Add Install Mode

- merged `friendica/develop` to `nupplaPhil/install_mode`
- content merged `mod/install.php` to `src/Class/`
This commit is contained in:
Philipp Holzer 2018-04-28 15:11:01 +02:00
commit 1ab965c944
128 changed files with 34290 additions and 33303 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@ include/jquery-1.4.2.min.js
favicon.* favicon.*
home.html home.html
addon addon
*.orig
*~ *~
robots.txt robots.txt

View file

@ -1,4 +1,10 @@
--- ---
language: php language: php
php: 5.6 ## Friendica supports PHP version >= 5.6
php:
- 5.6
- 7.0
- 7.1
- 7.2
install: composer install install: composer install

View file

@ -232,6 +232,7 @@ define('ACCOUNT_TYPE_RELAY', 4);
* Type of the community page * Type of the community page
* @{ * @{
*/ */
define('CP_NO_INTERNAL_COMMUNITY', -2);
define('CP_NO_COMMUNITY_PAGE', -1); define('CP_NO_COMMUNITY_PAGE', -1);
define('CP_USERS_ON_SERVER', 0); define('CP_USERS_ON_SERVER', 0);
define('CP_GLOBAL_COMMUNITY', 1); define('CP_GLOBAL_COMMUNITY', 1);
@ -1291,7 +1292,7 @@ function get_server()
$server = Config::get("system", "directory"); $server = Config::get("system", "directory");
if ($server == "") { if ($server == "") {
$server = "http://dir.friendica.social"; $server = "https://dir.friendica.social";
} }
return($server); return($server);

View file

@ -321,184 +321,300 @@ 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 01-Apr-2018). Please see the source for details of any hooks not documented above.
index.php: Addon::callHooks('init_1'); ### index.php
index.php: Addon::callHooks('app_menu', $arr);
index.php: Addon::callHooks('page_content_top', $a->page['content']);
index.php: Addon::callHooks($a->module.'_mod_init', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_init', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_post', $_POST);
index.php: Addon::callHooks($a->module.'_mod_afterpost', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_content', $arr);
index.php: Addon::callHooks($a->module.'_mod_aftercontent', $arr);
index.php: Addon::callHooks('page_end', $a->page['content']);
include/api.php: Addon::callHooks('logged_in', $a->user); Addon::callHooks('init_1');
include/api.php: Addon::callHooks('authenticate', $addon_auth); Addon::callHooks('app_menu', $arr);
include/api.php: Addon::callHooks('logged_in', $a->user); Addon::callHooks('page_content_top', $a->page['content']);
Addon::callHooks($a->module.'_mod_init', $placeholder);
Addon::callHooks($a->module.'_mod_init', $placeholder);
Addon::callHooks($a->module.'_mod_post', $_POST);
Addon::callHooks($a->module.'_mod_afterpost', $placeholder);
Addon::callHooks($a->module.'_mod_content', $arr);
Addon::callHooks($a->module.'_mod_aftercontent', $arr);
Addon::callHooks('page_end', $a->page['content']);
include/enotify.php: Addon::callHooks('enotify', $h); ### include/api.php
include/enotify.php: Addon::callHooks('enotify_store', $datarray);
include/enotify.php: Addon::callHooks('enotify_mail', $datarray);
include/enotify.php: Addon::callHooks('check_item_notification', $notification_data);
include/conversation.php: Addon::callHooks('conversation_start', $cb); Addon::callHooks('logged_in', $a->user);
include/conversation.php: Addon::callHooks('render_location', $locate); Addon::callHooks('authenticate', $addon_auth);
include/conversation.php: Addon::callHooks('display_item', $arr); Addon::callHooks('logged_in', $a->user);
include/conversation.php: Addon::callHooks('display_item', $arr);
include/conversation.php: Addon::callHooks('item_photo_menu', $args);
include/conversation.php: Addon::callHooks('jot_tool', $jotplugins);
include/security.php: Addon::callHooks('logged_in', $a->user); ### include/enotify.php
include/text.php: Addon::callHooks('contact_block_end', $arr); Addon::callHooks('enotify', $h);
include/text.php: Addon::callHooks('poke_verbs', $arr); Addon::callHooks('enotify_store', $datarray);
include/text.php: Addon::callHooks('prepare_body_init', $item); Addon::callHooks('enotify_mail', $datarray);
include/text.php: Addon::callHooks('prepare_body_content_filter', $hook_data); Addon::callHooks('check_item_notification', $notification_data);
include/text.php: Addon::callHooks('prepare_body', $hook_data);
include/text.php: Addon::callHooks('prepare_body_final', $hook_data);
include/items.php: Addon::callHooks('page_info_data', $data); ### include/conversation.php
mod/directory.php: Addon::callHooks('directory_item', $arr); Addon::callHooks('conversation_start', $cb);
Addon::callHooks('render_location', $locate);
Addon::callHooks('display_item', $arr);
Addon::callHooks('display_item', $arr);
Addon::callHooks('item_photo_menu', $args);
Addon::callHooks('jot_tool', $jotplugins);
mod/xrd.php: Addon::callHooks('personal_xrd', $arr); ### include/security.php
mod/ping.php: Addon::callHooks('network_ping', $arr); Addon::callHooks('logged_in', $a->user);
mod/parse_url.php: Addon::callHooks("parse_link", $arr); ### include/text.php
mod/manage.php: Addon::callHooks('home_init', $ret); Addon::callHooks('contact_block_end', $arr);
Addon::callHooks('poke_verbs', $arr);
Addon::callHooks('prepare_body_init', $item);
Addon::callHooks('prepare_body_content_filter', $hook_data);
Addon::callHooks('prepare_body', $hook_data);
Addon::callHooks('prepare_body_final', $hook_data);
mod/acl.php: Addon::callHooks('acl_lookup_end', $results); ### include/items.php
mod/network.php: Addon::callHooks('network_content_init', $arr); Addon::callHooks('page_info_data', $data);
mod/network.php: Addon::callHooks('network_tabs', $arr);
mod/friendica.php: Addon::callHooks('about_hook', $o); ### mod/directory.php
mod/subthread.php: Addon::callHooks('post_local_end', $arr);
mod/profiles.php: Addon::callHooks('profile_post', $_POST); Addon::callHooks('directory_item', $arr);
mod/profiles.php: Addon::callHooks('profile_edit', $arr);
mod/settings.php: Addon::callHooks('addon_settings_post', $_POST); ### mod/xrd.php
mod/settings.php: Addon::callHooks('connector_settings_post', $_POST);
mod/settings.php: Addon::callHooks('display_settings_post', $_POST);
mod/settings.php: Addon::callHooks('settings_post', $_POST);
mod/settings.php: Addon::callHooks('addon_settings', $settings_addons);
mod/settings.php: Addon::callHooks('connector_settings', $settings_connectors);
mod/settings.php: Addon::callHooks('display_settings', $o);
mod/settings.php: Addon::callHooks('settings_form', $o);
mod/photos.php: Addon::callHooks('photo_post_init', $_POST); Addon::callHooks('personal_xrd', $arr);
mod/photos.php: Addon::callHooks('photo_post_file', $ret);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', intval($item_id));
mod/photos.php: Addon::callHooks('photo_upload_form', $ret);
mod/profile.php: Addon::callHooks('profile_advanced', $o); ### mod/ping.php
mod/home.php: Addon::callHooks('home_init', $ret); Addon::callHooks('network_ping', $arr);
mod/home.php: Addon::callHooks("home_content", $content);
mod/poke.php: Addon::callHooks('post_local_end', $arr); ### mod/parse_url.php
mod/contacts.php: Addon::callHooks('contact_edit_post', $_POST); Addon::callHooks("parse_link", $arr);
mod/contacts.php: Addon::callHooks('contact_edit', $arr);
mod/tagger.php: Addon::callHooks('post_local_end', $arr); ### mod/manage.php
mod/lockview.php: Addon::callHooks('lockview_content', $item); Addon::callHooks('home_init', $ret);
mod/uexport.php: Addon::callHooks('uexport_options', $options); ### mod/acl.php
mod/register.php: Addon::callHooks('register_post', $arr); Addon::callHooks('acl_lookup_end', $results);
mod/register.php: Addon::callHooks('register_form', $arr);
mod/item.php: Addon::callHooks('post_local_start', $_REQUEST); ### mod/network.php
mod/item.php: Addon::callHooks('post_local', $datarray);
mod/item.php: Addon::callHooks('post_local_end', $datarray);
mod/editpost.php: Addon::callHooks('jot_tool', $jotplugins); Addon::callHooks('network_content_init', $arr);
Addon::callHooks('network_tabs', $arr);
src/Network/FKOAuth1.php: Addon::callHooks('logged_in', $a->user); ### mod/friendica.php
src/Render/FriendicaSmartyEngine.php: Addon::callHooks("template_vars", $arr); Addon::callHooks('about_hook', $o);
src/Model/Item.php: Addon::callHooks('post_local', $item); ### mod/subthread.php
src/Model/Item.php: Addon::callHooks('post_remote', $item);
src/Model/Item.php: Addon::callHooks('post_local_end', $posted_item);
src/Model/Item.php: Addon::callHooks('post_remote_end', $posted_item);
src/Model/Item.php: Addon::callHooks('tagged', $arr);
src/Model/Item.php: Addon::callHooks('post_local_end', $new_item);
src/Model/Contact.php: Addon::callHooks('contact_photo_menu', $args); Addon::callHooks('post_local_end', $arr);
src/Model/Contact.php: Addon::callHooks('follow', $arr);
src/Model/Profile.php: Addon::callHooks('profile_sidebar_enter', $profile); ### mod/profiles.php
src/Model/Profile.php: Addon::callHooks('profile_sidebar', $arr);
src/Model/Profile.php: Addon::callHooks('profile_tabs', $arr);
src/Model/Profile.php: Addon::callHooks('zrl_init', $arr);
src/Model/Event.php: Addon::callHooks('event_updated', $event['id']); Addon::callHooks('profile_post', $_POST);
src/Model/Event.php: Addon::callHooks("event_created", $event['id']); Addon::callHooks('profile_edit', $arr);
src/Model/User.php: Addon::callHooks('register_account', $uid); ### mod/settings.php
src/Model/User.php: Addon::callHooks('remove_user', $user);
src/Content/Text/BBCode.php: Addon::callHooks('bbcode', $text); Addon::callHooks('addon_settings_post', $_POST);
src/Content/Text/BBCode.php: Addon::callHooks('bb2diaspora', $text); Addon::callHooks('connector_settings_post', $_POST);
Addon::callHooks('display_settings_post', $_POST);
Addon::callHooks('settings_post', $_POST);
Addon::callHooks('addon_settings', $settings_addons);
Addon::callHooks('connector_settings', $settings_connectors);
Addon::callHooks('display_settings', $o);
Addon::callHooks('settings_form', $o);
src/Content/Text/HTML.php: Addon::callHooks('html2bbcode', $message); ### mod/photos.php
src/Content/Smilies.php: Addon::callHooks('smilie', $params); Addon::callHooks('photo_post_init', $_POST);
Addon::callHooks('photo_post_file', $ret);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', intval($item_id));
Addon::callHooks('photo_upload_form', $ret);
src/Content/Feature.php: Addon::callHooks('isEnabled', $arr); ### mod/profile.php
src/Content/Feature.php: Addon::callHooks('get', $arr);
src/Content/ContactSelector.php: Addon::callHooks('network_to_name', $nets); Addon::callHooks('profile_advanced', $o);
src/Content/ContactSelector.php: Addon::callHooks('gender_selector', $select);
src/Content/ContactSelector.php: Addon::callHooks('sexpref_selector', $select);
src/Content/ContactSelector.php: Addon::callHooks('marital_selector', $select);
src/Content/OEmbed.php: Addon::callHooks('oembed_fetch_url', $embedurl, $j); ### mod/home.php
src/Content/Nav.php: Addon::callHooks('page_header', $a->page['nav']); Addon::callHooks('home_init', $ret);
src/Content/Nav.php: Addon::callHooks('nav_info', $nav); Addon::callHooks("home_content", $content);
src/Worker/Directory.php: Addon::callHooks('globaldir_update', $arr); ### mod/poke.php
src/Worker/Notifier.php: Addon::callHooks('notifier_end', $target_item);
src/Worker/Queue.php: Addon::callHooks('queue_predeliver', $r);
src/Worker/Queue.php: Addon::callHooks('queue_deliver', $params);
src/Module/Login.php: Addon::callHooks('authenticate', $addon_auth); Addon::callHooks('post_local_end', $arr);
src/Module/Login.php: Addon::callHooks('login_hook', $o);
src/Module/Logout.php: Addon::callHooks("logging_out");
src/Object/Post.php: Addon::callHooks('render_location', $locate); ### mod/contacts.php
src/Object/Post.php: Addon::callHooks('display_item', $arr);
src/Core/ACL.php: Addon::callHooks('contact_select_options', $x); Addon::callHooks('contact_edit_post', $_POST);
src/Core/ACL.php: Addon::callHooks($a->module.'_pre_'.$selname, $arr); Addon::callHooks('contact_edit', $arr);
src/Core/ACL.php: Addon::callHooks($a->module.'_post_'.$selname, $o);
src/Core/ACL.php: Addon::callHooks($a->module.'_pre_'.$selname, $arr);
src/Core/ACL.php: Addon::callHooks($a->module.'_post_'.$selname, $o);
src/Core/ACL.php: Addon::callHooks('jot_networks', $jotnets);
src/Core/Worker.php: Addon::callHooks("proc_run", $arr); ### mod/tagger.php
src/Util/Emailer.php: Addon::callHooks('emailer_send_prepare', $params); Addon::callHooks('post_local_end', $arr);
src/Util/Emailer.php: Addon::callHooks("emailer_send", $hookdata);
src/Util/Map.php: Addon::callHooks('generate_map', $arr); ### mod/lockview.php
src/Util/Map.php: Addon::callHooks('generate_named_map', $arr);
src/Util/Map.php: Addon::callHooks('Map::getCoordinates', $arr);
src/Util/Network.php: Addon::callHooks('avatar_lookup', $avatar); Addon::callHooks('lockview_content', $item);
src/Util/ParseUrl.php: Addon::callHooks("getsiteinfo", $siteinfo); ### mod/uexport.php
src/Protocol/DFRN.php: Addon::callHooks('atom_feed_end', $atom); Addon::callHooks('uexport_options', $options);
src/Protocol/DFRN.php: Addon::callHooks('atom_feed_end', $atom);
### mod/register.php
Addon::callHooks('register_post', $arr);
Addon::callHooks('register_form', $arr);
### mod/item.php
Addon::callHooks('post_local_start', $_REQUEST);
Addon::callHooks('post_local', $datarray);
Addon::callHooks('post_local_end', $datarray);
### mod/editpost.php
Addon::callHooks('jot_tool', $jotplugins);
### src/Network/FKOAuth1.php
Addon::callHooks('logged_in', $a->user);
### src/Render/FriendicaSmartyEngine.php
Addon::callHooks("template_vars", $arr);
### src/Model/Item.php
Addon::callHooks('post_local', $item);
Addon::callHooks('post_remote', $item);
Addon::callHooks('post_local_end', $posted_item);
Addon::callHooks('post_remote_end', $posted_item);
Addon::callHooks('tagged', $arr);
Addon::callHooks('post_local_end', $new_item);
### src/Model/Contact.php
Addon::callHooks('contact_photo_menu', $args);
Addon::callHooks('follow', $arr);
### src/Model/Profile.php
Addon::callHooks('profile_sidebar_enter', $profile);
Addon::callHooks('profile_sidebar', $arr);
Addon::callHooks('profile_tabs', $arr);
Addon::callHooks('zrl_init', $arr);
### src/Model/Event.php
Addon::callHooks('event_updated', $event['id']);
Addon::callHooks("event_created", $event['id']);
### src/Model/User.php
Addon::callHooks('register_account', $uid);
Addon::callHooks('remove_user', $user);
### src/Content/Text/BBCode.php
Addon::callHooks('bbcode', $text);
Addon::callHooks('bb2diaspora', $text);
### src/Content/Text/HTML.php
Addon::callHooks('html2bbcode', $message);
### src/Content/Smilies.php
Addon::callHooks('smilie', $params);
### src/Content/Feature.php
Addon::callHooks('isEnabled', $arr);
Addon::callHooks('get', $arr);
### src/Content/ContactSelector.php
Addon::callHooks('network_to_name', $nets);
Addon::callHooks('gender_selector', $select);
Addon::callHooks('sexpref_selector', $select);
Addon::callHooks('marital_selector', $select);
### src/Content/OEmbed.php
Addon::callHooks('oembed_fetch_url', $embedurl, $j);
### src/Content/Nav.php
Addon::callHooks('page_header', $a->page['nav']);
Addon::callHooks('nav_info', $nav);
### src/Worker/Directory.php
Addon::callHooks('globaldir_update', $arr);
### src/Worker/Notifier.php
Addon::callHooks('notifier_end', $target_item);
### src/Worker/Queue.php
Addon::callHooks('queue_predeliver', $r);
Addon::callHooks('queue_deliver', $params);
### src/Module/Login.php
Addon::callHooks('authenticate', $addon_auth);
Addon::callHooks('login_hook', $o);
### src/Module/Logout.php
Addon::callHooks("logging_out");
### src/Object/Post.php
Addon::callHooks('render_location', $locate);
Addon::callHooks('display_item', $arr);
### src/Core/ACL.php
Addon::callHooks('contact_select_options', $x);
Addon::callHooks($a->module.'_pre_'.$selname, $arr);
Addon::callHooks($a->module.'_post_'.$selname, $o);
Addon::callHooks($a->module.'_pre_'.$selname, $arr);
Addon::callHooks($a->module.'_post_'.$selname, $o);
Addon::callHooks('jot_networks', $jotnets);
### src/Core/Worker.php
Addon::callHooks("proc_run", $arr);
### src/Util/Emailer.php
Addon::callHooks('emailer_send_prepare', $params);
Addon::callHooks("emailer_send", $hookdata);
### src/Util/Map.php
Addon::callHooks('generate_map', $arr);
Addon::callHooks('generate_named_map', $arr);
Addon::callHooks('Map::getCoordinates', $arr);
### src/Util/Network.php
Addon::callHooks('avatar_lookup', $avatar);
### src/Util/ParseUrl.php
Addon::callHooks("getsiteinfo", $siteinfo);
### src/Protocol/DFRN.php
Addon::callHooks('atom_feed_end', $atom);
Addon::callHooks('atom_feed_end', $atom);

View file

@ -2,6 +2,7 @@ Friendica Installation
=============== ===============
We've tried very hard to ensure that Friendica will run on commodity hosting platforms - such as those used to host Wordpress blogs and Drupal websites. We've tried very hard to ensure that Friendica will run on commodity hosting platforms - such as those used to host Wordpress blogs and Drupal websites.
We offer a manual and an automatic installation.
But be aware that Friendica is more than a simple web application. But be aware that Friendica is more than a simple web application.
It is a complex communications system which more closely resembles an email server than a web server. It is a complex communications system which more closely resembles an email server than a web server.
For reliability and performance, messages are delivered in the background and are queued for later delivery when sites are down. For reliability and performance, messages are delivered in the background and are queued for later delivery when sites are down.
@ -79,24 +80,49 @@ In this case find the [mysqld] section in your my.cnf file and add the line :
Restart mysql and you should be fine. Restart mysql and you should be fine.
### Option A: Run the manual installer
### Run the installer
Point your web browser to the new site and follow the instructions. Point your web browser to the new site and follow the instructions.
Please note any error messages and correct these before continuing. Please note any error messages and correct these before continuing.
If you need to specify a port for the connection to the database, you can do so in the host name setting for the database. If you need to specify a port for the connection to the database, you can do so in the host name setting for the database.
*If* the automated installation fails for any reason, check the following: *If* the manual installation fails for any reason, check the following:
* Does ".htconfig.php" exist? If not, edit htconfig.php and change the system settings. Rename to .htconfig.php * Does ".htconfig.php" exist? If not, edit htconfig.php and change the system settings. Rename to .htconfig.php
* Is the database is populated? If not, import the contents of "database.sql" with phpmyadmin or mysql command line. * Is the database is populated? If not, import the contents of "database.sql" with phpmyadmin or the mysql command line.
At this point visit your website again, and register your personal account. At this point visit your website again, and register your personal account.
Registration errors should all be recoverable automatically. Registration errors should all be recoverable automatically.
If you get any *critical* failure at this point, it generally indicates the database was not installed correctly. If you get any *critical* failure at this point, it generally indicates the database was not installed correctly.
You might wish to move/rename .htconfig.php to another name and empty (called 'dropping') the database tables, so that you can start fresh. You might wish to move/rename .htconfig.php to another name and empty (called 'dropping') the database tables, so that you can start fresh.
### Option B: Run the automatic install script
Open the file htconfig.php in the main Friendica directory with a text editor.
Remove the `die('...');` line and edit the lines to suit your installation (MySQL, language, theme etc.).
Then save the file (do not rename it).
Navigate to the main Friendica directory and execute the following command:
bin/console autoinstall
Or if you wish to include all optional checks, execute this statement instead:
bin/console autoinstall -a
At this point visit your website again, and register your personal account.
*If* the automatic installation fails for any reason, check the following:
* Does ".htconfig.php" already exist? If yes, the automatic installation won't start
* Are the settings inside "htconfig.php" correct? If not, edit the file again.
* Is the empty MySQL-database created? If not, create it.
For more information during the installation, you can use this command line option
bin/console autoinstall -v
### Set up the worker ### Set up the worker
Set up a cron job or scheduled task to run the worker once every 5-10 minutes in order to perform background processing. Set up a cron job or scheduled task to run the worker once every 5-10 minutes in order to perform background processing.

View file

@ -4,35 +4,32 @@ Profiles
* [Home](help) * [Home](help)
Friendica has unlimited profiles. Friendica has unlimited profiles.
You may use different profiles to show different "sides of yourself" to different audiences. You may use different profiles to present different aspects of yourself to different audiences.
Default / public profile Default / public profile
--- ---
You always have a profile known as your "default" or "public" profile. You always have a profile known as your "default" or "public" profile.
This profile is always available to the general public and cannot be hidden (there may be rare exceptions on privately run or disconnected sites). This profile is always available to the general public and usually cannot be hidden.
You may, and probably should restrict the information you make available on your public profile. You may, and probably should restrict the personal information you make available on your public profile.
That said, if you want other friends to be able to find you, it helps to have the following information in your public profile: That said, if you want other friends to be able to find you, it helps to have the following information in your public profile:
* Your real name * Your real name
* A photo of **you** * A photo of **you**
* Your location on the planet, at least to a country level. * Your location, preferably at least the country.
Without this basic information, you could get very lonely here. In addition, if you'd like to meet people that share some general interests with you, add some "Public Keywords" to your profile.
Most people (even your best friends) will not try and connect with somebody that has a fake name or doesn't contain a real photo.
In addition, if you'd like to meet people that share some general interests with you, please take a moment and add some "Public Keywords" to your profile.
Such as "music, linux, photography" or whatever. Such as "music, linux, photography" or whatever.
You can add as many keywords as you like. You can add as many keywords as you like.
Your default or public profile is also shown to contacts on other networks, since they do not have the ability to view your private profiles. Your default or public profile is also shown to contacts on other networks, since they do not have the ability to view your private profiles.
Only members of the Friendica network can see alternate/ private profiles. Only members of the Friendica network can see alternate/ private profiles.
Alternate profiles Alternate/ private profiles
--- ---
To create an alternate profile, select "Profiles" from the menu of your Friendica site. To create an alternate profile, select "Profiles" from the menu of your Friendica site.
You may edit an existing profile, change the profile photo, or create a new profile. You may edit an existing profile, change the profile photo, or create a new profile.
You may also create a "clone" of an existing profile if you only wish to change a few items but don't wish to enter all the information again. You may also "clone" your existing profile if you only wish to change a few items but don't wish to enter all the information again.
To assign a profile to specific persons, select the person from your "Contacts" page and click the pencil "Edit" icon. To assign a profile to specific persons, select the person from your "Contacts" page and click the pencil "Edit" icon.
You will find a dropdown box listing the various profiles available. You will find a dropdown box listing the various profiles available.
@ -51,17 +48,13 @@ You may also be able to comment directly on posts from while visiting the other
There are two settings which allow you to publish your profile to a directory and ensure that it can be found by others. There are two settings which allow you to publish your profile to a directory and ensure that it can be found by others.
You can change these through settings on the "Settings" page. You can change these through settings on the "Settings" page.
One setting allows you to publish your profile in the site directory of this Friendica server. One setting allows you to publish your profile in the site directory of this Friendica server.
Another option (this may have been disabled by the site creator) allows you to publish your profile in the "Global Directory". Another option (this may have been disabled by the site admin) allows you to publish your profile in a [Global Directory](Making-Friends.md#the-directories).
This is a mega directory which contains people from many other Friendica installations world-wide.
If you do not wish to be visible to any of these sites, you may leave your profile unpublished. If you do not wish to be visible to any of these directories, do not published your profile.
Although you may have multiple profiles, you only have one profile photo. Although you may have multiple profiles, you only have one profile photo.
This is intentional. This is intentional; it avoids confusion by potentially seeing different profile pictures of a contact depending on what website you visit or conversation you participate in.
In early tests we experimented with different photos for each profile and found it was very confusing for people. You can always can use the free text information boxes within a profile such as "Tell us about yourself" and link other photos for yourself.
They might see a different picture depending on what website they visited or what conversation they were in, and often alerted them to the fact that other people might be able to see different profiles of you than they could see.
(But you can use the rich-text information boxes within a profile such as "Tell us about yourself" and link other photos onto the page.)
Keywords and Directory Search Keywords and Directory Search
--- ---
@ -70,8 +63,7 @@ The search is typically for your nickname or part of your full name.
However this search will also match against other profile fields - such as gender, location, "about", work, and education. However this search will also match against other profile fields - such as gender, location, "about", work, and education.
You may also include "Keywords" in your default profile - which may be used to search for common interests with other members. You may also include "Keywords" in your default profile - which may be used to search for common interests with other members.
You have two sets of keywords available - public and private. You have two sets of keywords available - public and private.
Private keywords are *not* visible to anybody. Private keywords are *not* visible on your profile, but will bring up your profile when matched in a search of the site directory.
You could use these keywords to locate people who share membership in secret societies, or that share a love of fishing (for example) - without making this information visible on your public profile.
Public keywords are used in the friend suggestion tool and although they aren't readily visible, they may be seen by viewing the HTML of your profile page. Public keywords are used in the friend suggestion tool and although they aren't readily visible, they may be seen by viewing the HTML of your profile page.
Directory searches are also able to use "boolean" logic so that you can search for "+lesbian +Florida" and find those who's sexual preference (or keywords) contain the world "lesbian" and that live in Florida. Directory searches are also able to use "boolean" logic so that you can search for "+lesbian +Florida" and find those who's sexual preference (or keywords) contain the world "lesbian" and that live in Florida.
@ -79,7 +71,7 @@ See the section on "Topical Tags" on the [Tags-and-Mentions](help/Tags-and-Menti
On your Contacts page is a link to "Find People with Shared Interests" (unless your site administrator has disabled the global directory). On your Contacts page is a link to "Find People with Shared Interests" (unless your site administrator has disabled the global directory).
This will combine both your public and private keywords, and find people in the global directory who have matching and/or similar keywords. This will combine both your public and private keywords, and find people in the global directory who have matching and/or similar keywords.
(Your private keywords are not identified or stored on the global directory). Private keywords are not identified or stored on the global directory.
The more keywords you provide, the more relevant the search results that are returned. The more keywords you provide, the more relevant the search results that are returned.
These are sorted by relevance. These are sorted by relevance.
You may discover that you are the first person on the list - because you are very likely the most relevant match for your keywords in the directory. You may discover that you are the first person on the list - because you are very likely the most relevant match for your keywords in the directory.

View file

@ -191,184 +191,300 @@ Komplette Liste der Hook-Callbacks
Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Apr-2018 generiert): Bitte schau in die Quellcodes für Details zu Hooks, die oben nicht dokumentiert sind. Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Apr-2018 generiert): Bitte schau in die Quellcodes für Details zu Hooks, die oben nicht dokumentiert sind.
index.php: Addon::callHooks('init_1'); ### index.php
index.php: Addon::callHooks('app_menu', $arr);
index.php: Addon::callHooks('page_content_top', $a->page['content']);
index.php: Addon::callHooks($a->module.'_mod_init', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_init', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_post', $_POST);
index.php: Addon::callHooks($a->module.'_mod_afterpost', $placeholder);
index.php: Addon::callHooks($a->module.'_mod_content', $arr);
index.php: Addon::callHooks($a->module.'_mod_aftercontent', $arr);
index.php: Addon::callHooks('page_end', $a->page['content']);
include/api.php: Addon::callHooks('logged_in', $a->user); Addon::callHooks('init_1');
include/api.php: Addon::callHooks('authenticate', $addon_auth); Addon::callHooks('app_menu', $arr);
include/api.php: Addon::callHooks('logged_in', $a->user); Addon::callHooks('page_content_top', $a->page['content']);
Addon::callHooks($a->module.'_mod_init', $placeholder);
Addon::callHooks($a->module.'_mod_init', $placeholder);
Addon::callHooks($a->module.'_mod_post', $_POST);
Addon::callHooks($a->module.'_mod_afterpost', $placeholder);
Addon::callHooks($a->module.'_mod_content', $arr);
Addon::callHooks($a->module.'_mod_aftercontent', $arr);
Addon::callHooks('page_end', $a->page['content']);
include/enotify.php: Addon::callHooks('enotify', $h); ### include/api.php
include/enotify.php: Addon::callHooks('enotify_store', $datarray);
include/enotify.php: Addon::callHooks('enotify_mail', $datarray);
include/enotify.php: Addon::callHooks('check_item_notification', $notification_data);
include/conversation.php: Addon::callHooks('conversation_start', $cb); Addon::callHooks('logged_in', $a->user);
include/conversation.php: Addon::callHooks('render_location', $locate); Addon::callHooks('authenticate', $addon_auth);
include/conversation.php: Addon::callHooks('display_item', $arr); Addon::callHooks('logged_in', $a->user);
include/conversation.php: Addon::callHooks('display_item', $arr);
include/conversation.php: Addon::callHooks('item_photo_menu', $args);
include/conversation.php: Addon::callHooks('jot_tool', $jotplugins);
include/security.php: Addon::callHooks('logged_in', $a->user); ### include/enotify.php
include/text.php: Addon::callHooks('contact_block_end', $arr); Addon::callHooks('enotify', $h);
include/text.php: Addon::callHooks('poke_verbs', $arr); Addon::callHooks('enotify_store', $datarray);
include/text.php: Addon::callHooks('prepare_body_init', $item); Addon::callHooks('enotify_mail', $datarray);
include/text.php: Addon::callHooks('prepare_body_content_filter', $hook_data); Addon::callHooks('check_item_notification', $notification_data);
include/text.php: Addon::callHooks('prepare_body', $hook_data);
include/text.php: Addon::callHooks('prepare_body_final', $hook_data);
include/items.php: Addon::callHooks('page_info_data', $data); ### include/conversation.php
mod/directory.php: Addon::callHooks('directory_item', $arr); Addon::callHooks('conversation_start', $cb);
Addon::callHooks('render_location', $locate);
Addon::callHooks('display_item', $arr);
Addon::callHooks('display_item', $arr);
Addon::callHooks('item_photo_menu', $args);
Addon::callHooks('jot_tool', $jotplugins);
mod/xrd.php: Addon::callHooks('personal_xrd', $arr); ### include/security.php
mod/ping.php: Addon::callHooks('network_ping', $arr); Addon::callHooks('logged_in', $a->user);
mod/parse_url.php: Addon::callHooks("parse_link", $arr); ### include/text.php
mod/manage.php: Addon::callHooks('home_init', $ret); Addon::callHooks('contact_block_end', $arr);
Addon::callHooks('poke_verbs', $arr);
Addon::callHooks('prepare_body_init', $item);
Addon::callHooks('prepare_body_content_filter', $hook_data);
Addon::callHooks('prepare_body', $hook_data);
Addon::callHooks('prepare_body_final', $hook_data);
mod/acl.php: Addon::callHooks('acl_lookup_end', $results); ### include/items.php
mod/network.php: Addon::callHooks('network_content_init', $arr); Addon::callHooks('page_info_data', $data);
mod/network.php: Addon::callHooks('network_tabs', $arr);
mod/friendica.php: Addon::callHooks('about_hook', $o); ### mod/directory.php
mod/subthread.php: Addon::callHooks('post_local_end', $arr);
mod/profiles.php: Addon::callHooks('profile_post', $_POST); Addon::callHooks('directory_item', $arr);
mod/profiles.php: Addon::callHooks('profile_edit', $arr);
mod/settings.php: Addon::callHooks('addon_settings_post', $_POST); ### mod/xrd.php
mod/settings.php: Addon::callHooks('connector_settings_post', $_POST);
mod/settings.php: Addon::callHooks('display_settings_post', $_POST);
mod/settings.php: Addon::callHooks('settings_post', $_POST);
mod/settings.php: Addon::callHooks('addon_settings', $settings_addons);
mod/settings.php: Addon::callHooks('connector_settings', $settings_connectors);
mod/settings.php: Addon::callHooks('display_settings', $o);
mod/settings.php: Addon::callHooks('settings_form', $o);
mod/photos.php: Addon::callHooks('photo_post_init', $_POST); Addon::callHooks('personal_xrd', $arr);
mod/photos.php: Addon::callHooks('photo_post_file', $ret);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', $foo);
mod/photos.php: Addon::callHooks('photo_post_end', intval($item_id));
mod/photos.php: Addon::callHooks('photo_upload_form', $ret);
mod/profile.php: Addon::callHooks('profile_advanced', $o); ### mod/ping.php
mod/home.php: Addon::callHooks('home_init', $ret); Addon::callHooks('network_ping', $arr);
mod/home.php: Addon::callHooks("home_content", $content);
mod/poke.php: Addon::callHooks('post_local_end', $arr); ### mod/parse_url.php
mod/contacts.php: Addon::callHooks('contact_edit_post', $_POST); Addon::callHooks("parse_link", $arr);
mod/contacts.php: Addon::callHooks('contact_edit', $arr);
mod/tagger.php: Addon::callHooks('post_local_end', $arr); ### mod/manage.php
mod/lockview.php: Addon::callHooks('lockview_content', $item); Addon::callHooks('home_init', $ret);
mod/uexport.php: Addon::callHooks('uexport_options', $options); ### mod/acl.php
mod/register.php: Addon::callHooks('register_post', $arr); Addon::callHooks('acl_lookup_end', $results);
mod/register.php: Addon::callHooks('register_form', $arr);
mod/item.php: Addon::callHooks('post_local_start', $_REQUEST); ### mod/network.php
mod/item.php: Addon::callHooks('post_local', $datarray);
mod/item.php: Addon::callHooks('post_local_end', $datarray);
mod/editpost.php: Addon::callHooks('jot_tool', $jotplugins); Addon::callHooks('network_content_init', $arr);
Addon::callHooks('network_tabs', $arr);
src/Network/FKOAuth1.php: Addon::callHooks('logged_in', $a->user); ### mod/friendica.php
src/Render/FriendicaSmartyEngine.php: Addon::callHooks("template_vars", $arr); Addon::callHooks('about_hook', $o);
src/Model/Item.php: Addon::callHooks('post_local', $item); ### mod/subthread.php
src/Model/Item.php: Addon::callHooks('post_remote', $item);
src/Model/Item.php: Addon::callHooks('post_local_end', $posted_item);
src/Model/Item.php: Addon::callHooks('post_remote_end', $posted_item);
src/Model/Item.php: Addon::callHooks('tagged', $arr);
src/Model/Item.php: Addon::callHooks('post_local_end', $new_item);
src/Model/Contact.php: Addon::callHooks('contact_photo_menu', $args); Addon::callHooks('post_local_end', $arr);
src/Model/Contact.php: Addon::callHooks('follow', $arr);
src/Model/Profile.php: Addon::callHooks('profile_sidebar_enter', $profile); ### mod/profiles.php
src/Model/Profile.php: Addon::callHooks('profile_sidebar', $arr);
src/Model/Profile.php: Addon::callHooks('profile_tabs', $arr);
src/Model/Profile.php: Addon::callHooks('zrl_init', $arr);
src/Model/Event.php: Addon::callHooks('event_updated', $event['id']); Addon::callHooks('profile_post', $_POST);
src/Model/Event.php: Addon::callHooks("event_created", $event['id']); Addon::callHooks('profile_edit', $arr);
src/Model/User.php: Addon::callHooks('register_account', $uid); ### mod/settings.php
src/Model/User.php: Addon::callHooks('remove_user', $user);
src/Content/Text/BBCode.php: Addon::callHooks('bbcode', $text); Addon::callHooks('addon_settings_post', $_POST);
src/Content/Text/BBCode.php: Addon::callHooks('bb2diaspora', $text); Addon::callHooks('connector_settings_post', $_POST);
Addon::callHooks('display_settings_post', $_POST);
Addon::callHooks('settings_post', $_POST);
Addon::callHooks('addon_settings', $settings_addons);
Addon::callHooks('connector_settings', $settings_connectors);
Addon::callHooks('display_settings', $o);
Addon::callHooks('settings_form', $o);
src/Content/Text/HTML.php: Addon::callHooks('html2bbcode', $message); ### mod/photos.php
src/Content/Smilies.php: Addon::callHooks('smilie', $params); Addon::callHooks('photo_post_init', $_POST);
Addon::callHooks('photo_post_file', $ret);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', $foo);
Addon::callHooks('photo_post_end', intval($item_id));
Addon::callHooks('photo_upload_form', $ret);
src/Content/Feature.php: Addon::callHooks('isEnabled', $arr); ### mod/profile.php
src/Content/Feature.php: Addon::callHooks('get', $arr);
src/Content/ContactSelector.php: Addon::callHooks('network_to_name', $nets); Addon::callHooks('profile_advanced', $o);
src/Content/ContactSelector.php: Addon::callHooks('gender_selector', $select);
src/Content/ContactSelector.php: Addon::callHooks('sexpref_selector', $select);
src/Content/ContactSelector.php: Addon::callHooks('marital_selector', $select);
src/Content/OEmbed.php: Addon::callHooks('oembed_fetch_url', $embedurl, $j); ### mod/home.php
src/Content/Nav.php: Addon::callHooks('page_header', $a->page['nav']); Addon::callHooks('home_init', $ret);
src/Content/Nav.php: Addon::callHooks('nav_info', $nav); Addon::callHooks("home_content", $content);
src/Worker/Directory.php: Addon::callHooks('globaldir_update', $arr); ### mod/poke.php
src/Worker/Notifier.php: Addon::callHooks('notifier_end', $target_item);
src/Worker/Queue.php: Addon::callHooks('queue_predeliver', $r);
src/Worker/Queue.php: Addon::callHooks('queue_deliver', $params);
src/Module/Login.php: Addon::callHooks('authenticate', $addon_auth); Addon::callHooks('post_local_end', $arr);
src/Module/Login.php: Addon::callHooks('login_hook', $o);
src/Module/Logout.php: Addon::callHooks("logging_out");
src/Object/Post.php: Addon::callHooks('render_location', $locate); ### mod/contacts.php
src/Object/Post.php: Addon::callHooks('display_item', $arr);
src/Core/ACL.php: Addon::callHooks('contact_select_options', $x); Addon::callHooks('contact_edit_post', $_POST);
src/Core/ACL.php: Addon::callHooks($a->module.'_pre_'.$selname, $arr); Addon::callHooks('contact_edit', $arr);
src/Core/ACL.php: Addon::callHooks($a->module.'_post_'.$selname, $o);
src/Core/ACL.php: Addon::callHooks($a->module.'_pre_'.$selname, $arr);
src/Core/ACL.php: Addon::callHooks($a->module.'_post_'.$selname, $o);
src/Core/ACL.php: Addon::callHooks('jot_networks', $jotnets);
src/Core/Worker.php: Addon::callHooks("proc_run", $arr); ### mod/tagger.php
src/Util/Emailer.php: Addon::callHooks('emailer_send_prepare', $params); Addon::callHooks('post_local_end', $arr);
src/Util/Emailer.php: Addon::callHooks("emailer_send", $hookdata);
src/Util/Map.php: Addon::callHooks('generate_map', $arr); ### mod/lockview.php
src/Util/Map.php: Addon::callHooks('generate_named_map', $arr);
src/Util/Map.php: Addon::callHooks('Map::getCoordinates', $arr);
src/Util/Network.php: Addon::callHooks('avatar_lookup', $avatar); Addon::callHooks('lockview_content', $item);
src/Util/ParseUrl.php: Addon::callHooks("getsiteinfo", $siteinfo); ### mod/uexport.php
src/Protocol/DFRN.php: Addon::callHooks('atom_feed_end', $atom); Addon::callHooks('uexport_options', $options);
src/Protocol/DFRN.php: Addon::callHooks('atom_feed_end', $atom);
### mod/register.php
Addon::callHooks('register_post', $arr);
Addon::callHooks('register_form', $arr);
### mod/item.php
Addon::callHooks('post_local_start', $_REQUEST);
Addon::callHooks('post_local', $datarray);
Addon::callHooks('post_local_end', $datarray);
### mod/editpost.php
Addon::callHooks('jot_tool', $jotplugins);
### src/Network/FKOAuth1.php
Addon::callHooks('logged_in', $a->user);
### src/Render/FriendicaSmartyEngine.php
Addon::callHooks("template_vars", $arr);
### src/Model/Item.php
Addon::callHooks('post_local', $item);
Addon::callHooks('post_remote', $item);
Addon::callHooks('post_local_end', $posted_item);
Addon::callHooks('post_remote_end', $posted_item);
Addon::callHooks('tagged', $arr);
Addon::callHooks('post_local_end', $new_item);
### src/Model/Contact.php
Addon::callHooks('contact_photo_menu', $args);
Addon::callHooks('follow', $arr);
### src/Model/Profile.php
Addon::callHooks('profile_sidebar_enter', $profile);
Addon::callHooks('profile_sidebar', $arr);
Addon::callHooks('profile_tabs', $arr);
Addon::callHooks('zrl_init', $arr);
### src/Model/Event.php
Addon::callHooks('event_updated', $event['id']);
Addon::callHooks("event_created", $event['id']);
### src/Model/User.php
Addon::callHooks('register_account', $uid);
Addon::callHooks('remove_user', $user);
### src/Content/Text/BBCode.php
Addon::callHooks('bbcode', $text);
Addon::callHooks('bb2diaspora', $text);
### src/Content/Text/HTML.php
Addon::callHooks('html2bbcode', $message);
### src/Content/Smilies.php
Addon::callHooks('smilie', $params);
### src/Content/Feature.php
Addon::callHooks('isEnabled', $arr);
Addon::callHooks('get', $arr);
### src/Content/ContactSelector.php
Addon::callHooks('network_to_name', $nets);
Addon::callHooks('gender_selector', $select);
Addon::callHooks('sexpref_selector', $select);
Addon::callHooks('marital_selector', $select);
### src/Content/OEmbed.php
Addon::callHooks('oembed_fetch_url', $embedurl, $j);
### src/Content/Nav.php
Addon::callHooks('page_header', $a->page['nav']);
Addon::callHooks('nav_info', $nav);
### src/Worker/Directory.php
Addon::callHooks('globaldir_update', $arr);
### src/Worker/Notifier.php
Addon::callHooks('notifier_end', $target_item);
### src/Worker/Queue.php
Addon::callHooks('queue_predeliver', $r);
Addon::callHooks('queue_deliver', $params);
### src/Module/Login.php
Addon::callHooks('authenticate', $addon_auth);
Addon::callHooks('login_hook', $o);
### src/Module/Logout.php
Addon::callHooks("logging_out");
### src/Object/Post.php
Addon::callHooks('render_location', $locate);
Addon::callHooks('display_item', $arr);
### src/Core/ACL.php
Addon::callHooks('contact_select_options', $x);
Addon::callHooks($a->module.'_pre_'.$selname, $arr);
Addon::callHooks($a->module.'_post_'.$selname, $o);
Addon::callHooks($a->module.'_pre_'.$selname, $arr);
Addon::callHooks($a->module.'_post_'.$selname, $o);
Addon::callHooks('jot_networks', $jotnets);
### src/Core/Worker.php
Addon::callHooks("proc_run", $arr);
### src/Util/Emailer.php
Addon::callHooks('emailer_send_prepare', $params);
Addon::callHooks("emailer_send", $hookdata);
### src/Util/Map.php
Addon::callHooks('generate_map', $arr);
Addon::callHooks('generate_named_map', $arr);
Addon::callHooks('Map::getCoordinates', $arr);
### src/Util/Network.php
Addon::callHooks('avatar_lookup', $avatar);
### src/Util/ParseUrl.php
Addon::callHooks("getsiteinfo", $siteinfo);
### src/Protocol/DFRN.php
Addon::callHooks('atom_feed_end', $atom);
Addon::callHooks('atom_feed_end', $atom);

View file

@ -4,6 +4,7 @@ Friendica Installation
* [Zur Startseite der Hilfe](help) * [Zur Startseite der Hilfe](help)
Wir haben hart daran gearbeitet, um Friendica auf vorgefertigten Hosting-Plattformen zum Laufen zu bringen - solche, auf denen auch Wordpress Blogs und Drupal-Installationen laufen. Wir haben hart daran gearbeitet, um Friendica auf vorgefertigten Hosting-Plattformen zum Laufen zu bringen - solche, auf denen auch Wordpress Blogs und Drupal-Installationen laufen.
Wir bieten eine manuelle und eine automatische Installation an.
Aber bedenke, dass Friendica mehr als eine einfache Webanwendung ist. Aber bedenke, dass Friendica mehr als eine einfache Webanwendung ist.
Es handelt sich um ein komplexes Kommunikationssystem, das eher an einen Email-Server erinnert als an einen Webserver. Es handelt sich um ein komplexes Kommunikationssystem, das eher an einen Email-Server erinnert als an einen Webserver.
Um die Verfügbarkeit und Performance zu gewährleisten, werden Nachrichten im Hintergrund verschickt und gespeichert, um sie später zu verschicken, wenn eine Webseite gerade nicht erreichbar ist. Um die Verfügbarkeit und Performance zu gewährleisten, werden Nachrichten im Hintergrund verschickt und gespeichert, um sie später zu verschicken, wenn eine Webseite gerade nicht erreichbar ist.
@ -11,83 +12,124 @@ Diese Funktionalität benötigt ein wenig mehr als die normalen Blogs.
Nicht jeder PHP/MySQL-Hosting-Anbieter kann Friendica unterstützen. Nicht jeder PHP/MySQL-Hosting-Anbieter kann Friendica unterstützen.
Viele hingegen können es. Aber **bitte** prüfe die Voraussetzungen deines Servers vor der Installation. Viele hingegen können es. Aber **bitte** prüfe die Voraussetzungen deines Servers vor der Installation.
Wenn dir Fehler während der Installation auffallen, sag uns bitte über [github](https://github.com/friendica/issues) Bescheid. Wenn dir Fehler während der Installation auffallen, sag uns bitte über [Helper](http://forum.friendi.ca/profile/helpers) oder das [Entwickler Forum](https://forum.friendi.ca/profile/developers) Bescheid oder [erstelle ein Issue](https://github.com/friendica/friendica/issues).
Gib uns bitte so viele Infos zu deinem System, wie du kannst, und beschreibe den Fehler mit allen Details und Fehlermeldungen, so dass wir den Fehler zukünftig verhindern können. Gib uns bitte so viele Infos zu deinem System, wie du kannst, und beschreibe den Fehler mit allen Details und Fehlermeldungen, so dass wir den Fehler zukünftig verhindern können.
Aufgrund der großen Anzahl an verschiedenen Betriebssystemen und PHP-Plattformen haben wir nur geringe Kapazitäten, um deine PHP-Installation zu debuggen oder fehlende Module zu ersetzen, aber wir tun unser Bestes, um allgemeine Code-Fehler zu beheben. Aufgrund der großen Anzahl an verschiedenen Betriebssystemen und PHP-Plattformen haben wir nur geringe Kapazitäten, um deine PHP-Installation zu debuggen oder fehlende Module zu ersetzen, aber wir tun unser Bestes, um allgemeine Code-Fehler zu beheben.
Falls du noch keinen Friendica-Account hast, kannst du dir einen temporären Account hier erstellen: [tryfriendica.de](https://tryfriendica.de).
Darüber kannst du den genannten Forum beitreten.
Der Account wird nach 7 Tagen ablaufen, aber du kannst einen Server-Admin fragen, diesen Account länger zu erhalten, sollte das Problem nicht innerhalb dieser Zeit gelöst sein.
Bevor du anfängst: suche dir einen Domain- oder Subdomainnamen für deinen Server. Bevor du anfängst: suche dir einen Domain- oder Subdomainnamen für deinen Server.
Dinge verändern sich und einige deiner Freunde haben möglicherweise Probleme, mit dir zu kommunizieren. Dinge verändern sich und einige deiner Freunde haben möglicherweise Probleme, mit dir zu kommunizieren.
Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben. Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben.
1. Voraussetzungen Requirements
- Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst ---
- PHP 5.6+. Je neuer, desto besser.
- PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
- Curl, GD, PDO, MySQLi, xml, zip und OpenSSL-Erweiterung
- etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail()
- Das POSIX Modul muss aktiviert sein ([CentOS, RHEL](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) haben dies z.B. deaktiviert)
- Mysql 5.5.3+
- die Möglichkeit, wiederkehrende Aufgaben mit cron (Linux/Mac) oder "Scheduled Tasks" einzustellen (Windows) [Beachte: andere Optionen sind in Abschnitt 7 dieser Dokumentation zu finden]
- Installation in einer Top-Level-Domain oder Subdomain (ohne eine Verzeichnis/Pfad-Komponente in der URL) wird bevorzugt. Verzeichnispfade sind für diesen Zweck nicht so günstig und wurden auch nicht ausführlich getestet.
* Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst
* PHP 5.6+ (PHP 7 ist aufgrund der Performance empfohlen)
* PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
* Curl, GD, PDO, MySQLi, xml, zip und OpenSSL-Erweiterung
* Das POSIX Modul muss aktiviert sein ([CentOS, RHEL](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) haben dies z.B. deaktiviert)
* etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail()
* Mysql 5.5.3+ (oder eine äquivalente Alternative: MariaDB, Percona Server etc.)
* die Möglichkeit, wiederkehrende Aufgaben mit cron (Linux/Mac) oder "Scheduled Tasks" einzustellen (Windows) [Beachte: andere Optionen sind in Abschnitt 7 dieser Dokumentation zu finden]
* Installation in einer Top-Level-Domain oder Subdomain (ohne eine Verzeichnis/Pfad-Komponente in der URL) wird bevorzugt. Verzeichnispfade sind für diesen Zweck nicht so günstig und wurden auch nicht ausführlich getestet.
[Dreamhost.com bietet ein ausreichendes Hosting-Paket mit den nötigen Features zu einem annehmbaren Preis. Wenn dein Hosting-Anbieter keinen Unix-Zugriff erlaubt, kannst du Schwierigkeiten mit der Einrichtung der Webseite haben. Installation
---
1.1. APT-Pakete ### Friendica
- Apache: sudo apt-get install apache2
- PHP5: sudo apt-get install php5
- PHP5-Zusätzliche Pakete: sudo apt-get install php5-curl php5-gd php5-mysql
- MySQL: sudo apt-get install mysql-server
2. Entpacke die Friendica-Daten in das Quellverzeichnis (root) des Dokumentenbereichs deines Webservers. Entpacke die Friendica-Daten in das Quellverzeichnis (root) des Dokumentenbereichs deines Webservers.
Wenn du die Möglichkeit hierzu hast, empfehlen wir dir "git" zu nutzen, um die Daten direkt von der Quelle zu klonen, statt die gepackte .tar- oder .zip-Datei zu nutzen.
Das macht die Aktualisierung wesentlich einfacher.
Der Linux-Code, mit dem man die Dateien direkt in ein Verzeichnis wie "meinewebseite" kopiert, ist
- Wenn du die Möglichkeit hierzu hast, empfehlen wir dir "git" zu nutzen, um die Daten direkt von der Quelle zu klonen, statt die gepackte .tar- oder .zip-Datei zu nutzen. Das macht die Aktualisierung wesentlich einfacher. Der Linux-Code, mit dem man die Dateien direkt in ein Verzeichnis wie "meinewebseite" kopiert, ist git clone https://github.com/friendica/friendica.git mywebsite
cd mywebsite
bin/composer.phar install
`git clone https://github.com/friendica/friendica.git meinewebseite` Stelle sicher, dass der Ordner *view/smarty3* existiert and von dem Webserver-Benutzer beschreibbar ist
- und dann kannst du die letzten Änderungen immer mit dem folgenden Code holen mkdir view/smarty3
chmod 777 view/smarty3
`cd meinewebseite` Falls Addons installiert werden sollen: Gehe in den Friendica-Ordner
`git pull`
`bin/composer.phar install`
- Addons installieren cd mywebsite
- zunächst solltest du **in** deinem Webseitenordner sein
`cd meinewebseite` Und die Addon Repository klonst:
- dann kannst du das Addon-Verzeichnis seperat kopieren git clone https://github.com/friendica/friendica-addons.git addon
`git clone https://github.com/friendica/friendica-addons.git addon` Um das Addon-Verzeichnis aktuell zu halten, solltest du in diesem Pfad ein "git pull"-Befehl eintragen
- Um das Addon-Verzeichnis aktuell zu halten, solltest du in diesem Pfad ein "git pull"-Befehl eintragen cd meinewebseite/addon
git pull
`cd meinewebseite/addon` Wenn du den Verzeichnispfad auf deinen Webserver kopierst, dann stelle sicher, dass du auch die .htaccess kopierst, da "Punkt"-Dateien oft versteckt sind und normalerweise nicht direkt kopiert werden.
`git pull` ### Erselle eine Datenbank
- Wenn du den Verzeichnispfad auf deinen Webserver kopierst, dann stelle sicher, dass du auch die .htaccess kopierst, da "Punkt"-Dateien oft versteckt sind und normalerweise nicht direkt kopiert werden. Erstelle eine leere Datenbank und notiere alle Zugangsdaten (Adresse der Datenbank, Nutzername, Passwort, Datenbankname).
3. Erstelle eine leere Datenbank und notiere alle Zugangsdaten (Adresse der Datenbank, Nutzername, Passwort, Datenbankname).
Friendica benötigt die Berechtigungen um neue Felder in dieser Datenbank zu ertellen (create) und zu löschen (delete). Friendica benötigt die Berechtigungen um neue Felder in dieser Datenbank zu ertellen (create) und zu löschen (delete).
4. Besuche deine Webseite mit deinem Browser und befolge die Anleitung. Bitte beachte jeden Fehler und korrigiere diese, bevor du fortfährst. Mit neueren Versionen von MySQL (5.7.17+) musst du den `sql_mode` zu `''` (blank) setzen.
Benutze diese Einstellung, wenn der Installer nicht in der Lage ist, die Tabellen aufgrund eines Timestamp-Format Problems zu erstellen.
Falls dem so ist, finde den `[mysqld]` Bereich in deiner `my.conf` Datei und füge diese Zeile hinzu:
5. *Wenn* die automatisierte Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende: sql_mode = ''
- ".htconfig.php" existiert ... wenn nicht, bearbeite die „htconfig.php“ und ändere die Systemeinstellungen. Benenne sie um in „.htconfig.php" Starte MySQL dann neu und es sollte klappen.
- die Datenbank beinhaltet Daten. ... wenn nicht, importiere den Inhalt der Datei "database.sql" mit phpmyadmin oder per mysql-Kommandozeile.
6. Besuche deine Seite an diesem Punkt wieder und registriere deinen persönlichen Account. Alle Registrierungsprobleme sollten automatisch behebbar sein. ### Option A: Der manuelle Installer
Wenn du irgendwelche **kritischen** Fehler zu diesen Zeitpunkt erhalten solltest, deutet das darauf hin, dass die Datenbank nicht korrekt installiert wurde. Du kannst bei Bedarf die Datei .htconfig.php verschieben/umbenennen und die Datenbank leeren (als „Dropping“ bezeichnet), so dass du mit einem sauberen System neu starten kannst.
7. Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen. Beispiel: Besuche deine Webseite mit deinem Browser und befolge die Anleitung.
Bitte beachte jeden Fehler und korrigiere diese, bevor du fortfährst.
`cd /base/directory; /path/to/php bin/worker.php` Falls du einen Port für die Datenbankverbindung angeben musst, kannst du diesen in der Host-Eingabe Zeile angeben.
*Wenn* die manuelle Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende:
* ".htconfig.php" existiert ... wenn nicht, bearbeite die „htconfig.php“ und ändere die Systemeinstellungen. Benenne sie um in „.htconfig.php".
* die Datenbank beinhaltet Daten. ... wenn nicht, importiere den Inhalt der Datei "database.sql" mit phpmyadmin oder per mysql-Kommandozeile.
Besuche deine Seite an diesem Punkt wieder und registriere deinen persönlichen Account.
Alle Registrierungsprobleme sollten automatisch behebbar sein.
Wenn du irgendwelche **kritischen** Fehler zu diesen Zeitpunkt erhalten solltest, deutet das darauf hin, dass die Datenbank nicht korrekt installiert wurde.
Du kannst bei Bedarf die Datei .htconfig.php verschieben/umbenennen und die Datenbank leeren (als „Dropping“ bezeichnet), so dass du mit einem sauberen System neu starten kannst.
### Option B: Starte das manuelle Installationsscript
Öffne die Datei htconfig.php im Friendica-Hauptordner mit einem Text-Editor.
Entferne die `die('...');` Zeile und bearbeite die Einstellungen so, das sie zu deinem System passen (MySQL, Sprache, Theme etc.).
Dann speichere die Datei (jedoch nicht umbenennen).
Gehe in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus:
bin/console autoinstall
Oder falls du alle optionalen Checks ausfürehn lassen möchtest, benutze diese Option:
bin/console autoinstall -a
*Wenn* die automatisierte Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende:
* Existiert die `.htconfig.php`? Falls ja, wird die automatisierte Installation nicht gestartet.
* Sind Einstellungen in der `.htconfig.php` korrekt? Falls nicht, bitte bearbeite diese Datei erneut.
* Ist die leere MySQL-Datenbank erstellt? Falls nicht, erstelle diese.
Für mehr Informationen kannst du diese Option verwenden:
bin/console autoinstall -v
### Einen Worker einrichten
Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen.
Beispiel:
cd /base/directory; /path/to/php bin/worker.php
Ändere "/base/directory" und "/path/to/php" auf deine Systemvorgaben. Ändere "/base/directory" und "/path/to/php" auf deine Systemvorgaben.
@ -102,9 +144,10 @@ Friendica wird nicht korrekt laufen, wenn dieser Schritt nicht erfolgreich abges
Falls das Einrichten des cron nicht möglich ist, kannst Du alternativ den "frontend worker" vom Administrationsinterface aus aktivieren. Falls das Einrichten des cron nicht möglich ist, kannst Du alternativ den "frontend worker" vom Administrationsinterface aus aktivieren.
### Erstelle einen Backup Plan ### Erstelle einen Backup Plan
Es werden schlimme Dinge geschehen. Es werden schlimme Dinge geschehen.
Sei es nun ein Hardwareversage oder eine korrumpierte Datenbank. Sei es nun ein Hardwareversagen oder eine kaputte Datenbank.
Deshalb solltest du dir nachdem die Installation deines Friendica Knotens abgeschlossen ist einen Backup Plan erstellen. Deshalb solltest du dir, nachdem die Installation deines Friendica Knotens abgeschlossen ist, einen Backup Plan erstellen.
Die wichtigste Datei ist die `.htconfig.php` im Stammverzeichnis deiner Friendica Installation. Die wichtigste Datei ist die `.htconfig.php` im Stammverzeichnis deiner Friendica Installation.
Und da alle Daten in der Datenbank gespeichert werden, solltest du einen nicht all zu alten Dump der Friendica Datenbank zur Hand haben, solltest du deinen Knoten wieder herstellen müssen. Und da alle Daten in der Datenbank gespeichert werden, solltest du einen nicht all zu alten Dump der Friendica Datenbank zur Hand haben, solltest du deinen Knoten wieder herstellen müssen.

View file

@ -860,12 +860,15 @@ class dba {
* *
* @param string $table Table name * @param string $table Table name
* @param array $conditions Field condition(s) * @param array $conditions Field condition(s)
* @param array $options
* - cascade: If true we delete records in other tables that depend on the one we're deleting through
* relations (default: true)
* @param boolean $in_process Internal use: Only do a commit after the last delete * @param boolean $in_process Internal use: Only do a commit after the last delete
* @param array $callstack Internal use: prevent endless loops * @param array $callstack Internal use: prevent endless loops
* *
* @return boolean|array was the delete successful? When $in_process is set: deletion data * @return boolean|array was the delete successful? When $in_process is set: deletion data
*/ */
public static function delete($table, array $conditions, $in_process = false, array &$callstack = []) public static function delete($table, array $conditions, array $options = [], $in_process = false, array &$callstack = [])
{ {
if (empty($table) || empty($conditions)) { if (empty($table) || empty($conditions)) {
logger('Table and conditions have to be set'); logger('Table and conditions have to be set');
@ -888,13 +891,15 @@ class dba {
$commands[$key] = ['table' => $table, 'conditions' => $conditions]; $commands[$key] = ['table' => $table, 'conditions' => $conditions];
$cascade = defaults($options, 'cascade', true);
// To speed up the whole process we cache the table relations // To speed up the whole process we cache the table relations
if (count(self::$relation) == 0) { if ($cascade && count(self::$relation) == 0) {
self::buildRelationData(); self::buildRelationData();
} }
// Is there a relation entry for the table? // Is there a relation entry for the table?
if (isset(self::$relation[$table])) { if ($cascade && isset(self::$relation[$table])) {
// We only allow a simple "one field" relation. // We only allow a simple "one field" relation.
$field = array_keys(self::$relation[$table])[0]; $field = array_keys(self::$relation[$table])[0];
$rel_def = array_values(self::$relation[$table])[0]; $rel_def = array_values(self::$relation[$table])[0];
@ -907,7 +912,7 @@ class dba {
if ((count($conditions) == 1) && ($field == array_keys($conditions)[0])) { if ((count($conditions) == 1) && ($field == array_keys($conditions)[0])) {
foreach ($rel_def AS $rel_table => $rel_fields) { foreach ($rel_def AS $rel_table => $rel_fields) {
foreach ($rel_fields AS $rel_field) { foreach ($rel_fields AS $rel_field) {
$retval = self::delete($rel_table, [$rel_field => array_values($conditions)[0]], true, $callstack); $retval = self::delete($rel_table, [$rel_field => array_values($conditions)[0]], $options, true, $callstack);
$commands = array_merge($commands, $retval); $commands = array_merge($commands, $retval);
} }
} }
@ -921,7 +926,7 @@ class dba {
while ($row = self::fetch($data)) { while ($row = self::fetch($data)) {
// Now we accumulate the delete commands // Now we accumulate the delete commands
$retval = self::delete($table, [$field => $row[$field]], true, $callstack); $retval = self::delete($table, [$field => $row[$field]], $options, true, $callstack);
$commands = array_merge($commands, $retval); $commands = array_merge($commands, $retval);
} }
@ -968,7 +973,7 @@ class dba {
// Split the SQL queries in chunks of 100 values // Split the SQL queries in chunks of 100 values
// We do the $i stuff here to make the code better readable // We do the $i stuff here to make the code better readable
$i = $counter[$key_table][$key_condition]; $i = $counter[$key_table][$key_condition];
if (count($compacted[$key_table][$key_condition][$i]) > 100) { if (isset($compacted[$key_table][$key_condition][$i]) && count($compacted[$key_table][$key_condition][$i]) > 100) {
++$i; ++$i;
} }

View file

@ -1411,18 +1411,13 @@ function prepare_body(array &$item, $attach = false, $is_preview = false)
function apply_content_filter($html, array $reasons) function apply_content_filter($html, array $reasons)
{ {
if (count($reasons)) { if (count($reasons)) {
$rnd = random_string(8); $tpl = get_markup_template('wall/content_filter.tpl');
$content_filter_html = '<ul class="content-filter-reasons">'; $html = replace_macros($tpl, [
foreach ($reasons as $reason) { '$reasons' => $reasons,
$content_filter_html .= '<li>' . htmlspecialchars($reason) . '</li>' . PHP_EOL; '$rnd' => random_string(8),
} '$openclose' => L10n::t('Click to open/close'),
$content_filter_html .= '</ul> '$html' => $html
<p><span id="content-filter-wrap-' . $rnd . '" class="fakelink content-filter-button" onclick=openClose(\'content-filter-' . $rnd . '\'); >' . ]);
L10n::t('Click to open/close') .
'</span></p>
<div id="content-filter-' . $rnd . '" class="content-filter-content" style="display: none;">';
$html = $content_filter_html . $html . '</div>';
} }
return $html; return $html;

View file

@ -1120,6 +1120,7 @@ function admin_page_site_post(App $a)
} }
Config::set('system', 'language', $language); Config::set('system', 'language', $language);
Config::set('system', 'theme', $theme); Config::set('system', 'theme', $theme);
Theme::install($theme);
if ($theme_mobile == '---') { if ($theme_mobile == '---') {
Config::delete('system', 'mobile-theme'); Config::delete('system', 'mobile-theme');
@ -1262,6 +1263,7 @@ function admin_page_site(App $a)
/* Community page style */ /* Community page style */
$community_page_style_choices = [ $community_page_style_choices = [
CP_NO_INTERNAL_COMMUNITY => L10n::t("No community page for local users"),
CP_NO_COMMUNITY_PAGE => L10n::t("No community page"), CP_NO_COMMUNITY_PAGE => L10n::t("No community page"),
CP_USERS_ON_SERVER => L10n::t("Public postings from users of this site"), CP_USERS_ON_SERVER => L10n::t("Public postings from users of this site"),
CP_GLOBAL_COMMUNITY => L10n::t("Public postings from the federated network"), CP_GLOBAL_COMMUNITY => L10n::t("Public postings from the federated network"),

View file

@ -23,6 +23,12 @@ function babel_content()
'content' => visible_lf($bbcode) 'content' => visible_lf($bbcode)
]; ];
$plain = Text\BBCode::toPlaintext($bbcode, false);
$results[] = [
'title' => L10n::t('BBCode::toPlaintext'),
'content' => visible_lf($plain)
];
$html = Text\BBCode::convert($bbcode); $html = Text\BBCode::convert($bbcode);
$results[] = [ $results[] = [
'title' => L10n::t("BBCode::convert \x28raw HTML\x29"), 'title' => L10n::t("BBCode::convert \x28raw HTML\x29"),

View file

@ -30,6 +30,11 @@ function community_content(App $a, $update = 0)
$page_style = Config::get('system', 'community_page_style'); $page_style = Config::get('system', 'community_page_style');
if ($page_style == CP_NO_INTERNAL_COMMUNITY) {
notice(L10n::t('Access denied.') . EOL);
return;
}
if ($a->argc > 1) { if ($a->argc > 1) {
$content = $a->argv[1]; $content = $a->argv[1];
} else { } else {

View file

@ -25,42 +25,13 @@ function dfrn_notify_post(App $a) {
$data = json_decode($postdata); $data = json_decode($postdata);
if (is_object($data)) { if (is_object($data)) {
$nick = defaults($a->argv, 1, ''); $nick = defaults($a->argv, 1, '');
$user = dba::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]); $user = dba::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
if (!DBM::is_result($user)) { if (!DBM::is_result($user)) {
System::httpExit(500); System::httpExit(500);
} }
$msg = Diaspora::decodeRaw($user, $postdata); dfrn_dispatch_private($user, $postdata);
} elseif (!dfrn_dispatch_public($postdata)) {
// Check if the user has got this contact
$cid = Contact::getIdForURL($msg['author'], $user['uid']);
if (!$cid) {
// Otherwise there should be a public contact
$cid = Contact::getIdForURL($msg['author']);
if (!$cid) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
}
// We now have some contact, so we fetch it
$importer = dba::fetch_first("SELECT *, `name` as `senderName`
FROM `contact`
WHERE NOT `blocked` AND `id` = ? LIMIT 1",
$cid);
// This should never fail
if (!DBM::is_result($importer)) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
// Set the user id. This is important if this is a public contact
$importer['importer_uid'] = $user['uid'];
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer);
System::xmlExit($ret, 'Done');
} else {
require_once 'mod/salmon.php'; require_once 'mod/salmon.php';
salmon_post($a, $postdata); salmon_post($a, $postdata);
} }
@ -91,19 +62,12 @@ function dfrn_notify_post(App $a) {
$dfrn_id = substr($dfrn_id, 2); $dfrn_id = substr($dfrn_id, 2);
} }
$r = q("SELECT * FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s' LIMIT 1", if (!dba::exists('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge])) {
dbesc($dfrn_id), logger('could not match challenge to dfrn_id ' . $dfrn_id . ' challenge=' . $challenge);
dbesc($challenge)
);
if (! DBM::is_result($r)) {
logger('dfrn_notify: could not match challenge to dfrn_id ' . $dfrn_id . ' challenge=' . $challenge);
System::xmlExit(3, 'Could not match challenge'); System::xmlExit(3, 'Could not match challenge');
} }
$r = q("DELETE FROM `challenge` WHERE `dfrn-id` = '%s' AND `challenge` = '%s'", dba::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]);
dbesc($dfrn_id),
dbesc($challenge)
);
// find the local user who owns this relationship. // find the local user who owns this relationship.
@ -144,7 +108,7 @@ function dfrn_notify_post(App $a) {
); );
if (!DBM::is_result($r)) { if (!DBM::is_result($r)) {
logger('dfrn_notify: contact not found for dfrn_id ' . $dfrn_id); logger('contact not found for dfrn_id ' . $dfrn_id);
System::xmlExit(3, 'Contact not found'); System::xmlExit(3, 'Contact not found');
//NOTREACHED //NOTREACHED
} }
@ -153,15 +117,11 @@ function dfrn_notify_post(App $a) {
$importer = $r[0]; $importer = $r[0];
logger("Remote rino version: ".$rino_remote." for ".$importer["url"], LOGGER_DEBUG);
if ((($writable != (-1)) && ($writable != $importer['writable'])) || ($importer['forum'] != $forum) || ($importer['prv'] != $prv)) { if ((($writable != (-1)) && ($writable != $importer['writable'])) || ($importer['forum'] != $forum) || ($importer['prv'] != $prv)) {
q("UPDATE `contact` SET `writable` = %d, forum = %d, prv = %d WHERE `id` = %d", $fields = ['writable' => ($writable == (-1)) ? $importer['writable'] : $writable,
intval(($writable == (-1)) ? $importer['writable'] : $writable), 'forum' => $forum, 'prv' => $prv];
intval($forum), dba::update('contact', $fields, ['id' => $importer['id']]);
intval($prv),
intval($importer['id'])
);
if ($writable != (-1)) { if ($writable != (-1)) {
$importer['writable'] = $writable; $importer['writable'] = $writable;
} }
@ -173,8 +133,7 @@ function dfrn_notify_post(App $a) {
$importer = Contact::updateSslPolicy($importer, $ssl_policy); $importer = Contact::updateSslPolicy($importer, $ssl_policy);
logger('dfrn_notify: received notify from ' . $importer['name'] . ' for ' . $importer['username']); logger('data: ' . $data, LOGGER_DATA);
logger('dfrn_notify: data: ' . $data, LOGGER_DATA);
if ($dissolve == 1) { if ($dissolve == 1) {
// Relationship is dissolved permanently // Relationship is dissolved permanently
@ -186,8 +145,6 @@ function dfrn_notify_post(App $a) {
$rino = Config::get('system', 'rino_encrypt'); $rino = Config::get('system', 'rino_encrypt');
$rino = intval($rino); $rino = intval($rino);
logger("Local rino version: " . $rino, LOGGER_DEBUG);
if (strlen($key)) { if (strlen($key)) {
// if local rino is lower than remote rino, abort: should not happen! // if local rino is lower than remote rino, abort: should not happen!
@ -198,17 +155,18 @@ function dfrn_notify_post(App $a) {
} }
$rawkey = hex2bin(trim($key)); $rawkey = hex2bin(trim($key));
logger('rino: md5 raw key: ' . md5($rawkey)); logger('rino: md5 raw key: ' . md5($rawkey), LOGGER_DATA);
$final_key = ''; $final_key = '';
if ($dfrn_version >= 2.1) { if ($dfrn_version >= 2.1) {
if ((($importer['duplex']) && strlen($importer['cprvkey'])) || (! strlen($importer['cpubkey']))) { if (($importer['duplex'] && strlen($importer['cprvkey'])) || !strlen($importer['cpubkey'])) {
openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']); openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']);
} else { } else {
openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']); openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']);
} }
} else { } else {
if ((($importer['duplex']) && strlen($importer['cpubkey'])) || (! strlen($importer['cprvkey']))) { if (($importer['duplex'] && strlen($importer['cpubkey'])) || !strlen($importer['cprvkey'])) {
openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']); openssl_public_decrypt($rawkey, $final_key, $importer['cpubkey']);
} else { } else {
openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']); openssl_private_decrypt($rawkey, $final_key, $importer['cprvkey']);
@ -230,12 +188,89 @@ function dfrn_notify_post(App $a) {
logger('rino: decrypted data: ' . $data, LOGGER_DATA); logger('rino: decrypted data: ' . $data, LOGGER_DATA);
} }
logger('Importing post from ' . $importer['addr'] . ' to ' . $importer['nickname'] . ' with the RINO ' . $rino_remote . ' encryption.', LOGGER_DEBUG);
$ret = DFRN::import($data, $importer); $ret = DFRN::import($data, $importer);
System::xmlExit($ret, 'Processed'); System::xmlExit($ret, 'Processed');
// NOTREACHED // NOTREACHED
} }
function dfrn_dispatch_public($postdata)
{
$msg = Diaspora::decodeRaw([], $postdata);
if (!$msg) {
// We have to fail silently to be able to hand it over to the salmon parser
return false;
}
// Fetch the corresponding public contact
$contact = Contact::getDetailsByAddr($msg['author'], 0);
if (!$contact) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
// We now have some contact, so we fetch it
$importer = dba::fetch_first("SELECT *, `name` as `senderName`
FROM `contact`
WHERE NOT `blocked` AND `id` = ? LIMIT 1",
$contact['id']);
$importer['importer_uid'] = 0;
// This should never fail
if (!DBM::is_result($importer)) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
logger('Importing post from ' . $msg['author'] . ' with the public envelope.', LOGGER_DEBUG);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer);
System::xmlExit($ret, 'Done');
}
function dfrn_dispatch_private($user, $postdata)
{
$msg = Diaspora::decodeRaw($user, $postdata);
if (!$msg) {
System::xmlExit(4, 'Unable to parse message');
}
// Check if the user has got this contact
$cid = Contact::getIdForURL($msg['author'], $user['uid']);
if (!$cid) {
// Otherwise there should be a public contact
$cid = Contact::getIdForURL($msg['author']);
if (!$cid) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
}
// We now have some contact, so we fetch it
$importer = dba::fetch_first("SELECT *, `name` as `senderName`
FROM `contact`
WHERE NOT `blocked` AND `id` = ? LIMIT 1",
$cid);
// This should never fail
if (!DBM::is_result($importer)) {
logger('Contact not found for address ' . $msg['author']);
System::xmlExit(3, 'Contact not found');
}
// Set the user id. This is important if this is a public contact
$importer['importer_uid'] = $user['uid'];
logger('Importing post from ' . $msg['author'] . ' to ' . $user['nickname'] . ' with the private envelope.', LOGGER_DEBUG);
// Now we should be able to import it
$ret = DFRN::import($msg['message'], $importer);
System::xmlExit($ret, 'Done');
}
function dfrn_notify_content(App $a) { function dfrn_notify_content(App $a) {
@ -252,7 +287,7 @@ function dfrn_notify_content(App $a) {
$type = ""; $type = "";
$last_update = ""; $last_update = "";
logger('dfrn_notify: new notification dfrn_id=' . $dfrn_id); logger('new notification dfrn_id=' . $dfrn_id);
$direction = (-1); $direction = (-1);
if (strpos($dfrn_id,':') == 1) { if (strpos($dfrn_id,':') == 1) {
@ -264,18 +299,13 @@ function dfrn_notify_content(App $a) {
$status = 0; $status = 0;
$r = q("DELETE FROM `challenge` WHERE `expire` < " . intval(time())); dba::delete('challenge', ["`expire` < ?", time()]);
$r = q("INSERT INTO `challenge` ( `challenge`, `dfrn-id`, `expire` , `type`, `last_update` ) $fields = ['challenge' => $hash, 'dfrn-id' => $dfrn_id, 'expire' => time() + 90,
VALUES( '%s', '%s', %d, '%s', '%s' ) ", 'type' => $type, 'last_update' => $last_update];
dbesc($hash), dba::insert('challenge', $fields);
dbesc($dfrn_id),
intval(time() + 90 ),
dbesc($type),
dbesc($last_update)
);
logger('dfrn_notify: challenge=' . $hash, LOGGER_DEBUG); logger('challenge=' . $hash, LOGGER_DATA);
$sql_extra = ''; $sql_extra = '';
switch($direction) { switch($direction) {
@ -306,7 +336,7 @@ function dfrn_notify_content(App $a) {
$status = 1; $status = 1;
} }
logger("Remote rino version: ".$rino_remote." for ".$r[0]["url"], LOGGER_DEBUG); logger("Remote rino version: ".$rino_remote." for ".$r[0]["url"], LOGGER_DATA);
$challenge = ''; $challenge = '';
$encrypted_id = ''; $encrypted_id = '';
@ -316,7 +346,7 @@ function dfrn_notify_content(App $a) {
$pub_key = trim($r[0]['pubkey']); $pub_key = trim($r[0]['pubkey']);
$dplx = intval($r[0]['duplex']); $dplx = intval($r[0]['duplex']);
if ((($dplx) && (strlen($prv_key))) || ((strlen($prv_key)) && (!(strlen($pub_key))))) { if (($dplx && strlen($prv_key)) || (strlen($prv_key) && !strlen($pub_key))) {
openssl_private_encrypt($hash, $challenge, $prv_key); openssl_private_encrypt($hash, $challenge, $prv_key);
openssl_private_encrypt($id_str, $encrypted_id, $prv_key); openssl_private_encrypt($id_str, $encrypted_id, $prv_key);
} elseif (strlen($pub_key)) { } elseif (strlen($pub_key)) {
@ -334,7 +364,7 @@ function dfrn_notify_content(App $a) {
$rino = Config::get('system', 'rino_encrypt'); $rino = Config::get('system', 'rino_encrypt');
$rino = intval($rino); $rino = intval($rino);
logger("Local rino version: ". $rino, LOGGER_DEBUG); logger("Local rino version: ". $rino, LOGGER_DATA);
// if requested rino is lower than enabled local rino, lower local rino version // if requested rino is lower than enabled local rino, lower local rino version
// if requested rino is higher than enabled local rino, reply with local rino // if requested rino is higher than enabled local rino, reply with local rino
@ -342,7 +372,7 @@ function dfrn_notify_content(App $a) {
$rino = $rino_remote; $rino = $rino_remote;
} }
if((($r[0]['rel']) && ($r[0]['rel'] != CONTACT_IS_SHARING)) || ($r[0]['page-flags'] == PAGE_COMMUNITY)) { if (($r[0]['rel'] && ($r[0]['rel'] != CONTACT_IS_SHARING)) || ($r[0]['page-flags'] == PAGE_COMMUNITY)) {
$perm = 'rw'; $perm = 'rw';
} else { } else {
$perm = 'r'; $perm = 'r';
@ -362,5 +392,4 @@ function dfrn_notify_content(App $a) {
killme(); killme();
} }
} }

View file

@ -122,8 +122,8 @@ function friendica_content(App $a)
$o .= '<p>'.L10n::t('Read about the <a href="%1$s/tos">Terms of Service</a> of this node.', System::baseurl()).'</p>'; $o .= '<p>'.L10n::t('Read about the <a href="%1$s/tos">Terms of Service</a> of this node.', System::baseurl()).'</p>';
} }
$blocklist = Config::get('system', 'blocklist'); $blocklist = Config::get('system', 'blocklist', []);
if (count($blocklist)) { if (!empty($blocklist)) {
$o .= '<div id="about_blocklist"><p>' . L10n::t('On this server the following remote servers are blocked.') . '</p>' . PHP_EOL; $o .= '<div id="about_blocklist"><p>' . L10n::t('On this server the following remote servers are blocked.') . '</p>' . PHP_EOL;
$o .= '<table class="table"><thead><tr><th>' . L10n::t('Blocked domain') . '</th><th>' . L10n::t('Reason for the block') . '</th></thead><tbody>' . PHP_EOL; $o .= '<table class="table"><thead><tr><th>' . L10n::t('Blocked domain') . '</th><th>' . L10n::t('Reason for the block') . '</th></thead><tbody>' . PHP_EOL;
foreach ($blocklist as $b) { foreach ($blocklist as $b) {

View file

@ -51,7 +51,7 @@ function install_post(App $a) {
$phpath = notags(trim($_POST['phpath'])); $phpath = notags(trim($_POST['phpath']));
require_once("include/dba.php"); require_once("include/dba.php");
if (!dba::connect($dbhost, $dbuser, $dbpass, $dbdata)) { if (!dba::connect($dbhost, $dbuser, $dbpass, $dbdata, true)) {
$a->data['db_conn_failed'] = true; $a->data['db_conn_failed'] = true;
} }
@ -140,11 +140,37 @@ function install_content(App $a) {
switch ($install_wizard_pass) { switch ($install_wizard_pass) {
case 1: { // System check case 1: { // System check
$checks = [];
check_funcs($checks);
check_imagik($checks);
check_htconfig($checks);
check_smarty3($checks);
check_keys($checks);
if (x($_POST, 'phpath')) { if (x($_POST, 'phpath')) {
$phpath = notags(trim($_POST['phpath'])); $phpath = notags(trim($_POST['phpath']));
} }
list($checks, $checkspassed) = Install::check($phpath); check_php($phpath, $checks);
check_htaccess($checks);
/// @TODO Maybe move this out?
function check_passed($v, $c) {
if ($c['required']) {
$v = $v && $c['status'];
}
return $v;
}
$checkspassed = array_reduce($checks, "check_passed", true);
$tpl = get_markup_template('install_checks.tpl'); $tpl = get_markup_template('install_checks.tpl');
$o .= replace_macros($tpl, [ $o .= replace_macros($tpl, [

View file

@ -144,14 +144,13 @@ function invite_content(App $a) {
$o = replace_macros($tpl, [ $o = replace_macros($tpl, [
'$form_security_token' => get_form_security_token("send_invite"), '$form_security_token' => get_form_security_token("send_invite"),
'$invite' => L10n::t('Send invitations'), '$title' => L10n::t('Send invitations'),
'$addr_text' => L10n::t('Enter email addresses, one per line:'), '$recipients' => ['recipients', L10n::t('Enter email addresses, one per line:')],
'$msg_text' => L10n::t('Your message:'), '$message' => ['message', L10n::t('Your message:'),L10n::t('You are cordially invited to join me and other close friends on Friendica - and help us to create a better social web.') . "\r\n" . "\r\n"
'$default_message' => L10n::t('You are cordially invited to join me and other close friends on Friendica - and help us to create a better social web.') . "\r\n" . "\r\n"
. $linktxt . $linktxt
. "\r\n" . "\r\n" . (($invonly) ? L10n::t('You will need to supply this invitation code: $invite_code') . "\r\n" . "\r\n" : '') .L10n::t('Once you have registered, please connect with me via my profile page at:') . "\r\n" . "\r\n" . (($invonly) ? L10n::t('You will need to supply this invitation code: $invite_code') . "\r\n" . "\r\n" : '') .L10n::t('Once you have registered, please connect with me via my profile page at:')
. "\r\n" . "\r\n" . System::baseUrl() . '/profile/' . $a->user['nickname'] . "\r\n" . "\r\n" . System::baseUrl() . '/profile/' . $a->user['nickname']
. "\r\n" . "\r\n" . L10n::t('For more information about the Friendica project and why we feel it is important, please visit http://friendi.ca') . "\r\n" . "\r\n" , . "\r\n" . "\r\n" . L10n::t('For more information about the Friendica project and why we feel it is important, please visit http://friendi.ca') . "\r\n" . "\r\n"],
'$submit' => L10n::t('Submit') '$submit' => L10n::t('Submit')
]); ]);

View file

@ -23,9 +23,18 @@ function noscrape_init(App $a)
Profile::load($a, $which, $profile); Profile::load($a, $which, $profile);
$json_info = [
'addr' => $a->profile['addr'],
'nick' => $which,
'guid' => $a->profile['guid'],
'key' => $a->profile['pubkey'],
'homepage' => System::baseUrl()."/profile/{$which}",
'comm' => ($a->profile['account-type'] == ACCOUNT_TYPE_COMMUNITY),
];
if (!$a->profile['net-publish'] || $a->profile['hidewall']) { if (!$a->profile['net-publish'] || $a->profile['hidewall']) {
header('Content-type: application/json; charset=utf-8'); header('Content-type: application/json; charset=utf-8');
$json_info = ["hide" => true]; $json_info["hide"] = true;
echo json_encode($json_info); echo json_encode($json_info);
exit; exit;
} }
@ -36,17 +45,9 @@ function noscrape_init(App $a)
$contactPhoto = dba::selectFirst('contact', ['photo'], ['self' => true, 'uid' => $a->profile['uid']]); $contactPhoto = dba::selectFirst('contact', ['photo'], ['self' => true, 'uid' => $a->profile['uid']]);
$json_info = [ $json_info['fn'] = $a->profile['name'];
'fn' => $a->profile['name'], $json_info['photo'] = $contactPhoto["photo"];
'addr' => $a->profile['addr'], $json_info['tags'] = $keywords;
'nick' => $which,
'guid' => $a->profile['guid'],
'key' => $a->profile['pubkey'],
'homepage' => System::baseUrl()."/profile/{$which}",
'comm' => (x($a->profile, 'page-flags')) && ($a->profile['page-flags'] == PAGE_COMMUNITY),
'photo' => $contactPhoto["photo"],
'tags' => $keywords
];
if (is_array($a->profile) && !$a->profile['hide-friends']) { if (is_array($a->profile) && !$a->profile['hide-friends']) {
/// @todo What should this value tell us? /// @todo What should this value tell us?

View file

@ -191,7 +191,7 @@ function profile_content(App $a, $update = 0)
$o .= Widget::commonFriendsVisitor($a->profile['profile_uid']); $o .= Widget::commonFriendsVisitor($a->profile['profile_uid']);
if (x($_SESSION, 'new_member') && $is_owner) { if (x($_SESSION, 'new_member') && $is_owner) {
$o .= '<a href="newmember" id="newmember-tips" style="font-size: 1.2em;"><b>' . L10n::t('Tips for New Members') . '</b></a>' . EOL; $o .= '<div id="newmember-tips"><a href="newmember"><b>' . L10n::t('Tips for New Members') . '</b></a></div>';
} }
$commpage = $a->profile['page-flags'] == PAGE_COMMUNITY; $commpage = $a->profile['page-flags'] == PAGE_COMMUNITY;

View file

@ -12,6 +12,7 @@ use Friendica\Core\Config;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\PConfig; use Friendica\Core\PConfig;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Core\Theme;
use Friendica\Core\Worker; use Friendica\Core\Worker;
use Friendica\Database\DBM; use Friendica\Database\DBM;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -354,6 +355,7 @@ function settings_post(App $a)
theme_post($a); theme_post($a);
} }
} }
Theme::install($theme);
$r = q("UPDATE `user` SET `theme` = '%s' WHERE `uid` = %d", $r = q("UPDATE `user` SET `theme` = '%s' WHERE `uid` = %d",
dbesc($theme), dbesc($theme),

View file

@ -49,7 +49,7 @@ class ForumManager
"SELECT `contact`.`id`, `contact`.`url`, `contact`.`name`, `contact`.`micro`, `contact`.`thumb` "SELECT `contact`.`id`, `contact`.`url`, `contact`.`name`, `contact`.`micro`, `contact`.`thumb`
FROM `contact` FROM `contact`
WHERE `network`= 'dfrn' AND $select AND `uid` = ? WHERE `network`= 'dfrn' AND $select AND `uid` = ?
AND NOT `blocked` AND NOT `hidden` AND NOT `pending` AND NOT `archive` AND NOT `blocked` AND NOT `pending` AND NOT `archive`
AND `success_update` > `failure_update` AND `success_update` > `failure_update`
$order ", $order ",
$uid $uid

View file

@ -161,7 +161,8 @@ class Nav
} }
} }
if (local_user() || Config::get('system', 'community_page_style') != CP_NO_COMMUNITY_PAGE) { if ((local_user() || Config::get('system', 'community_page_style') != CP_NO_COMMUNITY_PAGE) &&
!(Config::get('system', 'community_page_style') == CP_NO_INTERNAL_COMMUNITY)) {
$nav['community'] = ['community', L10n::t('Community'), '', L10n::t('Conversations on this and other servers')]; $nav['community'] = ['community', L10n::t('Community'), '', L10n::t('Conversations on this and other servers')];
} }
@ -180,17 +181,13 @@ class Nav
$nav['home'] = ['profile/' . $a->user['nickname'], L10n::t('Home'), '', L10n::t('Your posts and conversations')]; $nav['home'] = ['profile/' . $a->user['nickname'], L10n::t('Home'), '', L10n::t('Your posts and conversations')];
if (in_array($_SESSION['page_flags'], [PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE, PAGE_PRVGROUP])) { // Don't show notifications for public communities
// only show friend requests for normal pages. Other page types have automatic friendship. if ($_SESSION['page_flags'] != PAGE_COMMUNITY) {
if (in_array($_SESSION['page_flags'], [PAGE_NORMAL, PAGE_SOAPBOX, PAGE_PRVGROUP])) {
$nav['introductions'] = ['notifications/intros', L10n::t('Introductions'), '', L10n::t('Friend Requests')]; $nav['introductions'] = ['notifications/intros', L10n::t('Introductions'), '', L10n::t('Friend Requests')];
}
if (in_array($_SESSION['page_flags'], [PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE])) {
$nav['notifications'] = ['notifications', L10n::t('Notifications'), '', L10n::t('Notifications')]; $nav['notifications'] = ['notifications', L10n::t('Notifications'), '', L10n::t('Notifications')];
$nav['notifications']['all'] = ['notifications/system', L10n::t('See all notifications'), '', '']; $nav['notifications']['all'] = ['notifications/system', L10n::t('See all notifications'), '', ''];
$nav['notifications']['mark'] = ['', L10n::t('Mark as seen'), '', L10n::t('Mark all system notifications seen')]; $nav['notifications']['mark'] = ['', L10n::t('Mark as seen'), '', L10n::t('Mark all system notifications seen')];
} }
}
$nav['messages'] = ['message', L10n::t('Messages'), '', L10n::t('Private mail')]; $nav['messages'] = ['message', L10n::t('Messages'), '', L10n::t('Private mail')];
$nav['messages']['inbox'] = ['message', L10n::t('Inbox'), '', L10n::t('Inbox')]; $nav['messages']['inbox'] = ['message', L10n::t('Inbox'), '', L10n::t('Inbox')];

View file

@ -343,159 +343,20 @@ class BBCode extends BaseObject
} }
/** /**
* @brief Convert a message into plaintext for connectors to other networks * @brief Converts a BBCode text into plaintext
* *
* @param array $b The message array that is about to be posted * @param bool $keep_urls Whether to keep URLs in the resulting plaintext
* @param int $limit The maximum number of characters when posting to that network
* @param bool $includedlinks Has an attached link to be included into the message?
* @param int $htmlmode This triggers the behaviour of the bbcode conversion
* @param string $target_network Name of the network where the post should go to.
* *
* @return string The converted message * @return string
*/ */
public static function toPlaintext($b, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = "") public static function toPlaintext($text, $keep_urls = true)
{ {
// Remove the hash tags $naked_text = preg_replace('/\[(.+?)\]/','', $text);
$URLSearchString = "^\[\]"; if (!$keep_urls) {
$body = preg_replace("/([#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $b["body"]); $naked_text = preg_replace('#https?\://[^\s<]+[^\s\.\)]#i', '', $naked_text);
// Add an URL element if the text contains a raw link
$body = preg_replace("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url]$2[/url]', $body);
// Remove the abstract
$body = self::stripAbstract($body);
// At first look at data that is attached via "type-..." stuff
// This will hopefully replaced with a dedicated bbcode later
//$post = self::getAttachedData($b["body"]);
$post = self::getAttachedData($body, $b);
if (($b["title"] != "") && ($post["text"] != "")) {
$post["text"] = trim($b["title"]."\n\n".$post["text"]);
} elseif ($b["title"] != "") {
$post["text"] = trim($b["title"]);
} }
$abstract = ""; return $naked_text;
// Fetch the abstract from the given target network
if ($target_network != "") {
$default_abstract = self::getAbstract($b["body"]);
$abstract = self::getAbstract($b["body"], $target_network);
// If we post to a network with no limit we only fetch
// an abstract exactly for this network
if (($limit == 0) && ($abstract == $default_abstract)) {
$abstract = "";
}
} else {// Try to guess the correct target network
switch ($htmlmode) {
case 8:
$abstract = self::getAbstract($b["body"], NETWORK_TWITTER);
break;
case 7:
$abstract = self::getAbstract($b["body"], NETWORK_STATUSNET);
break;
case 6:
$abstract = self::getAbstract($b["body"], NETWORK_APPNET);
break;
default: // We don't know the exact target.
// We fetch an abstract since there is a posting limit.
if ($limit > 0) {
$abstract = self::getAbstract($b["body"]);
}
}
}
if ($abstract != "") {
$post["text"] = $abstract;
if ($post["type"] == "text") {
$post["type"] = "link";
$post["url"] = $b["plink"];
}
}
$html = self::convert($post["text"].$post["after"], false, $htmlmode);
$msg = HTML::toPlaintext($html, 0, true);
$msg = trim(html_entity_decode($msg, ENT_QUOTES, 'UTF-8'));
$link = "";
if ($includedlinks) {
if ($post["type"] == "link") {
$link = $post["url"];
} elseif ($post["type"] == "text") {
$link = $post["url"];
} elseif ($post["type"] == "video") {
$link = $post["url"];
} elseif ($post["type"] == "photo") {
$link = $post["image"];
}
if (($msg == "") && isset($post["title"])) {
$msg = trim($post["title"]);
}
if (($msg == "") && isset($post["description"])) {
$msg = trim($post["description"]);
}
// If the link is already contained in the post, then it neeedn't to be added again
// But: if the link is beyond the limit, then it has to be added.
if (($link != "") && strstr($msg, $link)) {
$pos = strpos($msg, $link);
// Will the text be shortened in the link?
// Or is the link the last item in the post?
if (($limit > 0) && ($pos < $limit) && (($pos + 23 > $limit) || ($pos + strlen($link) == strlen($msg)))) {
$msg = trim(str_replace($link, "", $msg));
} elseif (($limit == 0) || ($pos < $limit)) {
// The limit has to be increased since it will be shortened - but not now
// Only do it with Twitter (htmlmode = 8)
if (($limit > 0) && (strlen($link) > 23) && ($htmlmode == 8)) {
$limit = $limit - 23 + strlen($link);
}
$link = "";
if ($post["type"] == "text") {
unset($post["url"]);
}
}
}
}
if ($limit > 0) {
// Reduce multiple spaces
// When posted to a network with limited space, we try to gain space where possible
while (strpos($msg, " ") !== false) {
$msg = str_replace(" ", " ", $msg);
}
// Twitter is using its own limiter, so we always assume that shortened links will have this length
if (iconv_strlen($link, "UTF-8") > 0) {
$limit = $limit - 23;
}
if (iconv_strlen($msg, "UTF-8") > $limit) {
if (($post["type"] == "text") && isset($post["url"])) {
$post["url"] = $b["plink"];
} elseif (!isset($post["url"])) {
$limit = $limit - 23;
$post["url"] = $b["plink"];
// Which purpose has this line? It is now uncommented, but left as a reminder
//} elseif (strpos($b["body"], "[share") !== false) {
// $post["url"] = $b["plink"];
} elseif (PConfig::get($b["uid"], "system", "no_intelligent_shortening")) {
$post["url"] = $b["plink"];
}
$msg = Plaintext::shorten($msg, $limit);
}
}
$post["text"] = trim($msg);
return($post);
} }
public static function scaleExternalImages($srctext, $include_link = true, $scale_replace = false) public static function scaleExternalImages($srctext, $include_link = true, $scale_replace = false)
@ -1947,7 +1808,7 @@ class BBCode extends BaseObject
* @param string $addon The addon for which the abstract is meant for * @param string $addon The addon for which the abstract is meant for
* @return string The abstract * @return string The abstract
*/ */
private static function getAbstract($text, $addon = "") public static function getAbstract($text, $addon = "")
{ {
$abstract = ""; $abstract = "";
$abstracts = []; $abstracts = [];

View file

@ -55,19 +55,24 @@ class Widget
} }
} }
return replace_macros(get_markup_template('peoplefind.tpl'), array( $nv = [];
'$findpeople' => L10n::t('Find People'), $nv['findpeople'] = L10n::t('Find People');
'$desc' => L10n::t('Enter name or interest'), $nv['desc'] = L10n::t('Enter name or interest');
'$label' => L10n::t('Connect/Follow'), $nv['label'] = L10n::t('Connect/Follow');
'$hint' => L10n::t('Examples: Robert Morgenstein, Fishing'), $nv['hint'] = L10n::t('Examples: Robert Morgenstein, Fishing');
'$findthem' => L10n::t('Find'), $nv['findthem'] = L10n::t('Find');
'$suggest' => L10n::t('Friend Suggestions'), $nv['suggest'] = L10n::t('Friend Suggestions');
'$similar' => L10n::t('Similar Interests'), $nv['similar'] = L10n::t('Similar Interests');
'$random' => L10n::t('Random Profile'), $nv['random'] = L10n::t('Random Profile');
'$inv' => L10n::t('Invite Friends'), $nv['inv'] = L10n::t('Invite Friends');
'$directory' => L10n::t('View Global Directory'), $nv['directory'] = L10n::t('Global Directory');
'$global_dir' => $global_dir $nv['global_dir'] = $global_dir;
)); $nv['local_directory'] = L10n::t('Local Directory');
$aside = [];
$aside['$nv'] = $nv;
return replace_macros(get_markup_template('peoplefind.tpl'), $aside);
} }
/** /**

View file

@ -246,7 +246,7 @@ class Addon
} else { } else {
// remove orphan hooks // remove orphan hooks
$condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]]; $condition = ['hook' => $name, 'file' => $hook[0], 'function' => $hook[1]];
dba::delete('hook', $condition); dba::delete('hook', $condition, ['cascade' => false]);
} }
} }

View file

@ -21,6 +21,7 @@ class Console extends \Asika\SimpleConsole\Console
'extract' => __NAMESPACE__ . '\Console\Extract', 'extract' => __NAMESPACE__ . '\Console\Extract',
'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock', 'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock',
'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence', 'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence',
'archivecontact' => __NAMESPACE__ . '\Console\ArchiveContact',
'autoinstall' => __NAMESPACE__ . '\Console\AutomaticInstallation', 'autoinstall' => __NAMESPACE__ . '\Console\AutomaticInstallation',
'maintenance' => __NAMESPACE__ . '\Console\Maintenance', 'maintenance' => __NAMESPACE__ . '\Console\Maintenance',
'newpassword' => __NAMESPACE__ . '\Console\NewPassword', 'newpassword' => __NAMESPACE__ . '\Console\NewPassword',
@ -42,6 +43,7 @@ Commands:
extract Generate translation string file for the Friendica project (deprecated) extract Generate translation string file for the Friendica project (deprecated)
globalcommunityblock Block remote profile from interacting with this node globalcommunityblock Block remote profile from interacting with this node
globalcommunitysilence Silence remote profile from global community page globalcommunitysilence Silence remote profile from global community page
archivecontact Archive a contact when you know that it isn't existing anymore
help Show help about a command, e.g (bin/console help config) help Show help about a command, e.g (bin/console help config)
autoinstall Starts automatic installation of friendica based on values from htconfig.php autoinstall Starts automatic installation of friendica based on values from htconfig.php
maintenance Set maintenance mode for this node maintenance Set maintenance mode for this node

View file

@ -0,0 +1,79 @@
<?php
namespace Friendica\Core\Console;
use Friendica\Core\L10n;
use dba;
/**
* @brief tool to archive a contact on the server
*
* With this tool you can archive a contact when you know that it isn't existing anymore.
* Normally this does happen automatically after a few days.
*
* License: AGPLv3 or later, same as Friendica
*
*/
class ArchiveContact extends \Asika\SimpleConsole\Console
{
protected $helpOptions = ['h', 'help', '?'];
protected function getHelp()
{
$help = <<<HELP
console archivecontact - archive a contact
Usage
bin/console archivecontact <profile_url> [-h|--help|-?] [-v]
Description
Archive a contact when you know that it isn't existing anymore. Normally this does happen automatically after a few days.
Options
-h|--help|-? Show help information
-v Show more debug information.
HELP;
return $help;
}
protected function doExecute()
{
$a = get_app();
if ($this->getOption('v')) {
$this->out('Class: ' . __CLASS__);
$this->out('Arguments: ' . var_export($this->args, true));
$this->out('Options: ' . var_export($this->options, true));
}
if (count($this->args) == 0) {
$this->out($this->getHelp());
return 0;
}
if (count($this->args) > 1) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
}
require_once '.htconfig.php';
$result = \dba::connect($db_host, $db_user, $db_pass, $db_data);
unset($db_host, $db_user, $db_pass, $db_data);
if (!$result) {
throw new \RuntimeException('Unable to connect to database');
}
$nurl = normalise_link($this->getArgument(0));
if (!dba::exists('contact', ['nurl' => $nurl, 'archive' => false])) {
throw new \RuntimeException(L10n::t('Could not find any unarchived contact entry for this URL (%s)', $nurl));
}
if (dba::update('contact', ['archive' => true], ['nurl' => $nurl])) {
$condition = ["`cid` IN (SELECT `id` FROM `contact` WHERE `archive`)"];
dba::delete('queue', $condition);
$this->out(L10n::t('The contact entries have been archived'));
} else {
throw new \RuntimeException('The contact archival failed.');
}
return 0;
}
}

View file

@ -423,7 +423,7 @@ class Contact extends BaseObject
// Fetch the data from the gcontact table // Fetch the data from the gcontact table
if (!DBM::is_result($r)) { if (!DBM::is_result($r)) {
$s = dba::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`, $s = dba::p("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self` `keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, 0 AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
FROM `gcontact` WHERE `nurl` = ?", normalise_link($url)); FROM `gcontact` WHERE `nurl` = ?", normalise_link($url));
$r = dba::inArray($s); $r = dba::inArray($s);
} }

View file

@ -7,6 +7,7 @@
namespace Friendica\Model; namespace Friendica\Model;
use Friendica\BaseObject; use Friendica\BaseObject;
use Friendica\Content\Text;
use Friendica\Core\Addon; use Friendica\Core\Addon;
use Friendica\Core\Config; use Friendica\Core\Config;
use Friendica\Core\L10n; use Friendica\Core\L10n;
@ -838,6 +839,101 @@ class Item extends BaseObject
return $current_post; return $current_post;
} }
/**
* @brief Distributes public items to the receivers
*
* @param integer $itemid Item ID that should be added
*/
public static function distribute($itemid)
{
$condition = ["`id` IN (SELECT `parent` FROM `item` WHERE `id` = ?)", $itemid];
$parent = dba::selectFirst('item', ['owner-id'], $condition);
if (!DBM::is_result($parent)) {
return;
}
// Only distribute public items from native networks
$condition = ['id' => $itemid, 'uid' => 0,
'network' => [NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""],
'visible' => true, 'deleted' => false, 'moderated' => false, 'private' => false];
$item = dba::selectFirst('item', [], ['id' => $itemid]);
if (!DBM::is_result($item)) {
return;
}
unset($item['id']);
unset($item['parent']);
unset($item['mention']);
unset($item['wall']);
unset($item['origin']);
unset($item['starred']);
unset($item['rendered-hash']);
unset($item['rendered-html']);
$users = [];
$condition = ["`nurl` IN (SELECT `nurl` FROM `contact` WHERE `id` = ?) AND `uid` != 0 AND NOT `blocked` AND NOT `readonly` AND `rel` IN (?, ?)",
$parent['owner-id'], CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
$contacts = dba::select('contact', ['uid'], $condition);
while ($contact = dba::fetch($contacts)) {
$users[$contact['uid']] = $contact['uid'];
}
if ($item['uri'] != $item['parent-uri']) {
$parents = dba::select('item', ['uid'], ["`uri` = ? AND `uid` != 0", $item['parent-uri']]);
while ($parent = dba::fetch($parents)) {
$users[$parent['uid']] = $parent['uid'];
}
}
foreach ($users as $uid) {
self::storeForUser($itemid, $item, $uid);
}
}
/**
* @brief Store public items for the receivers
*
* @param integer $itemid Item ID that should be added
* @param array $item The item entry that will be stored
* @param integer $uid The user that will receive the item entry
*/
private static function storeForUser($itemid, $item, $uid)
{
$item['uid'] = $uid;
$item['origin'] = 0;
$item['wall'] = 0;
if ($item['uri'] == $item['parent-uri']) {
$item['contact-id'] = Contact::getIdForURL($item['owner-link'], $uid);
} else {
$item['contact-id'] = Contact::getIdForURL($item['author-link'], $uid);
}
if (empty($item['contact-id'])) {
$self = dba::selectFirst('contact', ['id'], ['self' => true, 'uid' => $uid]);
if (!DBM::is_result($self)) {
return;
}
$item['contact-id'] = $self['id'];
}
if (in_array($item['type'], ["net-comment", "wall-comment"])) {
$item['type'] = 'remote-comment';
} elseif ($item['type'] == 'wall') {
$item['type'] = 'remote';
}
/// @todo Handling of "event-id"
$distributed = self::insert($item, false, false, true);
if (!$distributed) {
logger("Distributed public item " . $itemid . " for user " . $uid . " wasn't stored", LOGGER_DEBUG);
} else {
logger("Distributed public item " . $itemid . " for user " . $uid . " with id " . $distributed, LOGGER_DEBUG);
}
}
/** /**
* @brief Add a shadow entry for a given item id that is a thread starter * @brief Add a shadow entry for a given item id that is a thread starter
* *
@ -849,8 +945,8 @@ class Item extends BaseObject
*/ */
public static function addShadow($itemid) public static function addShadow($itemid)
{ {
$fields = ['uid', 'wall', 'private', 'moderated', 'visible', 'contact-id', 'deleted', 'network', 'author-id', 'owner-id']; $fields = ['uid', 'private', 'moderated', 'visible', 'deleted', 'network'];
$condition = ["`id` = ? AND (`parent` = ? OR `parent` = 0)", $itemid, $itemid]; $condition = ['id' => $itemid, 'parent' => [0, $itemid]];
$item = dba::selectFirst('item', $fields, $condition); $item = dba::selectFirst('item', $fields, $condition);
if (!DBM::is_result($item)) { if (!DBM::is_result($item)) {
@ -872,24 +968,6 @@ class Item extends BaseObject
return; return;
} }
// Only do these checks if the post isn't a wall post
if (!$item["wall"]) {
// Check, if hide-friends is activated - then don't do a shadow entry
if (dba::exists('profile', ['is-default' => true, 'uid' => $item['uid'], 'hide-friends' => true])) {
return;
}
// Check if the contact is hidden or blocked
if (!dba::exists('contact', ['hidden' => false, 'blocked' => false, 'id' => $item['contact-id']])) {
return;
}
}
// Only add a shadow, if the profile isn't hidden
if (dba::exists('user', ['uid' => $item['uid'], 'hidewall' => true])) {
return;
}
$item = dba::selectFirst('item', [], ['id' => $itemid]); $item = dba::selectFirst('item', [], ['id' => $itemid]);
if (DBM::is_result($item) && ($item["allow_cid"] == '') && ($item["allow_gid"] == '') && if (DBM::is_result($item) && ($item["allow_cid"] == '') && ($item["allow_gid"] == '') &&
@ -897,10 +975,15 @@ class Item extends BaseObject
if (!dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) { if (!dba::exists('item', ['uri' => $item['uri'], 'uid' => 0])) {
// Preparing public shadow (removing user specific data) // Preparing public shadow (removing user specific data)
unset($item['id']);
$item['uid'] = 0; $item['uid'] = 0;
$item['origin'] = 0; unset($item['id']);
$item['wall'] = 0; unset($item['parent']);
unset($item['wall']);
unset($item['mention']);
unset($item['origin']);
unset($item['starred']);
unset($item['rendered-hash']);
unset($item['rendered-html']);
if ($item['uri'] == $item['parent-uri']) { if ($item['uri'] == $item['parent-uri']) {
$item['contact-id'] = Contact::getIdForURL($item['owner-link']); $item['contact-id'] = Contact::getIdForURL($item['owner-link']);
} else { } else {
@ -954,11 +1037,20 @@ class Item extends BaseObject
return; return;
} }
// Save "origin" and "parent" state
$origin = $item['origin'];
$parent = $item['parent'];
// Preparing public shadow (removing user specific data) // Preparing public shadow (removing user specific data)
unset($item['id']);
$item['uid'] = 0; $item['uid'] = 0;
$item['origin'] = 0; unset($item['id']);
$item['wall'] = 0; unset($item['parent']);
unset($item['wall']);
unset($item['mention']);
unset($item['origin']);
unset($item['starred']);
unset($item['rendered-hash']);
unset($item['rendered-html']);
$item['contact-id'] = Contact::getIdForURL($item['author-link']); $item['contact-id'] = Contact::getIdForURL($item['author-link']);
if (in_array($item['type'], ["net-comment", "wall-comment"])) { if (in_array($item['type'], ["net-comment", "wall-comment"])) {
@ -970,6 +1062,14 @@ class Item extends BaseObject
$public_shadow = self::insert($item, false, false, true); $public_shadow = self::insert($item, false, false, true);
logger("Stored public shadow for comment ".$item['uri']." under id ".$public_shadow, LOGGER_DEBUG); logger("Stored public shadow for comment ".$item['uri']." under id ".$public_shadow, LOGGER_DEBUG);
// If this was a comment to a Diaspora post we don't get our comment back.
// This means that we have to distribute the comment by ourselves.
if ($origin) {
if (dba::exists('item', ['id' => $parent, 'network' => NETWORK_DIASPORA])) {
self::distribute($public_shadow);
}
}
} }
/** /**
@ -977,35 +1077,35 @@ class Item extends BaseObject
* if possible and not already present. * if possible and not already present.
* Expects "body" element to exist in $arr. * Expects "body" element to exist in $arr.
*/ */
private static function addLanguageInPostopts(&$arr) private static function addLanguageInPostopts(&$item)
{ {
if (x($arr, 'postopts')) { if (!empty($item['postopts'])) {
if (strstr($arr['postopts'], 'lang=')) { if (strstr($item['postopts'], 'lang=')) {
// do not override // do not override
return; return;
} }
$postopts = $arr['postopts']; $postopts = $item['postopts'];
} else { } else {
$postopts = ""; $postopts = "";
} }
$naked_body = preg_replace('/\[(.+?)\]/','', $arr['body']); $naked_body = Text\BBCode::toPlaintext($item['body'], false);
$l = new Text_LanguageDetect();
$lng = $l->detect($naked_body, 3);
if (sizeof($lng) > 0) { $languages = (new Text_LanguageDetect())->detect($naked_body, 3);
if ($postopts != "") {
if (sizeof($languages) > 0) {
if ($postopts != '') {
$postopts .= '&'; // arbitrary separator, to be reviewed $postopts .= '&'; // arbitrary separator, to be reviewed
} }
$postopts .= 'lang='; $postopts .= 'lang=';
$sep = ""; $sep = "";
foreach ($lng as $language => $score) { foreach ($languages as $language => $score) {
$postopts .= $sep . $language . ";" . $score; $postopts .= $sep . $language . ";" . $score;
$sep = ':'; $sep = ':';
} }
$arr['postopts'] = $postopts; $item['postopts'] = $postopts;
} }
} }

176
src/Model/ItemContent.php Normal file
View file

@ -0,0 +1,176 @@
<?php
/**
* @file src/Model/ItemContent.php
*/
namespace Friendica\Model;
use Friendica\BaseObject;
use Friendica\Content\Text;
use Friendica\Core\PConfig;
require_once 'boot.php';
require_once 'include/items.php';
require_once 'include/text.php';
class ItemContent extends BaseObject
{
/**
* @brief Convert a message into plaintext for connectors to other networks
*
* @param array $item The message array that is about to be posted
* @param int $limit The maximum number of characters when posting to that network
* @param bool $includedlinks Has an attached link to be included into the message?
* @param int $htmlmode This controls the behavior of the BBCode conversion
* @param string $target_network Name of the network where the post should go to.
*
* @see \Friendica\Content\Text\BBCode::getAttachedData
*
* @return array Same array structure than \Friendica\Content\Text\BBCode::getAttachedData
*/
public static function getPlaintextPost($item, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = '')
{
// Remove hashtags
$URLSearchString = '^\[\]';
$body = preg_replace("/([#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $item['body']);
// Add an URL element if the text contains a raw link
$body = preg_replace('/([^\]\=\'"]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism',
'$1[url]$2[/url]', $body);
// Remove the abstract
$body = Text\BBCode::stripAbstract($body);
// At first look at data that is attached via "type-..." stuff
// This will hopefully replaced with a dedicated bbcode later
//$post = self::getAttachedData($b['body']);
$post = Text\BBCode::getAttachedData($body, $item);
if (($item['title'] != '') && ($post['text'] != '')) {
$post['text'] = trim($item['title'] . "\n\n" . $post['text']);
} elseif ($item['title'] != '') {
$post['text'] = trim($item['title']);
}
$abstract = '';
// Fetch the abstract from the given target network
if ($target_network != '') {
$default_abstract = Text\BBCode::getAbstract($item['body']);
$abstract = Text\BBCode::getAbstract($item['body'], $target_network);
// If we post to a network with no limit we only fetch
// an abstract exactly for this network
if (($limit == 0) && ($abstract == $default_abstract)) {
$abstract = '';
}
} else {// Try to guess the correct target network
switch ($htmlmode) {
case 8:
$abstract = Text\BBCode::getAbstract($item['body'], NETWORK_TWITTER);
break;
case 7:
$abstract = Text\BBCode::getAbstract($item['body'], NETWORK_STATUSNET);
break;
case 6:
$abstract = Text\BBCode::getAbstract($item['body'], NETWORK_APPNET);
break;
default: // We don't know the exact target.
// We fetch an abstract since there is a posting limit.
if ($limit > 0) {
$abstract = Text\BBCode::getAbstract($item['body']);
}
}
}
if ($abstract != '') {
$post['text'] = $abstract;
if ($post['type'] == 'text') {
$post['type'] = 'link';
$post['url'] = $item['plink'];
}
}
$html = Text\BBCode::convert($post['text'] . $post['after'], false, $htmlmode);
$msg = Text\HTML::toPlaintext($html, 0, true);
$msg = trim(html_entity_decode($msg, ENT_QUOTES, 'UTF-8'));
$link = '';
if ($includedlinks) {
if ($post['type'] == 'link') {
$link = $post['url'];
} elseif ($post['type'] == 'text') {
$link = $post['url'];
} elseif ($post['type'] == 'video') {
$link = $post['url'];
} elseif ($post['type'] == 'photo') {
$link = $post['image'];
}
if (($msg == '') && isset($post['title'])) {
$msg = trim($post['title']);
}
if (($msg == '') && isset($post['description'])) {
$msg = trim($post['description']);
}
// If the link is already contained in the post, then it neeedn't to be added again
// But: if the link is beyond the limit, then it has to be added.
if (($link != '') && strstr($msg, $link)) {
$pos = strpos($msg, $link);
// Will the text be shortened in the link?
// Or is the link the last item in the post?
if (($limit > 0) && ($pos < $limit) && (($pos + 23 > $limit) || ($pos + strlen($link) == strlen($msg)))) {
$msg = trim(str_replace($link, '', $msg));
} elseif (($limit == 0) || ($pos < $limit)) {
// The limit has to be increased since it will be shortened - but not now
// Only do it with Twitter (htmlmode = 8)
if (($limit > 0) && (strlen($link) > 23) && ($htmlmode == 8)) {
$limit = $limit - 23 + strlen($link);
}
$link = '';
if ($post['type'] == 'text') {
unset($post['url']);
}
}
}
}
if ($limit > 0) {
// Reduce multiple spaces
// When posted to a network with limited space, we try to gain space where possible
while (strpos($msg, ' ') !== false) {
$msg = str_replace(' ', ' ', $msg);
}
// Twitter is using its own limiter, so we always assume that shortened links will have this length
if (iconv_strlen($link, 'UTF-8') > 0) {
$limit = $limit - 23;
}
if (iconv_strlen($msg, 'UTF-8') > $limit) {
if (($post['type'] == 'text') && isset($post['url'])) {
$post['url'] = $item['plink'];
} elseif (!isset($post['url'])) {
$limit = $limit - 23;
$post['url'] = $item['plink'];
} elseif (strpos($item['body'], '[share') !== false) {
$post['url'] = $item['plink'];
} elseif (PConfig::get($item['uid'], 'system', 'no_intelligent_shortening')) {
$post['url'] = $item['plink'];
}
$msg = Text\Plaintext::shorten($msg, $limit);
}
}
$post['text'] = trim($msg);
return $post;
}
}

View file

@ -92,14 +92,14 @@ class Profile
{ {
$user = dba::selectFirst('user', ['uid'], ['nickname' => $nickname]); $user = dba::selectFirst('user', ['uid'], ['nickname' => $nickname]);
if (!$user && !count($user) && !count($profiledata)) { if (!DBM::is_result($user) && empty($profiledata)) {
logger('profile error: ' . $a->query_string, LOGGER_DEBUG); logger('profile error: ' . $a->query_string, LOGGER_DEBUG);
notice(L10n::t('Requested account is not available.') . EOL); notice(L10n::t('Requested account is not available.') . EOL);
$a->error = 404; $a->error = 404;
return; return;
} }
if (!x($a->page, 'aside')) { if (empty($a->page['aside'])) {
$a->page['aside'] = ''; $a->page['aside'] = '';
} }
@ -157,10 +157,6 @@ class Profile
require_once $theme_info_file; require_once $theme_info_file;
} }
if (!x($a->page, 'aside')) {
$a->page['aside'] = '';
}
if (local_user() && local_user() == $a->profile['uid'] && $profiledata) { if (local_user() && local_user() == $a->profile['uid'] && $profiledata) {
$a->page['aside'] .= replace_macros( $a->page['aside'] .= replace_macros(
get_markup_template('profile_edlink.tpl'), get_markup_template('profile_edlink.tpl'),
@ -644,26 +640,26 @@ class Profile
$classtoday = ''; $classtoday = '';
$s = dba::p( $s = dba::p(
"SELECT * "SELECT `event`.*
FROM `event` FROM `event`
INNER JOIN `item`
ON `item`.`uid` = `event`.`uid`
AND `item`.`parent-uri` = `event`.`uri`
WHERE `event`.`uid` = ? WHERE `event`.`uid` = ?
AND `event`.`type` != 'birthday' AND `event`.`type` != 'birthday'
AND `event`.`start` < ? AND `event`.`start` < ?
AND `event`.`start` >= ? AND `event`.`start` >= ?
AND NOT EXISTS ( AND `item`.`author-id` = ?
SELECT `id` AND (`item`.`verb` = ? OR `item`.`verb` = ?)
FROM `item`
WHERE `item`.`uid` = `event`.`uid`
AND `item`.`parent-uri` = `event`.`uri`
AND `item`.`verb` = ?
AND `item`.`visible` AND `item`.`visible`
AND NOT `item`.`deleted` AND NOT `item`.`deleted`
)
ORDER BY `event`.`start` ASC", ORDER BY `event`.`start` ASC",
local_user(), local_user(),
DateTimeFormat::utc('now + 7 days'), DateTimeFormat::utc('now + 7 days'),
DateTimeFormat::utc('now - 1 days'), DateTimeFormat::utc('now - 1 days'),
ACTIVITY_ATTENDNO public_contact(),
ACTIVITY_ATTEND,
ACTIVITY_ATTENDMAYBE
); );
$r = []; $r = [];
@ -954,7 +950,7 @@ class Profile
]; ];
} }
if ((!$is_owner) && ((count($a->profile)) || (!$a->profile['hide-friends']))) { if (!$is_owner && empty($a->profile['hide-friends'])) {
$tabs[] = [ $tabs[] = [
'label' => L10n::t('Contacts'), 'label' => L10n::t('Contacts'),
'url' => System::baseUrl() . '/viewcontacts/' . $nickname, 'url' => System::baseUrl() . '/viewcontacts/' . $nickname,

View file

@ -78,7 +78,7 @@ class Post extends BaseObject
} }
// Prepare the children // Prepare the children
if (count($data['children'])) { if (!empty($data['children'])) {
foreach ($data['children'] as $item) { foreach ($data['children'] as $item) {
// Only add will be displayed // Only add will be displayed
if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) { if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) {

View file

@ -1197,6 +1197,7 @@ class DFRN
$ret = Network::curl($url); $ret = Network::curl($url);
if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) { if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) {
Contact::markForArchival($contact);
return -2; // timed out return -2; // timed out
} }
@ -1204,24 +1205,28 @@ class DFRN
$curl_stat = $a->get_curl_code(); $curl_stat = $a->get_curl_code();
if (empty($curl_stat)) { if (empty($curl_stat)) {
Contact::markForArchival($contact);
return -3; // timed out return -3; // timed out
} }
logger('dfrn_deliver: ' . $xml, LOGGER_DATA); logger('dfrn_deliver: ' . $xml, LOGGER_DATA);
if (empty($xml)) { if (empty($xml)) {
Contact::markForArchival($contact);
return 3; return 3;
} }
if (strpos($xml, '<?xml') === false) { if (strpos($xml, '<?xml') === false) {
logger('dfrn_deliver: no valid XML returned'); logger('dfrn_deliver: no valid XML returned');
logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA); logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA);
Contact::markForArchival($contact);
return 3; return 3;
} }
$res = XML::parseString($xml); $res = XML::parseString($xml);
if ((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) { if ((intval($res->status) != 0) || !strlen($res->challenge) || !strlen($res->dfrn_id)) {
Contact::markForArchival($contact);
return ($res->status ? $res->status : 3); return ($res->status ? $res->status : 3);
} }
@ -1274,6 +1279,7 @@ class DFRN
if ($final_dfrn_id != $orig_id) { if ($final_dfrn_id != $orig_id) {
logger('dfrn_deliver: wrong dfrn_id.'); logger('dfrn_deliver: wrong dfrn_id.');
// did not decode properly - cannot trust this site // did not decode properly - cannot trust this site
Contact::markForArchival($contact);
return 3; return 3;
} }
@ -1309,6 +1315,7 @@ class DFRN
break; break;
default: default:
logger("rino: invalid requested version '$rino_remote_version'"); logger("rino: invalid requested version '$rino_remote_version'");
Contact::markForArchival($contact);
return -8; return -8;
} }
@ -1346,22 +1353,26 @@ class DFRN
$curl_stat = $a->get_curl_code(); $curl_stat = $a->get_curl_code();
if (empty($curl_stat) || empty($xml)) { if (empty($curl_stat) || empty($xml)) {
Contact::markForArchival($contact);
return -9; // timed out return -9; // timed out
} }
if (($curl_stat == 503) && stristr($a->get_curl_headers(), 'retry-after')) { if (($curl_stat == 503) && stristr($a->get_curl_headers(), 'retry-after')) {
Contact::markForArchival($contact);
return -10; return -10;
} }
if (strpos($xml, '<?xml') === false) { if (strpos($xml, '<?xml') === false) {
logger('dfrn_deliver: phase 2: no valid XML returned'); logger('dfrn_deliver: phase 2: no valid XML returned');
logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA); logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA);
Contact::markForArchival($contact);
return 3; return 3;
} }
$res = XML::parseString($xml); $res = XML::parseString($xml);
if (!isset($res->status)) { if (!isset($res->status)) {
Contact::markForArchival($contact);
return -11; return -11;
} }
@ -1374,7 +1385,7 @@ class DFRN
logger('Delivery returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG); logger('Delivery returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG);
} }
if ($res->status == 200) { if (($res->status >= 200) && ($res->status <= 299)) {
Contact::unmarkForArchival($contact); Contact::unmarkForArchival($contact);
} }
@ -1394,6 +1405,7 @@ class DFRN
{ {
$a = get_app(); $a = get_app();
if (!$public_batch) {
if (empty($contact['addr'])) { if (empty($contact['addr'])) {
logger('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.'); logger('Empty contact handle for ' . $contact['id'] . ' - ' . $contact['url'] . ' - trying to update it.');
if (Contact::updateFromProbe($contact['id'])) { if (Contact::updateFromProbe($contact['id'])) {
@ -1403,6 +1415,7 @@ class DFRN
if (empty($contact['addr'])) { if (empty($contact['addr'])) {
logger('Unable to find contact handle for ' . $contact['id'] . ' - ' . $contact['url']); logger('Unable to find contact handle for ' . $contact['id'] . ' - ' . $contact['url']);
Contact::markForArchival($contact);
return -21; return -21;
} }
} }
@ -1410,12 +1423,23 @@ class DFRN
$fcontact = Diaspora::personByHandle($contact['addr']); $fcontact = Diaspora::personByHandle($contact['addr']);
if (empty($fcontact)) { if (empty($fcontact)) {
logger('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']); logger('Unable to find contact details for ' . $contact['id'] . ' - ' . $contact['addr']);
Contact::markForArchival($contact);
return -22; return -22;
} }
}
$envelope = Diaspora::buildMessage($atom, $owner, $contact, $owner['uprvkey'], $fcontact['pubkey'], $public_batch); $envelope = Diaspora::buildMessage($atom, $owner, $contact, $owner['uprvkey'], $fcontact['pubkey'], $public_batch);
$dest_url = ($public_batch ? $fcontact["batch"] : $contact["notify"]); // Create the endpoint for public posts. This is some WIP and should later be added to the probing
if ($public_batch && empty($contact["batch"])) {
$parts = parse_url($contact["notify"]);
$path_parts = explode('/', $parts['path']);
array_pop($path_parts);
$parts['path'] = implode('/', $path_parts);
$contact["batch"] = Network::unparseURL($parts);
}
$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
$content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json"); $content_type = ($public_batch ? "application/magic-envelope+xml" : "application/json");
@ -1424,22 +1448,26 @@ class DFRN
$curl_stat = $a->get_curl_code(); $curl_stat = $a->get_curl_code();
if (empty($curl_stat) || empty($xml)) { if (empty($curl_stat) || empty($xml)) {
logger('Empty answer from ' . $contact['id'] . ' - ' . $dest_url); logger('Empty answer from ' . $contact['id'] . ' - ' . $dest_url);
Contact::markForArchival($contact);
return -9; // timed out return -9; // timed out
} }
if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) { if (($curl_stat == 503) && (stristr($a->get_curl_headers(), 'retry-after'))) {
Contact::markForArchival($contact);
return -10; return -10;
} }
if (strpos($xml, '<?xml') === false) { if (strpos($xml, '<?xml') === false) {
logger('No valid XML returned from ' . $contact['id'] . ' - ' . $dest_url); logger('No valid XML returned from ' . $contact['id'] . ' - ' . $dest_url);
logger('Returned XML: ' . $xml, LOGGER_DATA); logger('Returned XML: ' . $xml, LOGGER_DATA);
Contact::markForArchival($contact);
return 3; return 3;
} }
$res = XML::parseString($xml); $res = XML::parseString($xml);
if (empty($res->status)) { if (empty($res->status)) {
Contact::markForArchival($contact);
return -23; return -23;
} }
@ -1447,7 +1475,7 @@ class DFRN
logger('Transmit to ' . $dest_url . ' returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG); logger('Transmit to ' . $dest_url . ' returned status '.$res->status.' - '.$res->message, LOGGER_DEBUG);
} }
if ($res->status == 200) { if (($res->status >= 200) && ($res->status <= 299)) {
Contact::unmarkForArchival($contact); Contact::unmarkForArchival($contact);
} }
@ -1467,33 +1495,33 @@ class DFRN
// Check for duplicates // Check for duplicates
$r = q( $r = q(
"SELECT `id` FROM `event` WHERE `uid` = %d AND `cid` = %d AND `start` = '%s' AND `type` = '%s' LIMIT 1", "SELECT `id` FROM `event` WHERE `uid` = %d AND `cid` = %d AND `start` = '%s' AND `type` = '%s' LIMIT 1",
intval($contact["uid"]), intval($contact['uid']),
intval($contact["id"]), intval($contact['id']),
dbesc(DateTimeFormat::utc($birthday)), dbesc(DateTimeFormat::utc($birthday)),
dbesc("birthday") dbesc('birthday')
); );
if (DBM::is_result($r)) { if (DBM::is_result($r)) {
return; return;
} }
logger("updating birthday: ".$birthday." for contact ".$contact["id"]); logger('updating birthday: ' . $birthday . ' for contact ' . $contact['id']);
$bdtext = L10n::t("%s\'s birthday", $contact["name"]); $bdtext = L10n::t('%s\'s birthday', $contact['name']);
$bdtext2 = L10n::t("Happy Birthday %s", " [url=".$contact["url"]."]".$contact["name"]."[/url]"); $bdtext2 = L10n::t('Happy Birthday %s', ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]');
$r = q( $r = q(
"INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`) "INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`)
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s') ",
intval($contact["uid"]), intval($contact['uid']),
intval($contact["id"]), intval($contact['id']),
dbesc(DateTimeFormat::utcNow()), dbesc(DateTimeFormat::utcNow()),
dbesc(DateTimeFormat::utcNow()), dbesc(DateTimeFormat::utcNow()),
dbesc(DateTimeFormat::utc($birthday)), dbesc(DateTimeFormat::utc($birthday)),
dbesc(DateTimeFormat::utc($birthday . " + 1 day ")), dbesc(DateTimeFormat::utc($birthday . ' + 1 day ')),
dbesc($bdtext), dbesc($bdtext),
dbesc($bdtext2), dbesc($bdtext2),
dbesc("birthday") dbesc('birthday')
); );
} }
@ -2747,6 +2775,10 @@ class DFRN
if ($posted_id) { if ($posted_id) {
logger("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id, LOGGER_DEBUG); logger("Reply from contact ".$item["contact-id"]." was stored with id ".$posted_id, LOGGER_DEBUG);
if ($item['uid'] == 0) {
Item::distribute($posted_id);
}
$item["id"] = $posted_id; $item["id"] = $posted_id;
$r = q( $r = q(
@ -2771,7 +2803,7 @@ class DFRN
logger('ignoring read-only contact '.$importer["id"]); logger('ignoring read-only contact '.$importer["id"]);
return; return;
} }
if ($importer["uid"] == 0) { if (($importer["uid"] == 0) && ($importer["importer_uid"] != 0)) {
logger("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.", LOGGER_DEBUG); logger("Contact ".$importer["id"]." isn't known to user ".$importer["importer_uid"].". The post will be ignored.", LOGGER_DEBUG);
return; return;
} }
@ -2801,6 +2833,10 @@ class DFRN
logger("Item was stored with id ".$posted_id, LOGGER_DEBUG); logger("Item was stored with id ".$posted_id, LOGGER_DEBUG);
if ($item['uid'] == 0) {
Item::distribute($posted_id);
}
if (stristr($item["verb"], ACTIVITY_POKE)) { if (stristr($item["verb"], ACTIVITY_POKE)) {
self::doPoke($item, $importer, $posted_id); self::doPoke($item, $importer, $posted_id);
} }
@ -2923,6 +2959,9 @@ class DFRN
logger("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG); logger("Import DFRN message for user " . $importer["importer_uid"] . " from contact " . $importer["id"], LOGGER_DEBUG);
// is it a public forum? Private forums aren't exposed with this method
$forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()")->item(0)->nodeValue);
// The account type is new since 3.5.1 // The account type is new since 3.5.1
if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) { if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) {
$accounttype = intval($xpath->evaluate("/atom:feed/dfrn:account_type/text()")->item(0)->nodeValue); $accounttype = intval($xpath->evaluate("/atom:feed/dfrn:account_type/text()")->item(0)->nodeValue);
@ -2930,17 +2969,17 @@ class DFRN
if ($accounttype != $importer["contact-type"]) { if ($accounttype != $importer["contact-type"]) {
dba::update('contact', ['contact-type' => $accounttype], ['id' => $importer["id"]]); dba::update('contact', ['contact-type' => $accounttype], ['id' => $importer["id"]]);
} }
// A forum contact can either have set "forum" or "prv" - but not both
if (($accounttype == ACCOUNT_TYPE_COMMUNITY) && (($forum != $importer["forum"]) || ($forum == $importer["prv"]))) {
$condition = ['(`forum` != ? OR `prv` != ?) AND `id` = ?', $forum, !$forum, $importer["id"]];
dba::update('contact', ['forum' => $forum, 'prv' => !$forum], $condition);
} }
} elseif ($forum != $importer["forum"]) { // Deprecated since 3.5.1
// is it a public forum? Private forums aren't supported with this method
// This is deprecated since 3.5.1
$forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()")->item(0)->nodeValue);
if ($forum != $importer["forum"]) {
$condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]]; $condition = ['`forum` != ? AND `id` = ?', $forum, $importer["id"]];
dba::update('contact', ['forum' => $forum], $condition); dba::update('contact', ['forum' => $forum], $condition);
} }
// We are processing relocations even if we are ignoring a contact // We are processing relocations even if we are ignoring a contact
$relocations = $xpath->query("/atom:feed/dfrn:relocate"); $relocations = $xpath->query("/atom:feed/dfrn:relocate");
foreach ($relocations as $relocation) { foreach ($relocations as $relocation) {

View file

@ -590,59 +590,15 @@ class Diaspora
return false; return false;
} }
if (!($postdata = self::validPosting($msg))) { if (!($fields = self::validPosting($msg))) {
logger("Invalid posting"); logger("Invalid posting");
return false; return false;
} }
$fields = $postdata['fields'];
// Is it a an action (comment, like, ...) for our own post?
if (isset($fields->parent_guid) && !$postdata["relayed"]) {
$guid = notags(unxmlify($fields->parent_guid));
$importer = self::importerForGuid($guid);
if (is_array($importer)) {
logger("delivering to origin: ".$importer["name"]);
$message_id = self::dispatch($importer, $msg, $fields);
return $message_id;
}
}
// Process item retractions. This has to be done separated from the other stuff,
// since retractions for comments could come even from non followers.
if (!empty($fields) && in_array($fields->getName(), ['retraction'])) {
$target = notags(unxmlify($fields->target_type));
if (in_array($target, ["Comment", "Like", "Post", "Reshare", "StatusMessage"])) {
logger('processing retraction for '.$target, LOGGER_DEBUG);
$importer = ["uid" => 0, "page-flags" => PAGE_FREELOVE]; $importer = ["uid" => 0, "page-flags" => PAGE_FREELOVE];
$message_id = self::dispatch($importer, $msg, $fields); $success = self::dispatch($importer, $msg, $fields);
return $message_id;
}
}
// Now distribute it to the followers return $success;
$r = q(
"SELECT `user`.* FROM `user` WHERE `user`.`uid` IN
(SELECT `contact`.`uid` FROM `contact` WHERE `contact`.`network` = '%s' AND `contact`.`addr` = '%s')
AND NOT `account_expired` AND NOT `account_removed`",
dbesc(NETWORK_DIASPORA),
dbesc($msg["author"])
);
if (DBM::is_result($r)) {
foreach ($r as $rr) {
logger("delivering to: ".$rr["username"]);
self::dispatch($rr, $msg, $fields);
}
} elseif (!Config::get('system', 'relay_subscribe', false)) {
logger("Unwanted message from ".$msg["author"]." send by ".$_SERVER["REMOTE_ADDR"]." with ".$_SERVER["HTTP_USER_AGENT"].": ".print_r($msg, true), LOGGER_DEBUG);
} else {
// Use a dummy importer to import the data for the public copy
$importer = ["uid" => 0, "page-flags" => PAGE_FREELOVE];
$message_id = self::dispatch($importer, $msg, $fields);
}
return $message_id;
} }
/** /**
@ -662,11 +618,13 @@ class Diaspora
// This is only needed for private postings since this is already done for public ones before // This is only needed for private postings since this is already done for public ones before
if (is_null($fields)) { if (is_null($fields)) {
if (!($postdata = self::validPosting($msg))) { $private = true;
if (!($fields = self::validPosting($msg))) {
logger("Invalid posting"); logger("Invalid posting");
return false; return false;
} }
$fields = $postdata['fields']; } else {
$private = false;
} }
$type = $fields->getName(); $type = $fields->getName();
@ -675,27 +633,47 @@ class Diaspora
switch ($type) { switch ($type) {
case "account_migration": case "account_migration":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveAccountMigration($importer, $fields); return self::receiveAccountMigration($importer, $fields);
case "account_deletion": case "account_deletion":
return self::receiveAccountDeletion($importer, $fields); return self::receiveAccountDeletion($fields);
case "comment": case "comment":
return self::receiveComment($importer, $sender, $fields, $msg["message"]); return self::receiveComment($importer, $sender, $fields, $msg["message"]);
case "contact": case "contact":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveContactRequest($importer, $fields); return self::receiveContactRequest($importer, $fields);
case "conversation": case "conversation":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveConversation($importer, $msg, $fields); return self::receiveConversation($importer, $msg, $fields);
case "like": case "like":
return self::receiveLike($importer, $sender, $fields); return self::receiveLike($importer, $sender, $fields);
case "message": case "message":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveMessage($importer, $fields); return self::receiveMessage($importer, $fields);
case "participation": case "participation":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveParticipation($importer, $fields); return self::receiveParticipation($importer, $fields);
case "photo": // Not implemented case "photo": // Not implemented
@ -705,6 +683,10 @@ class Diaspora
return self::receivePollParticipation($importer, $fields); return self::receivePollParticipation($importer, $fields);
case "profile": case "profile":
if (!$private) {
logger('Message with type ' . $type . ' is not private, quitting.');
return false;
}
return self::receiveProfile($importer, $fields); return self::receiveProfile($importer, $fields);
case "reshare": case "reshare":
@ -840,7 +822,7 @@ class Diaspora
// Only some message types have signatures. So we quit here for the other types. // Only some message types have signatures. So we quit here for the other types.
if (!in_array($type, ["comment", "like"])) { if (!in_array($type, ["comment", "like"])) {
return ["fields" => $fields, "relayed" => false]; return $fields;
} }
// No author_signature? This is a must, so we quit. // No author_signature? This is a must, so we quit.
if (!isset($author_signature)) { if (!isset($author_signature)) {
@ -849,25 +831,29 @@ class Diaspora
} }
if (isset($parent_author_signature)) { if (isset($parent_author_signature)) {
$relayed = true;
$key = self::key($msg["author"]); $key = self::key($msg["author"]);
if (empty($key)) {
logger("No key found for parent author ".$msg["author"], LOGGER_DEBUG);
return false;
}
if (!Crypto::rsaVerify($signed_data, $parent_author_signature, $key, "sha256")) { if (!Crypto::rsaVerify($signed_data, $parent_author_signature, $key, "sha256")) {
logger("No valid parent author signature for parent author ".$msg["author"]. " in type ".$type." - signed data: ".$signed_data." - Message: ".$msg["message"]." - Signature ".$parent_author_signature, LOGGER_DEBUG); logger("No valid parent author signature for parent author ".$msg["author"]. " in type ".$type." - signed data: ".$signed_data." - Message: ".$msg["message"]." - Signature ".$parent_author_signature, LOGGER_DEBUG);
return false; return false;
} }
} else {
$relayed = false;
} }
$key = self::key($fields->author); $key = self::key($fields->author);
if (empty($key)) {
logger("No key found for author ".$fields->author, LOGGER_DEBUG);
return false;
}
if (!Crypto::rsaVerify($signed_data, $author_signature, $key, "sha256")) { if (!Crypto::rsaVerify($signed_data, $author_signature, $key, "sha256")) {
logger("No valid author signature for author ".$fields->author. " in type ".$type." - signed data: ".$signed_data." - Message: ".$msg["message"]." - Signature ".$author_signature, LOGGER_DEBUG); logger("No valid author signature for author ".$fields->author. " in type ".$type." - signed data: ".$signed_data." - Message: ".$msg["message"]." - Signature ".$author_signature, LOGGER_DEBUG);
return false; return false;
} else { } else {
return ["fields" => $fields, "relayed" => $relayed]; return $fields;
} }
} }
@ -1650,25 +1636,23 @@ class Diaspora
/** /**
* @brief Processes an account deletion * @brief Processes an account deletion
* *
* @param array $importer Array of the importer user
* @param object $data The message object * @param object $data The message object
* *
* @return bool Success * @return bool Success
*/ */
private static function receiveAccountDeletion($importer, $data) private static function receiveAccountDeletion($data)
{ {
/// @todo Account deletion should remove the contact from the global contacts as well
$author = notags(unxmlify($data->author)); $author = notags(unxmlify($data->author));
$contact = self::contactByHandle($importer["uid"], $author); $contacts = dba::select('contact', ['id'], ['addr' => $author]);
if (!$contact) { while ($contact = dba::fetch($contacts)) {
logger("cannot find contact for author: ".$author); Contact::remove($contact["id"]);
return false;
} }
// We now remove the contact dba::delete('gcontact', ['addr' => $author]);
Contact::remove($contact["id"]);
logger('Removed contacts for ' . $author);
return true; return true;
} }
@ -1836,6 +1820,9 @@ class Diaspora
if ($message_id) { if ($message_id) {
logger("Stored comment ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); logger("Stored comment ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
if ($datarray['uid'] == 0) {
Item::distribute($message_id);
}
} }
// If we are the origin of the parent we store the original data and notify our followers // If we are the origin of the parent we store the original data and notify our followers
@ -2157,6 +2144,9 @@ class Diaspora
if ($message_id) { if ($message_id) {
logger("Stored like ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); logger("Stored like ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
if ($datarray['uid'] == 0) {
Item::distribute($message_id);
}
} }
// like on comments have the comment as parent. So we need to fetch the toplevel parent // like on comments have the comment as parent. So we need to fetch the toplevel parent
@ -2739,10 +2729,15 @@ class Diaspora
*/ */
public static function originalItem($guid, $orig_author) public static function originalItem($guid, $orig_author)
{ {
if (empty($guid)) {
logger('Empty guid. Quitting.');
return false;
}
// Do we already have this item? // Do we already have this item?
$fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid', $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid',
'author-name', 'author-link', 'author-avatar']; 'author-name', 'author-link', 'author-avatar'];
$condition = ['guid' => $guid, 'visible' => true, 'deleted' => false]; $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false, 'private' => false];
$item = dba::selectfirst('item', $fields, $condition); $item = dba::selectfirst('item', $fields, $condition);
if (DBM::is_result($item)) { if (DBM::is_result($item)) {
@ -2752,7 +2747,7 @@ class Diaspora
// Then refetch the content, if it is a reshare from a reshare. // Then refetch the content, if it is a reshare from a reshare.
// If it is a reshared post from another network then reformat to avoid display problems with two share elements // If it is a reshared post from another network then reformat to avoid display problems with two share elements
if (self::isReshare($item["body"], true)) { if (self::isReshare($item["body"], true)) {
$r = []; $item = [];
} elseif (self::isReshare($item["body"], false) || strstr($item["body"], "[share")) { } elseif (self::isReshare($item["body"], false) || strstr($item["body"], "[share")) {
$item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"])); $item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"]));
@ -2767,21 +2762,26 @@ class Diaspora
} }
} }
if (!DBM::is_result($r)) { if (!DBM::is_result($item)) {
$server = "https://".substr($orig_author, strpos($orig_author, "@") + 1); if (empty($orig_author)) {
logger("1st try: reshared message ".$guid." will be fetched via SSL from the server ".$server); logger('Empty author for guid ' . $guid . '. Quitting.');
$item_id = self::storeByGuid($guid, $server); return false;
if (!$item_id) {
$server = "http://".substr($orig_author, strpos($orig_author, "@") + 1);
logger("2nd try: reshared message ".$guid." will be fetched without SLL from the server ".$server);
$item_id = self::storeByGuid($guid, $server);
} }
if ($item_id) { $server = "https://".substr($orig_author, strpos($orig_author, "@") + 1);
logger("1st try: reshared message ".$guid." will be fetched via SSL from the server ".$server);
$stored = self::storeByGuid($guid, $server);
if (!$stored) {
$server = "http://".substr($orig_author, strpos($orig_author, "@") + 1);
logger("2nd try: reshared message ".$guid." will be fetched without SSL from the server ".$server);
$stored = self::storeByGuid($guid, $server);
}
if ($stored) {
$fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid', $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid',
'author-name', 'author-link', 'author-avatar']; 'author-name', 'author-link', 'author-avatar'];
$condition = ['id' => $item_id, 'visible' => true, 'deleted' => false]; $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false, 'private' => false];
$item = dba::selectfirst('item', $fields, $condition); $item = dba::selectfirst('item', $fields, $condition);
if (DBM::is_result($item)) { if (DBM::is_result($item)) {
@ -2883,6 +2883,9 @@ class Diaspora
if ($message_id) { if ($message_id) {
logger("Stored reshare ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); logger("Stored reshare ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
if ($datarray['uid'] == 0) {
Item::distribute($message_id);
}
return true; return true;
} else { } else {
return false; return false;
@ -3107,6 +3110,9 @@ class Diaspora
if ($message_id) { if ($message_id) {
logger("Stored item ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG); logger("Stored item ".$datarray["guid"]." with message id ".$message_id, LOGGER_DEBUG);
if ($datarray['uid'] == 0) {
Item::distribute($message_id);
}
return true; return true;
} else { } else {
return false; return false;

View file

@ -36,7 +36,30 @@ class Network
*/ */
public static function fetchUrl($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0) public static function fetchUrl($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0)
{ {
$ret = self::curl( $ret = self::fetchUrlFull($url, $binary, $redirects, $timeout, $accept_content, $cookiejar);
return $ret['body'];
}
/**
* @brief Curl wrapper with array of return values.
*
* Inner workings and parameters are the same as @ref fetchUrl but returns an array with
* all the information collected during the fetch.
*
* @param string $url URL to fetch
* @param boolean $binary default false
* TRUE if asked to return binary results (file download)
* @param integer $redirects The recursion counter for internal use - default 0
* @param integer $timeout Timeout in seconds, default system config value or 60 seconds
* @param string $accept_content supply Accept: header with 'accept_content' as the value
* @param string $cookiejar Path to cookie jar file
*
* @return array With all relevant information, 'body' contains the actual fetched content.
*/
public static function fetchUrlFull($url, $binary = false, &$redirects = 0, $timeout = 0, $accept_content = null, $cookiejar = 0)
{
return self::curl(
$url, $url,
$binary, $binary,
$redirects, $redirects,
@ -45,8 +68,6 @@ class Network
'cookiejar'=>$cookiejar 'cookiejar'=>$cookiejar
] ]
); );
return($ret['body']);
} }
/** /**

View file

@ -59,7 +59,7 @@ class Temporal
$o = '<select id="timezone_select" name="timezone">'; $o = '<select id="timezone_select" name="timezone">';
usort($timezone_identifiers, [self, 'timezoneCompareCallback']); usort($timezone_identifiers, [__CLASS__, 'timezoneCompareCallback']);
$continent = ''; $continent = '';
foreach ($timezone_identifiers as $value) { foreach ($timezone_identifiers as $value) {
$ex = explode("/", $value); $ex = explode("/", $value);

View file

@ -4,6 +4,7 @@
*/ */
namespace Friendica\Worker; namespace Friendica\Worker;
use Friendica\BaseObject;
use Friendica\Core\Config; use Friendica\Core\Config;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\System; use Friendica\Core\System;
@ -19,98 +20,67 @@ use dba;
require_once 'include/items.php'; require_once 'include/items.php';
/// @todo This is some ugly code that needs to be split into several methods class Delivery extends BaseObject
{
const MAIL = 'mail';
const SUGGESTION = 'suggest';
const RELOCATION = 'relocate';
const DELETION = 'drop';
const POST = 'wall-new';
const COMMENT = 'comment-new';
class Delivery { public static function execute($cmd, $item_id, $contact_id)
public static function execute($cmd, $item_id, $contact_id) { {
global $a; logger('Invoked: ' . $cmd . ': ' . $item_id . ' to ' . $contact_id, LOGGER_DEBUG);
logger('delivery: invoked: '.$cmd.': '.$item_id.' to '.$contact_id, LOGGER_DEBUG);
$mail = false;
$fsuggest = false;
$relocate = false;
$top_level = false; $top_level = false;
$recipients = [];
$followup = false; $followup = false;
$public_message = false;
$normal_mode = true; if ($cmd == self::MAIL) {
$target_item = dba::selectFirst('mail', [], ['id' => $item_id]);
$item = null; if (!DBM::is_result($message)) {
$recipients[] = $contact_id;
if ($cmd === 'mail') {
$normal_mode = false;
$mail = true;
$message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1",
intval($item_id)
);
if (!count($message)) {
return; return;
} }
$uid = $message[0]['uid']; $uid = $target_item['uid'];
$recipients[] = $message[0]['contact-id']; } elseif ($cmd == self::SUGGESTION) {
$item = $message[0]; $target_item = dba::selectFirst('fsuggest', [], ['id' => $item_id]);
} elseif ($cmd === 'suggest') { if (!DBM::is_result($message)) {
$normal_mode = false;
$fsuggest = true;
$suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1",
intval($item_id)
);
if (!count($suggest)) {
return; return;
} }
$uid = $suggest[0]['uid']; $uid = $target_item['uid'];
$recipients[] = $suggest[0]['cid']; } elseif ($cmd == self::RELOCATION) {
$item = $suggest[0];
} elseif ($cmd === 'relocate') {
$normal_mode = false;
$relocate = true;
$uid = $item_id; $uid = $item_id;
} else { } else {
// find ancestors $item = dba::selectFirst('item', ['parent'], ['id' => $item_id]);
$target_item = dba::fetch_first("SELECT `item`.*, `contact`.`uid` AS `cuid` FROM `item` if (!DBM::is_result($item) || empty($item['parent'])) {
return;
}
$parent_id = intval($item['parent']);
$itemdata = dba::p("SELECT `item`.*, `contact`.`uid` AS `cuid`,
`sign`.`signed_text`,`sign`.`signature`,`sign`.`signer`
FROM `item`
INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
WHERE `item`.`id` = ? AND `visible` AND NOT `moderated`", $item_id); LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
WHERE `item`.`id` IN (?, ?) AND `visible` AND NOT `moderated`
if (!DBM::is_result($target_item) || !intval($target_item['parent'])) { ORDER BY `item`.`id`",
return; $item_id, $parent_id);
$items = [];
while ($item = dba::fetch($itemdata)) {
if ($item['id'] == $parent_id) {
$parent = $item;
} }
if ($item['id'] == $item_id) {
$target_item = $item;
}
$items[] = $item;
}
dba::close($itemdata);
$parent_id = intval($target_item['parent']);
$uid = $target_item['cuid']; $uid = $target_item['cuid'];
$updated = $target_item['edited'];
$items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer`
FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d AND visible = 1 AND moderated = 0 ORDER BY `id` ASC",
intval($parent_id)
);
if (!count($items)) {
return;
}
$icontacts = null;
$contacts_arr = [];
foreach ($items as $item) {
if (!in_array($item['contact-id'],$contacts_arr)) {
$contacts_arr[] = intval($item['contact-id']);
}
}
if (count($contacts_arr)) {
$str_contacts = implode(',',$contacts_arr);
$icontacts = q("SELECT * FROM `contact`
WHERE `id` IN ( $str_contacts ) "
);
}
if ( !($icontacts && count($icontacts))) {
return;
}
// avoid race condition with deleting entries // avoid race condition with deleting entries
if ($items[0]['deleted']) { if ($items[0]['deleted']) {
foreach ($items as $item) { foreach ($items as $item) {
$item['deleted'] = 1; $item['deleted'] = 1;
@ -120,24 +90,10 @@ class Delivery {
// When commenting too fast after delivery, a post wasn't recognized as top level post. // When commenting too fast after delivery, a post wasn't recognized as top level post.
// The count then showed more than one entry. The additional check should help. // The count then showed more than one entry. The additional check should help.
// The check for the "count" should be superfluous, but I'm not totally sure by now, so we keep it. // The check for the "count" should be superfluous, but I'm not totally sure by now, so we keep it.
if ((($items[0]['id'] == $item_id) || (count($items) == 1)) && ($items[0]['uri'] === $items[0]['parent-uri'])) { if ((($parent['id'] == $item_id) || (count($items) == 1)) && ($parent['uri'] === $parent['parent-uri'])) {
logger('delivery: top level post'); logger('Top level post');
$top_level = true; $top_level = true;
} }
}
$owner = User::getOwnerDataById($uid);
if (!$owner) {
return;
}
// We don't treat Forum posts as "wall-to-wall" to be able to post them via Diaspora
$walltowall = $top_level && ($owner['id'] != $items[0]['contact-id']) & ($owner['account-type'] != ACCOUNT_TYPE_COMMUNITY);
$public_message = true;
if (!$mail && !$fsuggest && !$relocate) {
$parent = $items[0];
// This is IMPORTANT!!!! // This is IMPORTANT!!!!
@ -147,7 +103,7 @@ class Delivery {
// if $parent['wall'] == 1 we will already have the parent message in our array // if $parent['wall'] == 1 we will already have the parent message in our array
// and we will relay the whole lot. // and we will relay the whole lot.
$localhost = $a->get_hostname(); $localhost = self::getApp()->get_hostname();
if (strpos($localhost, ':')) { if (strpos($localhost, ':')) {
$localhost = substr($localhost, 0, strpos($localhost, ':')); $localhost = substr($localhost, 0, strpos($localhost, ':'));
} }
@ -159,26 +115,24 @@ class Delivery {
* *
*/ */
$relay_to_owner = false;
if (!$top_level && ($parent['wall'] == 0) && stristr($target_item['uri'], $localhost)) { if (!$top_level && ($parent['wall'] == 0) && stristr($target_item['uri'], $localhost)) {
$relay_to_owner = true; logger('Followup ' . $target_item["guid"], LOGGER_DEBUG);
}
if ($relay_to_owner) {
logger('followup '.$target_item["guid"], LOGGER_DEBUG);
// local followup to remote post // local followup to remote post
$followup = true; $followup = true;
} }
if (strlen($parent['allow_cid']) if (empty($parent['allow_cid'])
|| strlen($parent['allow_gid']) && empty($parent['allow_gid'])
|| strlen($parent['deny_cid']) && empty($parent['deny_cid'])
|| strlen($parent['deny_gid']) && empty($parent['deny_gid'])
|| $parent["private"]) { && !$parent["private"]) {
$public_message = false; // private recipients, not public $public_message = true;
}
} }
$owner = User::getOwnerDataById($uid);
if (!DBM::is_result($owner)) {
return;
} }
// We don't deliver our items to blocked or pending contacts, and not to ourselves either // We don't deliver our items to blocked or pending contacts, and not to ourselves either
@ -189,146 +143,22 @@ class Delivery {
return; return;
} }
$deliver_status = 0; // Transmit via Diaspora if the thread had started as Diaspora post
// This is done since the uri wouldn't match (Diaspora doesn't transmit it)
// Transmit via Diaspora if not possible via Friendica if (isset($parent) && ($parent['network'] == NETWORK_DIASPORA) && ($contact['network'] == NETWORK_DFRN)) {
if (($item['uid'] == 0) && ($contact['network'] == NETWORK_DFRN)) {
$contact['network'] = NETWORK_DIASPORA; $contact['network'] = NETWORK_DIASPORA;
} }
logger("main delivery by delivery: followup=$followup mail=$mail fsuggest=$fsuggest relocate=$relocate - network ".$contact['network']); logger("Delivering " . $cmd . " followup=$followup - via network " . $contact['network']);
switch ($contact['network']) { switch ($contact['network']) {
case NETWORK_DFRN: case NETWORK_DFRN:
logger('notifier: '.$target_item["guid"].' dfrndelivery: '.$contact['name']); self::deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
if ($mail) {
$item['body'] = Item::fixPrivatePhotos($item['body'], $owner['uid'], null, $item['contact-id']);
$atom = DFRN::mail($item, $owner);
} elseif ($fsuggest) {
$atom = DFRN::fsuggest($item, $owner);
dba::delete('fsuggest', ['id' => $item['id']]);
} elseif ($relocate) {
$atom = DFRN::relocate($owner, $uid);
} elseif ($followup) {
$msgitems = [];
foreach ($items as $item) { // there is only one item
if (!$item['parent']) {
return;
}
if ($item['id'] == $item_id) {
logger('followup: item: '. print_r($item,true), LOGGER_DATA);
$msgitems[] = $item;
}
}
$atom = DFRN::entries($msgitems,$owner);
} else {
$msgitems = [];
foreach ($items as $item) {
if (!$item['parent']) {
return;
}
// private emails may be in included in public conversations. Filter them.
if ($public_message && $item['private']) {
return;
}
$item_contact = self::getItemContact($item,$icontacts);
if (!$item_contact) {
return;
}
if ($normal_mode) {
// Only add the parent when we don't delete other items.
if ($item_id == $item['id'] || (($item['id'] == $item['parent']) && ($cmd != 'drop'))) {
$item["entry:comment-allow"] = true;
$item["entry:cid"] = (($top_level) ? $contact['id'] : 0);
$msgitems[] = $item;
}
} else {
$item["entry:comment-allow"] = true;
$msgitems[] = $item;
}
}
$atom = DFRN::entries($msgitems,$owner);
}
logger('notifier entry: '.$contact["url"].' '.$target_item["guid"].' entry: '.$atom, LOGGER_DEBUG);
logger('notifier: '.$atom, LOGGER_DATA);
$basepath = implode('/', array_slice(explode('/',$contact['url']),0,3));
// perform local delivery if we are on the same site
if (link_compare($basepath,System::baseUrl())) {
$nickname = basename($contact['url']);
if ($contact['issued-id']) {
$sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id']));
} else {
$sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id']));
}
$x = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`,
`contact`.`pubkey` AS `cpubkey`,
`contact`.`prvkey` AS `cprvkey`,
`contact`.`thumb` AS `thumb`,
`contact`.`url` as `url`,
`contact`.`name` as `senderName`,
`user`.*
FROM `contact`
INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `contact`.`blocked` = 0 AND `contact`.`pending` = 0
AND `contact`.`network` = '%s' AND `user`.`nickname` = '%s'
$sql_extra
AND `user`.`account_expired` = 0 AND `user`.`account_removed` = 0 LIMIT 1",
dbesc(NETWORK_DFRN),
dbesc($nickname)
);
if ($x && count($x)) {
$write_flag = ((($x[0]['rel']) && ($x[0]['rel'] != CONTACT_IS_SHARING)) ? true : false);
if ((($owner['page-flags'] == PAGE_COMMUNITY) || $write_flag) && !$x[0]['writable']) {
dba::update('contact', ['writable' => true], ['id' => $x[0]['id']]);
$x[0]['writable'] = 1;
}
$ssl_policy = Config::get('system','ssl_policy');
$x[0] = Contact::updateSslPolicy($x[0], $ssl_policy);
// If we are setup as a soapbox we aren't accepting top level posts from this person
if (($x[0]['page-flags'] == PAGE_SOAPBOX) && $top_level) {
break; break;
}
logger('mod-delivery: local delivery');
DFRN::import($atom, $x[0]);
break;
}
}
if (!Queue::wasDelayed($contact['id'])) {
$deliver_status = DFRN::deliver($owner, $contact, $atom);
} else {
$deliver_status = -1;
}
logger('notifier: dfrn_delivery to '.$contact["url"].' with guid '.$target_item["guid"].' returns '.$deliver_status);
if ($deliver_status < 0) {
logger('notifier: delivery failed: queuing message');
Queue::add($contact['id'], NETWORK_DFRN, $atom, false, $target_item['guid']);
}
if (($deliver_status >= 200) && ($deliver_status <= 299)) {
// We successfully delivered a message, the contact is alive
Contact::unmarkForArchival($contact);
} else {
// The message could not be delivered. We mark the contact as "dead"
Contact::markForArchival($contact);
}
case NETWORK_DIASPORA:
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
break; break;
case NETWORK_OSTATUS: case NETWORK_OSTATUS:
@ -345,156 +175,7 @@ class Delivery {
break; break;
case NETWORK_MAIL: case NETWORK_MAIL:
self::deliverMail($cmd, $contact, $owner, $target_item);
if (Config::get('system','dfrn_only')) {
break;
}
// WARNING: does not currently convert to RFC2047 header encodings, etc.
$addr = $contact['addr'];
if (!strlen($addr)) {
break;
}
if ($cmd === 'wall-new' || $cmd === 'comment-new') {
$it = null;
if ($cmd === 'wall-new') {
$it = $items[0];
} else {
$r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1",
intval($item_id)
);
if (DBM::is_result($r)) {
$it = $r[0];
}
}
if (!$it) {
break;
}
$local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
intval($uid)
);
if (!count($local_user)) {
break;
}
$reply_to = '';
$r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
intval($uid)
);
if ($r1 && $r1[0]['reply_to']) {
$reply_to = $r1[0]['reply_to'];
}
$subject = (($it['title']) ? Email::encodeHeader($it['title'],'UTF-8') : L10n::t("\x28no subject\x29")) ;
// only expose our real email address to true friends
if (($contact['rel'] == CONTACT_IS_FRIEND) && !$contact['blocked']) {
if ($reply_to) {
$headers = 'From: '.Email::encodeHeader($local_user[0]['username'],'UTF-8').' <'.$reply_to.'>'."\n";
$headers .= 'Sender: '.$local_user[0]['email']."\n";
} else {
$headers = 'From: '.Email::encodeHeader($local_user[0]['username'],'UTF-8').' <'.$local_user[0]['email'].'>'."\n";
}
} else {
$headers = 'From: '. Email::encodeHeader($local_user[0]['username'], 'UTF-8') . ' <noreply@' . $a->get_hostname() . '>' . "\n";
}
//if ($reply_to)
// $headers .= 'Reply-to: '.$reply_to . "\n";
$headers .= 'Message-Id: <'. Email::iri2msgid($it['uri']).'>'. "\n";
//logger("Mail: uri: ".$it['uri']." parent-uri ".$it['parent-uri'], LOGGER_DEBUG);
//logger("Mail: Data: ".print_r($it, true), LOGGER_DEBUG);
//logger("Mail: Data: ".print_r($it, true), LOGGER_DATA);
if ($it['uri'] !== $it['parent-uri']) {
$headers .= "References: <".Email::iri2msgid($it["parent-uri"]).">";
// If Threading is enabled, write down the correct parent
if (($it["thr-parent"] != "") && ($it["thr-parent"] != $it["parent-uri"])) {
$headers .= " <".Email::iri2msgid($it["thr-parent"]).">";
}
$headers .= "\n";
if (!$it['title']) {
$r = q("SELECT `title` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($it['parent-uri']),
intval($uid));
if (DBM::is_result($r) && ($r[0]['title'] != '')) {
$subject = $r[0]['title'];
} else {
$r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d LIMIT 1",
dbesc($it['parent-uri']),
intval($uid));
if (DBM::is_result($r) && ($r[0]['title'] != '')) {
$subject = $r[0]['title'];
}
}
}
if (strncasecmp($subject,'RE:',3)) {
$subject = 'Re: '.$subject;
}
}
Email::send($addr, $subject, $headers, $it);
}
break;
case NETWORK_DIASPORA:
if ($public_message) {
$loc = 'public batch '.$contact['batch'];
} else {
$loc = $contact['name'];
}
logger('delivery: diaspora batch deliver: '.$loc);
if (Config::get('system','dfrn_only') || !Config::get('system','diaspora_enabled')) {
break;
}
if ($mail) {
Diaspora::sendMail($item,$owner,$contact);
break;
}
if (!$normal_mode) {
break;
}
if (!$contact['pubkey'] && !$public_message) {
break;
}
if (($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
// top-level retraction
logger('diaspora retract: '.$loc);
Diaspora::sendRetraction($target_item,$owner,$contact,$public_message);
break;
} elseif ($relocate) {
Diaspora::sendAccountMigration($owner, $contact, $uid);
break;
} elseif ($followup) {
// send comments and likes to owner to relay
logger('diaspora followup: '.$loc);
Diaspora::sendFollowup($target_item,$owner,$contact,$public_message);
break;
} elseif ($target_item['uri'] !== $target_item['parent-uri']) {
// we are the relay - send comments, likes and relayable_retractions to our conversants
logger('diaspora relay: '.$loc);
Diaspora::sendRelay($target_item,$owner,$contact,$public_message);
break;
} elseif ($top_level && !$walltowall) {
// currently no workable solution for sending walltowall
logger('diaspora status: '.$loc);
Diaspora::sendStatus($target_item,$owner,$contact,$public_message);
break;
}
logger('delivery: diaspora unknown mode: '.$contact['name']);
break; break;
default: default:
@ -504,16 +185,268 @@ class Delivery {
return; return;
} }
private static function getItemContact($item, $contacts) /**
* @brief Deliver content via DFRN
*
* @param string $cmd Command
* @param array $contact Contact record of the receiver
* @param array $owner Owner record of the sender
* @param array $items Item record of the content and the parent
* @param array $target_item Item record of the content
* @param boolean $public_message Is the content public?
* @param boolean $top_level Is it a thread starter?
* @param boolean $followup Is it an answer to a remote post?
*/
private static function deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup)
{ {
if (!count($contacts) || !is_array($item)) { logger('Deliver ' . $target_item["guid"] . ' via DFRN to ' . $contact['addr']);
return false;
} if ($cmd == self::MAIL) {
foreach ($contacts as $contact) { $item = $target_item;
if ($contact['id'] == $item['contact-id']) { $item['body'] = Item::fixPrivatePhotos($item['body'], $owner['uid'], null, $item['contact-id']);
return $contact; $atom = DFRN::mail($item, $owner);
} elseif ($cmd == self::SUGGESTION) {
$item = $target_item;
$atom = DFRN::fsuggest($item, $owner);
dba::delete('fsuggest', ['id' => $item['id']]);
} elseif ($cmd == self::RELOCATION) {
$atom = DFRN::relocate($owner, $owner['uid']);
} elseif ($followup) {
$msgitems = [$target_item];
$atom = DFRN::entries($msgitems, $owner);
} else {
$msgitems = [];
foreach ($items as $item) {
// Only add the parent when we don't delete other items.
if (($target_item['id'] == $item['id']) || ($cmd != self::DELETION)) {
$item["entry:comment-allow"] = true;
$item["entry:cid"] = ($top_level ? $contact['id'] : 0);
$msgitems[] = $item;
} }
} }
return false; $atom = DFRN::entries($msgitems, $owner);
}
logger('Notifier entry: ' . $contact["url"] . ' ' . $target_item["guid"] . ' entry: ' . $atom, LOGGER_DATA);
$basepath = implode('/', array_slice(explode('/', $contact['url']), 0, 3));
// perform local delivery if we are on the same site
if (link_compare($basepath, System::baseUrl())) {
$condition = ['nurl' => normalise_link($contact['url']), 'self' => true];
$target_self = dba::selectFirst('contact', ['uid'], $condition);
if (!DBM::is_result($target_self)) {
return;
}
$target_uid = $target_self['uid'];
// Check if the user has got this contact
$cid = Contact::getIdForURL($owner['url'], $target_uid);
if (!$cid) {
// Otherwise there should be a public contact
$cid = Contact::getIdForURL($owner['url']);
if (!$cid) {
return;
}
}
// We now have some contact, so we fetch it
$target_importer = dba::fetch_first("SELECT *, `name` as `senderName`
FROM `contact`
WHERE NOT `blocked` AND `id` = ? LIMIT 1",
$cid);
// This should never fail
if (!DBM::is_result($target_importer)) {
return;
}
// Set the user id. This is important if this is a public contact
$target_importer['importer_uid'] = $target_uid;
DFRN::import($atom, $target_importer);
return;
}
// We don't have a relationship with contacts on a public post.
// Se we transmit with the new method and via Diaspora as a fallback
if ($items[0]['uid'] == 0) {
// Transmit in public if it's a relay post
$public_dfrn = ($contact['contact-type'] == ACCOUNT_TYPE_RELAY);
$deliver_status = DFRN::transmit($owner, $contact, $atom, $public_dfrn);
if (($deliver_status < 200) || ($deliver_status > 299)) {
// Transmit via Diaspora if not possible via Friendica
self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
return;
}
} else {
$deliver_status = DFRN::deliver($owner, $contact, $atom);
}
logger('Delivery to ' . $contact["url"] . ' with guid ' . $target_item["guid"] . ' returns ' . $deliver_status);
if ($deliver_status < 0) {
logger('Delivery failed: queuing message ' . $target_item["guid"] );
Queue::add($contact['id'], NETWORK_DFRN, $atom, false, $target_item['guid']);
}
if (($deliver_status >= 200) && ($deliver_status <= 299)) {
// We successfully delivered a message, the contact is alive
Contact::unmarkForArchival($contact);
} else {
// The message could not be delivered. We mark the contact as "dead"
Contact::markForArchival($contact);
}
}
/**
* @brief Deliver content via Diaspora
*
* @param string $cmd Command
* @param array $contact Contact record of the receiver
* @param array $owner Owner record of the sender
* @param array $items Item record of the content and the parent
* @param array $target_item Item record of the content
* @param boolean $public_message Is the content public?
* @param boolean $top_level Is it a thread starter?
* @param boolean $followup Is it an answer to a remote post?
*/
private static function deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup)
{
// We don't treat Forum posts as "wall-to-wall" to be able to post them via Diaspora
$walltowall = $top_level && ($owner['id'] != $items[0]['contact-id']) & ($owner['account-type'] != ACCOUNT_TYPE_COMMUNITY);
if ($public_message) {
$loc = 'public batch ' . $contact['batch'];
} else {
$loc = $contact['addr'];
}
logger('Deliver ' . $target_item["guid"] . ' via Diaspora to ' . $loc);
if (Config::get('system', 'dfrn_only') || !Config::get('system', 'diaspora_enabled')) {
return;
}
if ($cmd == self::MAIL) {
Diaspora::sendMail($target_item, $owner, $contact);
return;
}
if ($cmd == self::SUGGESTION) {
return;
}
if (!$contact['pubkey'] && !$public_message) {
return;
}
if (($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
// top-level retraction
logger('diaspora retract: ' . $loc);
Diaspora::sendRetraction($target_item, $owner, $contact, $public_message);
return;
} elseif ($cmd == self::RELOCATION) {
Diaspora::sendAccountMigration($owner, $contact, $owner['uid']);
return;
} elseif ($followup) {
// send comments and likes to owner to relay
logger('diaspora followup: ' . $loc);
Diaspora::sendFollowup($target_item, $owner, $contact, $public_message);
return;
} elseif ($target_item['uri'] !== $target_item['parent-uri']) {
// we are the relay - send comments, likes and relayable_retractions to our conversants
logger('diaspora relay: ' . $loc);
Diaspora::sendRelay($target_item, $owner, $contact, $public_message);
return;
} elseif ($top_level && !$walltowall) {
// currently no workable solution for sending walltowall
logger('diaspora status: ' . $loc);
Diaspora::sendStatus($target_item, $owner, $contact, $public_message);
return;
}
logger('Unknown mode ' . $cmd . ' for ' . $loc);
}
/**
* @brief Deliver content via mail
*
* @param string $cmd Command
* @param array $contact Contact record of the receiver
* @param array $owner Owner record of the sender
* @param array $target_item Item record of the content
*/
private static function deliverMail($cmd, $contact, $owner, $target_item)
{
if (Config::get('system','dfrn_only')) {
return;
}
// WARNING: does not currently convert to RFC2047 header encodings, etc.
$addr = $contact['addr'];
if (!strlen($addr)) {
return;
}
if (!in_array($cmd, [self::POST, self::COMMENT])) {
return;
}
$local_user = dba::selectFirst('user', [], ['uid' => $owner['uid']]);
if (!DBM::is_result($local_user)) {
return;
}
logger('Deliver ' . $target_item["guid"] . ' via mail to ' . $contact['addr']);
$reply_to = '';
$mailacct = dba::selectFirst('mailacct', ['reply_to'], ['uid' => $owner['uid']]);
if (DBM::is_result($mailacct) && !empty($mailacct['reply_to'])) {
$reply_to = $mailacct['reply_to'];
}
$subject = ($target_item['title'] ? Email::encodeHeader($target_item['title'], 'UTF-8') : L10n::t("\x28no subject\x29"));
// only expose our real email address to true friends
if (($contact['rel'] == CONTACT_IS_FRIEND) && !$contact['blocked']) {
if ($reply_to) {
$headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8') . ' <' . $reply_to.'>' . "\n";
$headers .= 'Sender: ' . $local_user['email'] . "\n";
} else {
$headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8').' <' . $local_user['email'] . '>' . "\n";
}
} else {
$headers = 'From: '. Email::encodeHeader($local_user['username'], 'UTF-8') . ' <noreply@' . self::getApp()->get_hostname() . '>' . "\n";
}
$headers .= 'Message-Id: <' . Email::iri2msgid($target_item['uri']) . '>' . "\n";
if ($target_item['uri'] !== $target_item['parent-uri']) {
$headers .= "References: <" . Email::iri2msgid($target_item["parent-uri"]) . ">";
// If Threading is enabled, write down the correct parent
if (($target_item["thr-parent"] != "") && ($target_item["thr-parent"] != $target_item["parent-uri"])) {
$headers .= " <".Email::iri2msgid($target_item["thr-parent"]).">";
}
$headers .= "\n";
if (empty($target_item['title'])) {
$condition = ['uri' => $target_item['parent-uri'], 'uid' => $owner['uid']];
$title = dba::selectFirst('item', ['title'], $condition);
if (DBM::is_result($title) && ($title['title'] != '')) {
$subject = $title['title'];
} else {
$condition = ['parent-uri' => $target_item['parent-uri'], 'uid' => $owner['uid']];
$title = dba::selectFirst('item', ['title'], $condition);
if (DBM::is_result($title) && ($title['title'] != '')) {
$subject = $title['title'];
}
}
}
if (strncasecmp($subject, 'RE:', 3)) {
$subject = 'Re: ' . $subject;
}
}
Email::send($addr, $subject, $headers, $target_item);
} }
} }

View file

@ -68,7 +68,7 @@ class PubSubPublish {
$rr['topic']), $rr['topic']),
"X-Hub-Signature: sha1=".$hmac_sig]; "X-Hub-Signature: sha1=".$hmac_sig];
logger('POST '.print_r($headers, true)."\n".$params, LOGGER_DEBUG); logger('POST '.print_r($headers, true)."\n".$params, LOGGER_DATA);
Network::post($rr['callback_url'], $params, $headers); Network::post($rr['callback_url'], $params, $headers);
$ret = $a->get_curl_code(); $ret = $a->get_curl_code();

View file

@ -63,7 +63,7 @@ class Queue
return; return;
} }
if (empty($contact['notify'])) { if (empty($contact['notify']) || $contact['archive']) {
QueueModel::removeItem($q_item['id']); QueueModel::removeItem($q_item['id']);
return; return;
} }

View file

@ -7,12 +7,14 @@ namespace Friendica\Test;
use Friendica\App; use Friendica\App;
use Friendica\BaseObject; use Friendica\BaseObject;
use PHPUnit_Framework_TestCase; // backward compatibility
if (!class_exists('\PHPUnit\Framework\TestCase')) {
class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase');
}
/** /**
* Tests for the BaseObject class. * Tests for the BaseObject class.
*/ */
class BaseObjectTest extends PHPUnit_Framework_TestCase class BaseObjectTest extends \PHPUnit\Framework\TestCase
{ {
/** /**

View file

@ -5,12 +5,15 @@
namespace Friendica\Test; namespace Friendica\Test;
use PHPUnit_Framework_TestCase; // backward compatibility
if (!class_exists('\PHPUnit\Framework\TestCase')) {
class_alias('\PHPUnit_Framework_TestCase', '\PHPUnit\Framework\TestCase');
}
/** /**
* Tests for text functions. * Tests for text functions.
*/ */
class TextTest extends PHPUnit_Framework_TestCase class TextTest extends \PHPUnit\Framework\TestCase
{ {
/** /**
@ -61,10 +64,10 @@ class TextTest extends PHPUnit_Framework_TestCase
public function testAutonameLength1() public function testAutonameLength1()
{ {
$autoname1=autoname(1); $autoname1=autoname(1);
$this->assertEquals(1, count($autoname1)); $this->assertEquals(1, strlen($autoname1));
$autoname2=autoname(1); $autoname2=autoname(1);
$this->assertEquals(1, count($autoname2)); $this->assertEquals(1, strlen($autoname2));
} }
/** /**

View file

@ -12,6 +12,7 @@ Alexandre Alapetite
AlfredSK AlfredSK
Andi Stadler Andi Stadler
Andreas H. Andreas H.
Andreas Neustifter
Andrej Stieben Andrej Stieben
André Alves André Alves
André Lohan André Lohan

View file

@ -65,7 +65,7 @@ $a->config['system']['no_regfullname'] = true;
//$a->config['system']['block_local_dir'] = false; //$a->config['system']['block_local_dir'] = false;
// Location of the global directory // Location of the global directory
$a->config['system']['directory'] = 'http://dir.friendica.social'; $a->config['system']['directory'] = 'https://dir.friendica.social';
// turn on friendica's log // turn on friendica's log
$a->config['system']['debugging'] = true; $a->config['system']['debugging'] = true;

File diff suppressed because it is too large Load diff

View file

@ -202,9 +202,17 @@ blockquote.shared_content {
} }
#profile-photo-wrapper { #profile-photo-wrapper {
clear: both;
overflow: hidden; overflow: hidden;
} }
#newmember-tips {
font-size: 1.2em;
float: right;
margin-top: -32px;
padding-right: 10px;
}
/* headers */ /* headers */
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
margin: 5px 0px 5px 0px; margin: 5px 0px 5px 0px;

View file

@ -32,6 +32,9 @@ td.help {
td.help blockquote { td.help blockquote {
margin-left: 60px; margin-left: 60px;
} }
.error_header {
margin-left: 60px;
}
input[type="submit"] { input[type="submit"] {
margin: 2em 0; margin: 2em 0;
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,8 @@
<div class="field checkbox" id="div_id_{{$field.0}}"> <div class="field checkbox" id="div_id_{{$field.0}}">
<label for="id_{{$field.0}}">{{$field.1}}</label> <label id="id_{{$field.0}}_label" for="id_{{$field.0}}">{{$field.1}}</label>
<input type="hidden" name="{{$field.0}}" value="0"> <input type="hidden" name="{{$field.0}}" value="0">
<input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" aria-describedby="{{$field.0}}_tip" value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.4}}{{$field.4}}{{/if}}> <input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" aria-describedby="{{$field.0}}_tip" value="1" {{if $field.2}}checked="checked"{{/if}} {{if $field.4}}{{$field.4}}{{/if}}>
{{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span> <span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -13,6 +13,8 @@
{{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{$val}}</option>{{/foreach}} {{foreach $field.4 as $opt=>$val}}<option value="{{$val|escape:'html'}}">{{$val}}</option>{{/foreach}}
</select> </select>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -3,5 +3,7 @@
<div class='field custom'> <div class='field custom'>
<label for='{{$field.0}}'>{{$field.1}}</label> <label for='{{$field.0}}'>{{$field.1}}</label>
{{$field.2}} {{$field.2}}
<span class='field_help'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -2,5 +2,7 @@
<div class='field input' id='wrapper_{{$field.0}}'> <div class='field input' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{else}} type="text"{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq "autofocus"}} autofocus{{elseif $field.5}} {{$field.5}}{{/if}} aria-describedby='{{$field.0}}_tip'> <input{{if $field.6 eq 'email'}} type='email'{{elseif $field.6 eq 'url'}} type='url'{{else}} type="text"{{/if}} name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq "autofocus"}} autofocus{{elseif $field.5}} {{$field.5}}{{/if}} aria-describedby='{{$field.0}}_tip'>
{{if $field.3}}
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -3,5 +3,7 @@
<div class='field checkbox'> <div class='field checkbox'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}} aria-describedby='{{$field.0}}_tip'> <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3|escape:'html'}}" {{if $field.2}}checked="true"{{/if}} aria-describedby='{{$field.0}}_tip'>
{{if $field.4}}
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.4}}</span> <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.4}}</span>
{{/if}}
</div> </div>

View file

@ -2,5 +2,7 @@
<div class='field input openid' id='wrapper_{{$field.0}}'> <div class='field input openid' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<input name='{{$field.0}}' id='id_{{$field.0}}' type="text" value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'> <input name='{{$field.0}}' id='id_{{$field.0}}' type="text" value="{{$field.2|escape:'html'}}" aria-describedby='{{$field.0}}_tip'>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -2,5 +2,7 @@
<div class='field password' id='wrapper_{{$field.0}}'> <div class='field password' id='wrapper_{{$field.0}}'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'> <input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2|escape:'html'}}"{{if $field.4 eq 'required'}} required{{/if}}{{if $field.5 eq 'autofocus'}} autofocus{{/if}} aria-describedby='{{$field.0}}_tip'>
{{if $field.3}}
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> <span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -1,5 +1,7 @@
<div class='field radio'> <div class='field radio'>
<label for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label> <label for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label>
<input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked{{/if}} aria-describedby={{$field.0}}_{{$field.2}}_tip'> <input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2|escape:'html'}}" {{if $field.4}}checked{{/if}} aria-describedby={{$field.0}}_{{$field.2}}_tip'>
{{if $field.3}}
<span class='field_help' role='tooltip' id='{{$field.0}}_{{$field.2}}_tip'>{{$field.3}}</span> <span class='field_help' role='tooltip' id='{{$field.0}}_{{$field.2}}_tip'>{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -3,5 +3,7 @@
<div class='field richtext'> <div class='field richtext'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext" aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea> <textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext" aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -5,5 +5,7 @@
<select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'> <select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
{{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} {{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
</select> </select>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -5,5 +5,7 @@
<select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'> <select name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>
{{$field.4}} {{$field.4}}
</select> </select>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -3,5 +3,7 @@
<div class='field textarea'> <div class='field textarea'>
<label for='id_{{$field.0}}'>{{$field.1}}</label> <label for='id_{{$field.0}}'>{{$field.1}}</label>
<textarea name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea> <textarea name='{{$field.0}}' id='id_{{$field.0}}' aria-describedby='{{$field.0}}_tip'>{{$field.2}}</textarea>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -5,6 +5,8 @@
<select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} aria-describedby='{{$field.0}}_tip'> <select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5}}onchange="previewTheme(this);"{{/if}} aria-describedby='{{$field.0}}_tip'>
{{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} {{foreach $field.4 as $opt=>$val}}<option value="{{$opt|escape:'html'}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}}
</select> </select>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
{{if $field.5}}<div id="theme-preview"></div>{{/if}} {{if $field.5}}<div id="theme-preview"></div>{{/if}}
</div> </div>

View file

@ -10,5 +10,7 @@
{{if $field.4}}{{$field.4.1}}{{else}}ON{{/if}} {{if $field.4}}{{$field.4.1}}{{else}}ON{{/if}}
</a> </a>
</div> </div>
<span class='field_help' role='tooltip' id='{{$field.0}}_tip'>{{$field.3}}</span> {{if $field.3}}
<span class="field_help" role="tooltip" id="{{$field.0}}_tip">{{$field.3}}</span>
{{/if}}
</div> </div>

View file

@ -107,7 +107,7 @@ $a->config['system']['no_regfullname'] = true;
//$a->config['system']['block_local_dir'] = false; //$a->config['system']['block_local_dir'] = false;
// Location of the global directory // Location of the global directory
$a->config['system']['directory'] = 'http://dir.friendica.social'; $a->config['system']['directory'] = 'https://dir.friendica.social';
// Authentication cookie lifetime, in days // Authentication cookie lifetime, in days
$a->config['system']['auth_cookie_lifetime'] = 7; $a->config['system']['auth_cookie_lifetime'] = 7;

View file

@ -16,7 +16,13 @@
{{/if}} {{/if}}
</td><td>{{if $check.required}}(required){{/if}}</td></tr> </td><td>{{if $check.required}}(required){{/if}}</td></tr>
{{if $check.help}} {{if $check.help}}
<tr><td class="help" colspan="3"><blockquote>{{$check.help}}</blockquote></td></tr> <tr><td class="help" colspan="3">
<blockquote>{{$check.help}}</blockquote>
{{if $check.error_msg}}
<div class="error_header"><b>{{$check.error_msg.head}}</br><a href="{{$check.error_msg.url}}">{{$check.error_msg.url}}</a></b></div>
<blockquote>{{$check.error_msg.msg}}</blockquote>
{{/if}}
</td></tr>
{{/if}} {{/if}}
{{/foreach}} {{/foreach}}
</table> </table>

View file

@ -5,23 +5,10 @@
<div id="invite-wrapper"> <div id="invite-wrapper">
<h3>{{$invite}}</h3> <h3>{{$title}}</h3>
<div id="invite-recipient-text"> {{include file="field_textarea.tpl" field=$recipients}}
{{$addr_text}} {{include file="field_textarea.tpl" field=$message}}
</div>
<div id="invite-recipient-textarea">
<textarea id="invite-recipients" name="recipients" rows="8" cols="32" ></textarea>
</div>
<div id="invite-message-text">
{{$msg_text}}
</div>
<div id="invite-message-textarea">
<textarea id="invite-message" name="message" rows="10" cols="72" >{{$default_message}}</textarea>
</div>
<div id="invite-submit-wrapper"> <div id="invite-submit-wrapper">
<input type="submit" name="submit" value="{{$submit|escape:'html'}}" /> <input type="submit" name="submit" value="{{$submit|escape:'html'}}" />

View file

@ -4,11 +4,14 @@
<div id="login-group" role="group" aria-labelledby="login-head"> <div id="login-group" role="group" aria-labelledby="login-head">
<input type="hidden" name="auth-params" value="login" /> <input type="hidden" name="auth-params" value="login" />
<div id="login-head" class="sr-only">{{$login}}</div> <h3 id="login-head" class="sr-only">{{$login}}</h3>
<div id="login_standard"> <div id="login_standard">
{{include file="field_input.tpl" field=$lname}} {{include file="field_input.tpl" field=$lname}}
{{include file="field_password.tpl" field=$lpassword}} {{include file="field_password.tpl" field=$lpassword}}
<div id="login-lost-password-link">
<a href="lostpass" title="{{$lostpass|escape:'html'}}" id="lost-password-link" >{{$lostlink}}</a>
</div>
</div> </div>
{{if $openid}} {{if $openid}}
@ -17,17 +20,12 @@
</div> </div>
{{/if}} {{/if}}
{{include file="field_checkbox.tpl" field=$lremember}}
<div id="login-extra-links">
{{if $register}}<a href="register" title="{{$register.title|escape:'html'}}" id="register-link">{{$register.desc}}</a>{{/if}}
<a href="lostpass" title="{{$lostpass|escape:'html'}}" id="lost-password-link" >{{$lostlink}}</a>
</div>
<div id="login-submit-wrapper" > <div id="login-submit-wrapper" >
<input type="submit" name="submit" id="login-submit-button" value="{{$login|escape:'html'}}" /> <input type="submit" name="submit" id="login-submit-button" value="{{$login|escape:'html'}}" />
</div> </div>
{{include file="field_checkbox.tpl" field=$lremember}}
{{foreach $hiddens as $k=>$v}} {{foreach $hiddens as $k=>$v}}
<input type="hidden" name="{{$k}}" value="{{$v|escape:'html'}}" /> <input type="hidden" name="{{$k}}" value="{{$v|escape:'html'}}" />
{{/foreach}} {{/foreach}}
@ -35,5 +33,11 @@
</div> </div>
</form> </form>
{{if $register}}
<div id="login-extra-links">
<h3 id="login-head" class="sr-only">{{$register.title|escape:'html'}}</h3>
<a href="register" title="{{$register.title|escape:'html'}}" id="register-link">{{$register.desc}}</a>
</div>
{{/if}}
<script type="text/javascript"> $(document).ready(function() { $("#id_{{$lname.0}}").focus();} );</script> <script type="text/javascript"> $(document).ready(function() { $("#id_{{$lname.0}}").focus();} );</script>

View file

@ -1,5 +1,5 @@
{{if $pager && ($pager.prev || $pager.next)}}
<div class="pager"> <div class="pager">
{{if $pager}}
{{if $pager.prev}}<span class="pager_prev {{$pager.prev.class}}"><a href="{{$pager.prev.url}}">{{$pager.prev.text}}</a></span>{{/if}} {{if $pager.prev}}<span class="pager_prev {{$pager.prev.class}}"><a href="{{$pager.prev.url}}">{{$pager.prev.text}}</a></span>{{/if}}
{{if $pager.first}}<span class="pager_first {{$pager.first.class}}"><a href="{{$pager.first.url}}">{{$pager.first.text}}</a></span>{{/if}} {{if $pager.first}}<span class="pager_first {{$pager.first.class}}"><a href="{{$pager.first.url}}">{{$pager.first.text}}</a></span>{{/if}}
@ -9,5 +9,5 @@
{{if $pager.last}}&nbsp;<span class="pager_last {{$pager.last.class}}"><a href="{{$pager.last.url}}">{{$pager.last.text}}</a></span>{{/if}} {{if $pager.last}}&nbsp;<span class="pager_last {{$pager.last.class}}"><a href="{{$pager.last.url}}">{{$pager.last.text}}</a></span>{{/if}}
{{if $pager.next}}<span class="pager_next {{$pager.next.class}}"><a href="{{$pager.next.url}}">{{$pager.next.text}}</a></span>{{/if}} {{if $pager.next}}<span class="pager_next {{$pager.next.class}}"><a href="{{$pager.next.url}}">{{$pager.next.text}}</a></span>{{/if}}
{{/if}}
</div> </div>
{{/if}}

View file

@ -1,16 +1,17 @@
<div id="peoplefind-sidebar" class="widget"> <div id="peoplefind-sidebar" class="widget">
<h3>{{$findpeople}}</h3> <h3>{{$nv.findpeople}}</h3>
<div id="peoplefind-desc">{{$desc}}</div> <div id="peoplefind-desc">{{$nv.desc}}</div>
<form action="dirfind" method="get" /> <form action="dirfind" method="get" />
<input id="side-peoplefind-url" type="text" name="search" size="24" title="{{$hint|escape:'html'}}" /><input id="side-peoplefind-submit" type="submit" name="submit" value="{{$findthem|escape:'html'}}" /> <input id="side-peoplefind-url" type="text" name="search" size="24" title="{{$nv.hint|escape:'html'}}" /><input id="side-peoplefind-submit" type="submit" name="submit" value="{{$nv.findthem|escape:'html'}}" />
</form> </form>
<div class="side-link" id="side-match-link"><a href="match" >{{$similar}}</a></div> <div class="side-link" id="side-match-link"><a href="match" >{{$nv.similar}}</a></div>
<div class="side-link" id="side-suggest-link"><a href="suggest" >{{$suggest}}</a></div> <div class="side-link" id="side-suggest-link"><a href="suggest" >{{$nv.suggest}}</a></div>
<div class="side-link" id="side-directory-link"><a href="{{$global_dir}}" target="extlink" >{{$directory}}</a></div> <div class="side-link" id="side-directory-link"><a href="directory" >{{$nv.local_directory}}</a></div>
<div class="side-link" id="side-random-profile-link" ><a href="randprof" target="extlink" >{{$random}}</a></div> <div class="side-link" id="side-directory-link"><a href="{{$nv.global_dir}}" target="extlink" >{{$nv.directory}}</a></div>
{{if $inv}} <div class="side-link" id="side-random-profile-link" ><a href="randprof" target="extlink" >{{$nv.random}}</a></div>
<div class="side-link" id="side-invite-link" ><a href="invite" >{{$inv}}</a></div> {{if $nv.inv}}
<div class="side-link" id="side-invite-link" ><a href="invite" >{{$nv.inv}}</a></div>
{{/if}} {{/if}}
</div> </div>

View file

@ -0,0 +1,22 @@
{{if count($reasons) > 1}}
<ul class="content-filter-reasons">
{{foreach $reasons as $reason}}
<li>{{$reason|escape:html}}</li>
{{/foreach}}
</ul>
<p>
<button type="button" id="content-filter-wrap-{{$rnd}}" class="btn btn-default btn-small content-filter-button" onclick="openClose('content-filter-{{$rnd}}');">
<i class="glyphicon glyphicon-eye-open"></i> {{$openclose}}
</button>
</p>
{{else}}
<p>
{{$reasons.0|escape:html}}
<button type="button" id="content-filter-wrap-{{$rnd}}" class="btn btn-default btn-xs content-filter-button" onclick="openClose('content-filter-{{$rnd}}');">
<i class="glyphicon glyphicon-eye-open"></i> {{$openclose}}
</button>
</p>
{{/if}}
<div id="content-filter-{{$rnd}}" class="content-filter-content" style="display: none;">
{{$html}}
</div>

View file

@ -37,11 +37,11 @@ Don't blame me too much for ugly code and hacks. Fix it ;-)
**Theme - Settings** **Theme - Settings**
![Theme - Settings](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-settings.png) ![Theme - Settings](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-settings.png)
**Red schema** **Red scheme**
![Red schema](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-schema-red.png) ![Red scheme](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-scheme-red.png)
**Love Music schema** **Love Music scheme**
![Love Music schema](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-schema-love-music.png) ![Love Music scheme](https://github.com/rabuzarus/frio/blob/master/img/screenshots/screenshot-scheme-love-music.png)
**frio on mobile** **frio on mobile**

View file

@ -8,129 +8,135 @@ use Friendica\Core\System;
require_once 'view/theme/frio/php/Image.php'; require_once 'view/theme/frio/php/Image.php';
function theme_post(App $a) { function theme_post(App $a)
{
if (!local_user()) { if (!local_user()) {
return; return;
} }
if (isset($_POST['frio-settings-submit'])) { if (isset($_POST['frio-settings-submit'])) {
PConfig::set(local_user(), 'frio', 'schema', $_POST["frio_schema"]); PConfig::set(local_user(), 'frio', 'scheme', $_POST['frio_scheme']);
PConfig::set(local_user(), 'frio', 'nav_bg', $_POST["frio_nav_bg"]); PConfig::set(local_user(), 'frio', 'nav_bg', $_POST['frio_nav_bg']);
PConfig::set(local_user(), 'frio', 'nav_icon_color', $_POST["frio_nav_icon_color"]); PConfig::set(local_user(), 'frio', 'nav_icon_color', $_POST['frio_nav_icon_color']);
PConfig::set(local_user(), 'frio', 'link_color', $_POST["frio_link_color"]); PConfig::set(local_user(), 'frio', 'link_color', $_POST['frio_link_color']);
PConfig::set(local_user(), 'frio', 'background_color', $_POST["frio_background_color"]); PConfig::set(local_user(), 'frio', 'background_color', $_POST['frio_background_color']);
PConfig::set(local_user(), 'frio', 'contentbg_transp', $_POST["frio_contentbg_transp"]); PConfig::set(local_user(), 'frio', 'contentbg_transp', $_POST['frio_contentbg_transp']);
PConfig::set(local_user(), 'frio', 'background_image', $_POST["frio_background_image"]); PConfig::set(local_user(), 'frio', 'background_image', $_POST['frio_background_image']);
PConfig::set(local_user(), 'frio', 'bg_image_option', $_POST["frio_bg_image_option"]); PConfig::set(local_user(), 'frio', 'bg_image_option', $_POST['frio_bg_image_option']);
PConfig::set(local_user(), 'frio', 'css_modified', time()); PConfig::set(local_user(), 'frio', 'css_modified', time());
} }
} }
function theme_admin_post(App $a) { function theme_admin_post(App $a)
{
if (!local_user()) { if (!local_user()) {
return; return;
} }
if (isset($_POST['frio-settings-submit'])) { if (isset($_POST['frio-settings-submit'])) {
Config::set('frio', 'schema', $_POST["frio_schema"]); Config::set('frio', 'scheme', $_POST['frio_scheme']);
Config::set('frio', 'nav_bg', $_POST["frio_nav_bg"]); Config::set('frio', 'nav_bg', $_POST['frio_nav_bg']);
Config::set('frio', 'nav_icon_color', $_POST["frio_nav_icon_color"]); Config::set('frio', 'nav_icon_color', $_POST['frio_nav_icon_color']);
Config::set('frio', 'link_color', $_POST["frio_link_color"]); Config::set('frio', 'link_color', $_POST['frio_link_color']);
Config::set('frio', 'background_color', $_POST["frio_background_color"]); Config::set('frio', 'background_color', $_POST['frio_background_color']);
Config::set('frio', 'contentbg_transp', $_POST["frio_contentbg_transp"]); Config::set('frio', 'contentbg_transp', $_POST['frio_contentbg_transp']);
Config::set('frio', 'background_image', $_POST["frio_background_image"]); Config::set('frio', 'background_image', $_POST['frio_background_image']);
Config::set('frio', 'bg_image_option', $_POST["frio_bg_image_option"]); Config::set('frio', 'bg_image_option', $_POST['frio_bg_image_option']);
Config::set('frio', 'login_bg_image', $_POST["frio_login_bg_image"]); Config::set('frio', 'login_bg_image', $_POST['frio_login_bg_image']);
Config::set('frio', 'login_bg_color', $_POST["frio_login_bg_color"]); Config::set('frio', 'login_bg_color', $_POST['frio_login_bg_color']);
Config::set('frio', 'css_modified', time()); Config::set('frio', 'css_modified', time());
} }
} }
function theme_content(App $a) { function theme_content(App $a)
{
if (!local_user()) { if (!local_user()) {
return; return;
} }
$arr = []; $arr = [];
$arr["schema"] = PConfig::get(local_user(), 'frio', 'schema'); $arr['scheme'] = PConfig::get(local_user(), 'frio', 'scheme', PConfig::get(local_user(), 'frio', 'schema'));
$arr["nav_bg"] = PConfig::get(local_user(), 'frio', 'nav_bg'); $arr['nav_bg'] = PConfig::get(local_user(), 'frio', 'nav_bg');
$arr["nav_icon_color"] = PConfig::get(local_user(), 'frio', 'nav_icon_color'); $arr['nav_icon_color'] = PConfig::get(local_user(), 'frio', 'nav_icon_color');
$arr["link_color"] = PConfig::get(local_user(), 'frio', 'link_color'); $arr['link_color'] = PConfig::get(local_user(), 'frio', 'link_color');
$arr["bgcolor"] = PConfig::get(local_user(), 'frio', 'background_color'); $arr['background_color'] = PConfig::get(local_user(), 'frio', 'background_color');
$arr["contentbg_transp"] = PConfig::get(local_user(), 'frio', 'contentbg_transp'); $arr['contentbg_transp'] = PConfig::get(local_user(), 'frio', 'contentbg_transp');
$arr["background_image"] = PConfig::get(local_user(), 'frio', 'background_image'); $arr['background_image'] = PConfig::get(local_user(), 'frio', 'background_image');
$arr["bg_image_option"] = PConfig::get(local_user(), 'frio', 'bg_image_option'); $arr['bg_image_option'] = PConfig::get(local_user(), 'frio', 'bg_image_option');
return frio_form($arr); return frio_form($arr);
} }
function theme_admin(App $a) { function theme_admin(App $a)
{
if (!local_user()) { if (!local_user()) {
return; return;
} }
$arr = []; $arr = [];
$arr["schema"] = Config::get('frio', 'schema'); $arr['scheme'] = Config::get('frio', 'scheme', Config::get('frio', 'scheme'));
$arr["nav_bg"] = Config::get('frio', 'nav_bg'); $arr['nav_bg'] = Config::get('frio', 'nav_bg');
$arr["nav_icon_color"] = Config::get('frio', 'nav_icon_color'); $arr['nav_icon_color'] = Config::get('frio', 'nav_icon_color');
$arr["link_color"] = Config::get('frio', 'link_color'); $arr['link_color'] = Config::get('frio', 'link_color');
$arr["bgcolor"] = Config::get('frio', 'background_color'); $arr['background_color'] = Config::get('frio', 'background_color');
$arr["contentbg_transp"] = Config::get('frio', 'contentbg_transp'); $arr['contentbg_transp'] = Config::get('frio', 'contentbg_transp');
$arr["background_image"] = Config::get('frio', 'background_image'); $arr['background_image'] = Config::get('frio', 'background_image');
$arr["bg_image_option"] = Config::get('frio', 'bg_image_option'); $arr['bg_image_option'] = Config::get('frio', 'bg_image_option');
$arr["login_bg_image"] = Config::get('frio', 'login_bg_image'); $arr['login_bg_image'] = Config::get('frio', 'login_bg_image');
$arr["login_bg_color"] = Config::get('frio', 'login_bg_color'); $arr['login_bg_color'] = Config::get('frio', 'login_bg_color');
return frio_form($arr); return frio_form($arr);
} }
function frio_form($arr) { function frio_form($arr)
require_once("view/theme/frio/php/schema.php"); {
require_once 'view/theme/frio/php/scheme.php';
$scheme_info = get_schema_info($arr["schema"]); $scheme_info = get_scheme_info($arr['scheme']);
$disable = $scheme_info["overwrites"]; $disable = $scheme_info['overwrites'];
if (!is_array($disable)) { if (!is_array($disable)) {
$disable = []; $disable = [];
} }
$scheme_choices = []; $scheme_choices = [];
$scheme_choices["---"] = L10n::t("Default"); $scheme_choices['---'] = L10n::t('Custom');
$files = glob('view/theme/frio/schema/*.php'); $files = glob('view/theme/frio/scheme/*.php');
if ($files) { if ($files) {
foreach ($files as $file) { foreach ($files as $file) {
$f = basename($file, ".php"); $f = basename($file, '.php');
if ($f != 'default') { if ($f != 'default') {
$scheme_name = $f; $scheme_name = ucfirst($f);
$scheme_choices[$f] = $scheme_name; $scheme_choices[$f] = $scheme_name;
} }
} }
} }
$background_image_help = "<strong>" . L10n::t("Note"). ": </strong>".L10n::t("Check image permissions if all users are allowed to visit the image"); $background_image_help = '<strong>' . L10n::t('Note') . ': </strong>' . L10n::t('Check image permissions if all users are allowed to see the image');
$t = get_markup_template('theme_settings.tpl'); $t = get_markup_template('theme_settings.tpl');
$ctx = [ $ctx = [
'$submit' => L10n::t('Submit'), '$submit' => L10n::t('Submit'),
'$baseurl' => System::baseUrl(), '$baseurl' => System::baseUrl(),
'$title' => L10n::t("Theme settings"), '$title' => L10n::t('Theme settings'),
'$schema' => ['frio_schema', L10n::t("Select scheme"), $arr["schema"], '', $scheme_choices], '$scheme' => ['frio_scheme', L10n::t('Select color scheme'), $arr['scheme'], '', $scheme_choices],
'$nav_bg' => array_key_exists("nav_bg", $disable) ? "" : ['frio_nav_bg', L10n::t('Navigation bar background color'), $arr['nav_bg'], '', false], '$nav_bg' => array_key_exists('nav_bg', $disable) ? '' : ['frio_nav_bg', L10n::t('Navigation bar background color'), $arr['nav_bg'], '', false],
'$nav_icon_color' => array_key_exists("nav_icon_color", $disable) ? "" : ['frio_nav_icon_color', L10n::t('Navigation bar icon color '), $arr['nav_icon_color'], '', false], '$nav_icon_color' => array_key_exists('nav_icon_color', $disable) ? '' : ['frio_nav_icon_color', L10n::t('Navigation bar icon color '), $arr['nav_icon_color'], '', false],
'$link_color' => array_key_exists("link_color", $disable) ? "" : ['frio_link_color', L10n::t('Link color'), $arr['link_color'], '', false], '$link_color' => array_key_exists('link_color', $disable) ? '' : ['frio_link_color', L10n::t('Link color'), $arr['link_color'], '', false],
'$bgcolor' => array_key_exists("bgcolor", $disable) ? "" : ['frio_background_color', L10n::t('Set the background color'), $arr['bgcolor'], '', false], '$background_color' => array_key_exists('background_color', $disable) ? '' : ['frio_background_color', L10n::t('Set the background color'), $arr['background_color'], '', false],
'$contentbg_transp' => array_key_exists("contentbg_transp", $disable) ? "" : ['frio_contentbg_transp', L10n::t("Content background opacity"), ((isset($arr["contentbg_transp"]) && $arr["contentbg_transp"] != "") ? $arr["contentbg_transp"] : 100), ''], '$contentbg_transp' => array_key_exists('contentbg_transp', $disable) ? '' : ['frio_contentbg_transp', L10n::t('Content background opacity'), defaults($arr, 'contentbg_transp', 100), ''],
'$background_image' => array_key_exists("background_image", $disable) ? "" : ['frio_background_image', L10n::t('Set the background image'), $arr['background_image'], $background_image_help, false], '$background_image' => array_key_exists('background_image', $disable) ? '' : ['frio_background_image', L10n::t('Set the background image'), $arr['background_image'], $background_image_help, false],
'$bg_image_options_title' => L10n::t('Background image style'),
'$bg_image_options' => Image::get_options($arr), '$bg_image_options' => Image::get_options($arr),
]; ];
if (array_key_exists("login_bg_image", $arr) && !array_key_exists("login_bg_image", $disable)) { if (array_key_exists('login_bg_image', $arr) && !array_key_exists('login_bg_image', $disable)) {
$ctx['$login_bg_image'] = ['frio_login_bg_image', L10n::t('Login page background image'), $arr['login_bg_image'], $background_image_help, false]; $ctx['$login_bg_image'] = ['frio_login_bg_image', L10n::t('Login page background image'), $arr['login_bg_image'], $background_image_help, false];
} }
if (array_key_exists("login_bg_color", $arr) && !array_key_exists("login_bg_color", $disable)) {
if (array_key_exists('login_bg_color', $arr) && !array_key_exists('login_bg_color', $disable)) {
$ctx['$login_bg_color'] = ['frio_login_bg_color', L10n::t('Login page background color'), $arr['login_bg_color'], L10n::t('Leave background image and color empty for theme defaults'), false]; $ctx['$login_bg_color'] = ['frio_login_bg_color', L10n::t('Login page background color'), $arr['login_bg_color'], L10n::t('Leave background image and color empty for theme defaults'), false];
} }
$o = replace_macros($t, $ctx); $o = replace_macros($t, $ctx);
return $o; return $o;

View file

@ -1,17 +1,20 @@
#admin-users.adminpage { padding-left:0; padding-right: 0;}
#admin-users.adminpage > h1 { padding: 0 15px; } #admin-users.adminpage > h1 { padding: 0 15px; }
#admin-users.adminpage .panel-collapse { margin-left: -15px; margin-right: -15px; }
#admin-users td { word-break: break-all; }
#admin-users #users th:first-of-type { width: 1em; } #admin-users #users th:first-of-type { width: 1em; }
#admin-users #users th:nth-of-type(2) { width: 40px; } #admin-users #users th:nth-of-type(2) { width: 40px; }
#admin-users #users th:last-of-type { width: 1em; } #admin-users #users th:last-of-type { width: 1em; }
#admin-users .admin-settings-footer-elements { padding-left: 8px; padding-right: 8px; }
#admin-users #deleted th:first-of-type { width: 40px; } #admin-users #deleted th:first-of-type { width: 40px; }
#admin-users #users img.avatar-nano, #deleted img.avatar-nano { height: 24px; width: 24px; } #admin-users #users img.avatar-nano, #deleted img.avatar-nano { height: 24px; width: 24px; }
.opened .caret { transform: rotate(180deg); } .opened .caret { transform: rotate(180deg); }
tr.details td, tr.details td,
tr.details th tr.details th {
{ border-top: 0!important; } border-top: 0!important;
}
.adminpage td > .checkbox { margin: 0; }
.adminpage td { word-break: break-all; }

View file

@ -24,9 +24,10 @@ and open the template in the editor.
body { body {
padding-top: 110px; padding-top: 110px;
background-color: $bgcolor; background-color: $background_color;
background-image: url("$background_image"); background-image: url("$background_image");
background-size: $background_size_img; background-size: $background_size_img;
background-repeat: $background_repeat;
background-attachment: fixed; background-attachment: fixed;
color: #777; color: #777;
/*color: #555;*/ /*color: #555;*/
@ -2115,9 +2116,10 @@ ul.dropdown-menu li:hover {
.allfriends-content-wrapper, .match-content-wrapper, .dirfind-content-wrapper, .allfriends-content-wrapper, .match-content-wrapper, .dirfind-content-wrapper,
.directory-content-wrapper, .manage-content-wrapper, .notes-content-wrapper, .directory-content-wrapper, .manage-content-wrapper, .notes-content-wrapper,
.message-content-wrapper, .apps-content-wrapper, .photos-content-wrapper, .message-content-wrapper, .apps-content-wrapper, .photos-content-wrapper,
#adminpage, .viewcontacts-content-wrapper, .dfrn_request-content-wrapper, #adminpage, .delegate-content-wrapper, .uexport-content-wrapper,
.viewcontacts-content-wrapper, .dfrn_request-content-wrapper,
.friendica-content-wrapper, .credits-content-wrapper, .nogroup-content-wrapper, .friendica-content-wrapper, .credits-content-wrapper, .nogroup-content-wrapper,
.profperm-content-wrapper { .profperm-content-wrapper, .invite-content-wrapper {
min-height: calc(100vh - 150px); min-height: calc(100vh - 150px);
padding: 15px; padding: 15px;
padding-bottom: 20px; padding-bottom: 20px;
@ -2409,10 +2411,13 @@ ul li:hover .contact-wrapper .contact-action-link:hover {
height: 48px; height: 48px;
width: 48px; width: 48px;
} }
#prvmail-end { #prvmail-end {
clear:both; clear:both;
} }
#modal #prvmail-text-edit-bb .bb-img {
display: none;
}
/* photos */ /* photos */
.photo-album-actions { .photo-album-actions {
margin-bottom: 10px; margin-bottom: 10px;
@ -2650,7 +2655,8 @@ ul li:hover .contact-wrapper .contact-action-link:hover {
margin-left: -15px; margin-left: -15px;
margin-right: -15px; margin-right: -15px;
} }
.panel-group-settings > .panel { .panel-group-settings > .panel,
.panel-group-settings > form > .panel {
padding-left: 15px; padding-left: 15px;
padding-right: 15px; padding-right: 15px;
} }
@ -2935,6 +2941,22 @@ section.help-content-wrapper li {
#adminpage .plugin .desc { #adminpage .plugin .desc {
padding-left: 10px; padding-left: 10px;
} }
.adminpage .admin-settings-action-link,
.adminpage .admin-settings-action-link:hover {
color: #555;
}
.adminpage .admin-settings-action-link:hover {
opacity: 1;
}
.adminpage .admin-settings-action-link {
opacity: 0.8;
}
#admin-users tr.blocked {
background-color: #f8efc0;
}
.adminpage .table-hover > tbody > tr:hover + tr.details {
background-color: #f5f5f5;
}
/* Register Page*/ /* Register Page*/
#register-openid-wrapper, #register-name-wrapper, #register-invite-wrapper, #profile-publish-wrapper { #register-openid-wrapper, #register-name-wrapper, #register-invite-wrapper, #profile-publish-wrapper {
@ -3115,12 +3137,30 @@ section .profile-match-wrapper {
* Login page * Login page
*/ */
#login-submit-wrapper { #login-submit-wrapper {
display: flex; float: right;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
} }
#lost-password-link { flex-grow: 2; } #lost-password-link { flex-grow: 2; }
#login-lost-password-link {
margin-bottom: 10px;
float: right;
}
#div_id_remember {
float: left;
}
#id_password_wrapper {
margin-bottom: unset;
}
#login_openid {
clear: both;
}
#register-link {
color: white;
background: #8ad0a1;
width: 100%;
}
#login-end {
clear: both;
}
.mod-home.is-not-singleuser, .mod-home.is-not-singleuser,
.mod-login { .mod-login {
@ -3145,12 +3185,15 @@ section .profile-match-wrapper {
margin-top: 2.5%; margin-top: 2.5%;
} }
.mod-home.is-not-singleuser .login-form > #login-extra-links {
margin-top: 4em;
}
.mod-home.is-not-singleuser .login-form > #login-form label, .mod-home.is-not-singleuser .login-form > #login-form label,
.mod-login #content #login-form label { .mod-login #content #login-form label {
color: #eee; color: #eee;
} }
.mod-home.is-not-singleuser .login-panel-content, .mod-home.is-not-singleuser .login-panel-content,
.mod-login .login-panel-content { .mod-login .login-panel-content {
background-color: rgba(255,255,255,.85); background-color: rgba(255,255,255,.85);
@ -3164,11 +3207,15 @@ section .profile-match-wrapper {
} }
.mod-home.is-not-singleuser .login-form > #login-form, .mod-home.is-not-singleuser .login-form > #login-form,
.mod-home.is-not-singleuser .login-form > #login-extra-links,
.mod-login #content #login-form { .mod-login #content #login-form {
background-color: #fff; background-color: #fff;
padding: 1em; padding: 1em;
position: relative; position: relative;
margin-top: 4em; }
.mod-home.is-not-singleuser .login-form > #login-extra-links {
margin-top: unset;
background-color: white;
} }
.mod-home.is-not-singleuser .login-form > #login-form label, .mod-home.is-not-singleuser .login-form > #login-form label,
@ -3176,7 +3223,7 @@ section .profile-match-wrapper {
color: #444; color: #444;
} }
.mod-home.is-not-singleuser .login-form > #login-form::before, .mod-home.is-not-singleuser .login-form::before,
.mod-login #content #login-form::before { .mod-login #content #login-form::before {
display: block; display: block;
position: absolute; position: absolute;
@ -3189,7 +3236,7 @@ section .profile-match-wrapper {
z-index: -1; z-index: -1;
} }
.mod-home.is-not-singleuser .login-form > #login-form::after, .mod-home.is-not-singleuser .login-form::after,
.mod-login #content #login-form::after { .mod-login #content #login-form::after {
display: block; display: block;
position: absolute; position: absolute;

View file

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 239 KiB

View file

Before

Width:  |  Height:  |  Size: 750 KiB

After

Width:  |  Height:  |  Size: 750 KiB

View file

@ -231,7 +231,6 @@ var FileBrowser = {
$(".fbrowser .fbswitcher [data-mode=" + FileBrowser.type + "]").addClass("active"); $(".fbrowser .fbswitcher [data-mode=" + FileBrowser.type + "]").addClass("active");
// We need to add the AjaxUpload to the button // We need to add the AjaxUpload to the button
FileBrowser.uploadButtons(); FileBrowser.uploadButtons();
}, },
// Load new content (e.g. change photo album) // Load new content (e.g. change photo album)

View file

@ -9,7 +9,9 @@ $(function() {
selectnone($(this).data('selectNone')); selectnone($(this).data('selectNone'));
}); });
$('body').on('change', 'input[type=checkbox].select', function() { // Toggle checkbox status to all or none for all checkboxes of a specific
// css class.
$('body').on('change', 'input[type=checkbox].selecttoggle', function() {
$this = $(this); $this = $(this);
if ($this.prop('checked')) { if ($this.prop('checked')) {
selectall($this.data('selectClass')); selectall($this.data('selectClass'));
@ -20,6 +22,26 @@ $(function() {
} }
}); });
// Use AJAX calls to reorder the table (so we don't need to reload the page).
$('body').on('click', '.table-order', function(e) {
e.preventDefault();
// Get the parent table element.
var table = $(this).parents('table');
var orderUrl = this.getAttribute("data-order-url");
table.fadeTo("fast", 0.33);
$("body").css("cursor", "wait");
$.get(orderUrl, function(data) {
// Find the table element in the html we got.
var result = $(data).find('#' + table[0].id);
// And add the new table html to the parent.
$(table).parent().html(result);
$("body").css("cursor", "auto");
});
});
function selectall(cls) { function selectall(cls) {
$('.' + cls).prop('checked', true); $('.' + cls).prop('checked', true);

View file

@ -152,6 +152,7 @@ Dialog._load = function(url) {
var jsbrowser = function() { var jsbrowser = function() {
FileBrowser.init(nickname, type, hash); 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", jsbrowser);
}; };
@ -206,6 +207,10 @@ function addToModal(url) {
//Get first element with the class "heading" //Get first element with the class "heading"
//and use it as title. //and use it as title.
loadModalTitle(); loadModalTitle();
// We need to initialize autosize again for new
// modal conent.
autosize($('.modal .text-autosize'));
} }
}); });
} }

View file

@ -446,8 +446,16 @@ function justifyPhotosAjax() {
$('#photo-album-contents').justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; }); $('#photo-album-contents').justifiedGallery('norewind').on('jg.complete', function(e){ justifiedGalleryActive = false; });
} }
// Load a js script to the html head.
function loadScript(url, callback) { function loadScript(url, callback) {
// Adding the script tag to the head as suggested before // Check if the script is already in the html head.
var oscript = $('head script[src="' + url + '"]');
// Delete the old script from head.
if (oscript.length > 0) {
oscript.remove();
}
// Adding the script tag to the head as suggested before.
var head = document.getElementsByTagName('head')[0]; var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script'); var script = document.createElement('script');
script.type = 'text/javascript'; script.type = 'text/javascript';
@ -458,7 +466,7 @@ function loadScript(url, callback) {
script.onreadystatechange = callback; script.onreadystatechange = callback;
script.onload = callback; script.onload = callback;
// Fire the loading // Fire the loading.
head.appendChild(script); head.appendChild(script);
} }

View file

@ -21,14 +21,10 @@ class Image
public static function get_options($arr) public static function get_options($arr)
{ {
$bg_image_options = [ $bg_image_options = [
'repeat' => [ 'stretch' => ['frio_bg_image_option', L10n::t('Top Banner'), 'stretch', L10n::t('Resize image to the width of the screen and show background color below on long pages.'), ($arr['bg_image_option'] == 'stretch')],
'frio_bg_image_option', L10n::t("Repeat the image"), "repeat", L10n::t("Will repeat your image to fill the background."), ($arr["bg_image_option"] == "repeat")], 'cover' => ['frio_bg_image_option', L10n::t('Full screen'), 'cover', L10n::t('Resize image to fill entire screen, clipping either the right or the bottom.'), ($arr['bg_image_option'] == 'cover')],
'stretch' => [ 'contain' => ['frio_bg_image_option', L10n::t('Single row mosaic'), 'contain', L10n::t('Resize image to repeat it on a single row, either vertical or horizontal.'), ($arr['bg_image_option'] == 'contain')],
'frio_bg_image_option', L10n::t("Stretch"), "stretch", L10n::t("Will stretch to width/height of the image."), ($arr["bg_image_option"] == "stretch")], 'repeat' => ['frio_bg_image_option', L10n::t('Mosaic'), 'repeat', L10n::t('Repeat image to fill the screen.'), ($arr['bg_image_option'] == 'repeat')],
'cover' => [
'frio_bg_image_option', L10n::t("Resize fill and-clip"), "cover", L10n::t("Resize to fill and retain aspect ratio."), ($arr["bg_image_option"] == "cover")],
'contain' => [
'frio_bg_image_option', L10n::t("Resize best fit"), "contain", L10n::t("Resize to best fit and retain aspect ratio."), ($arr["bg_image_option"] == "contain")],
]; ];
return $bg_image_options; return $bg_image_options;

View file

@ -26,8 +26,9 @@ if (!isset($minimal)) {
<script type="text/javascript">var baseurl = "<?php echo System::baseUrl(); ?>";</script> <script type="text/javascript">var baseurl = "<?php echo System::baseUrl(); ?>";</script>
<script type="text/javascript">var frio = "<?php echo 'view/theme/frio'; ?>";</script> <script type="text/javascript">var frio = "<?php echo 'view/theme/frio'; ?>";</script>
<?php <?php
$baseurl = System::baseUrl(); $basepath = $a->path ? "/" . $a->path . "/" : "/";
$frio = "view/theme/frio"; $frio = "view/theme/frio";
// Because we use minimal for modals the header and the included js stuff should be only loaded // Because we use minimal for modals the header and the included js stuff should be only loaded
// if the page is an standard page (so we don't have it twice for modals) // if the page is an standard page (so we don't have it twice for modals)
// //
@ -35,16 +36,17 @@ if (!isset($minimal)) {
if (!$minimal && x($page, 'htmlhead')) { if (!$minimal && x($page, 'htmlhead')) {
echo $page['htmlhead']; echo $page['htmlhead'];
} }
// Add the theme color meta // Add the theme color meta
// It makes mobile Chrome UI match Frio's top bar color. // It makes mobile Chrome UI match Frio's top bar color.
$uid = $a->profile_uid; $uid = $a->profile_uid;
if (is_null($uid)) { if (is_null($uid)) {
$uid = Profile::getThemeUid(); $uid = Profile::getThemeUid();
} }
$schema = PConfig::get($uid, 'frio', 'schema'); $scheme = PConfig::get($uid, 'frio', 'scheme', PConfig::get($uid, 'frio', 'schema'));
if (($schema) && ($schema != '---')) { if (($scheme) && ($scheme != '---')) {
if (file_exists('view/theme/frio/schema/' . $schema . '.php')) { if (file_exists('view/theme/frio/scheme/' . $scheme . '.php')) {
$schemefile = 'view/theme/frio/schema/' . $schema . '.php'; $schemefile = 'view/theme/frio/scheme/' . $scheme . '.php';
require_once $schemefile; require_once $schemefile;
} }
} else { } else {
@ -60,6 +62,7 @@ if (!isset($minimal)) {
$is_singleuser_class = $is_singleuser ? "is-singleuser" : "is-not-singleuser"; $is_singleuser_class = $is_singleuser ? "is-singleuser" : "is-not-singleuser";
?> ?>
</head> </head>
<body id="top" class="mod-<?php echo $a->module." ".$is_singleuser_class;?>"> <body id="top" class="mod-<?php echo $a->module." ".$is_singleuser_class;?>">
<a href="#content" class="sr-only sr-only-focusable">Skip to main content</a> <a href="#content" class="sr-only sr-only-focusable">Skip to main content</a>
<?php <?php
@ -90,7 +93,7 @@ if (!isset($minimal)) {
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<?php <?php
if ((!x($_REQUEST, 'pagename') || $_REQUEST['pagename'] != "lostpass") && ($_SERVER['REQUEST_URI'] != "/")) { if ((!x($_REQUEST, 'pagename') || $_REQUEST['pagename'] != "lostpass") && ($_SERVER['REQUEST_URI'] != $basepath)) {
echo ' echo '
<aside class="col-lg-3 col-md-3 offcanvas-sm offcanvas-xs">'; <aside class="col-lg-3 col-md-3 offcanvas-sm offcanvas-xs">';

View file

@ -1,74 +0,0 @@
<?php
/**
* @brief: Get info header of the shema
*
* This function parses the header of the shemename.php file for inormations like
* Author, Description and Overwrites. Most of the code comes from the Addon::getInfo()
* function. We use this to get the variables which get overwritten through the shema.
* All color variables which get overwritten through the theme have to be
* listed (comma seperated) in the shema header under Overwrites:
* This seemst not to be the best solution. We need to investigate further.
*
* @param string $schema Name of the shema
* @return array With theme information
* 'author' => Author Name
* 'description' => Schema description
* 'version' => Schema version
* 'overwrites' => Variables which overwriting custom settings
*/
use Friendica\Core\PConfig;
function get_schema_info($schema){
$theme = current_theme();
$themepath = "view/theme/" . $theme . "/";
$schema = PConfig::get(local_user(),'frio', 'schema');
$info=[
'name' => $schema,
'description' => "",
'author' => [],
'version' => "",
'overwrites' => []
];
if (!is_file($themepath . "schema/" . $schema . ".php")) return $info;
$f = file_get_contents($themepath . "schema/" . $schema . ".php");
$r = preg_match("|/\*.*\*/|msU", $f, $m);
if ($r){
$ll = explode("\n", $m[0]);
foreach( $ll as $l ) {
$l = trim($l,"\t\n\r */");
if ($l!=""){
list($k,$v) = array_map("trim", explode(":",$l,2));
$k= strtolower($k);
if ($k=="author"){
$r=preg_match("|([^<]+)<([^>]+)>|", $v, $m);
if ($r) {
$info['author'][] = ['name'=>$m[1], 'link'=>$m[2]];
} else {
$info['author'][] = ['name'=>$v];
}
} elseif ($k == "overwrites") {
$theme_settings = explode(',',str_replace(' ','', $v));
foreach ($theme_settings as $key => $value) {
$info["overwrites"][$value] = true;
}
} else {
if (array_key_exists($k,$info)){
$info[$k]=$v;
}
}
}
}
}
return $info;
}

View file

@ -0,0 +1,71 @@
<?php
/**
* @brief: Get info header of the scheme
*
* This function parses the header of the schemename.php file for informations like
* Author, Description and Overwrites. Most of the code comes from the Addon::getInfo()
* function. We use this to get the variables which get overwritten through the scheme.
* All color variables which get overwritten through the theme have to be
* listed (comma separated) in the scheme header under Overwrites:
* This seems not to be the best solution. We need to investigate further.
*
* @param string $scheme Name of the scheme
* @return array With theme information
* 'author' => Author Name
* 'description' => Scheme description
* 'version' => Scheme version
* 'overwrites' => Variables which overwriting custom settings
*/
use Friendica\Core\PConfig;
function get_scheme_info($scheme)
{
$theme = current_theme();
$themepath = 'view/theme/' . $theme . '/';
$scheme = PConfig::get(local_user(), 'frio', 'scheme', PConfig::get(local_user(), 'frio', 'scheme'));
$info = [
'name' => $scheme,
'description' => '',
'author' => [],
'version' => '',
'overwrites' => []
];
if (!is_file($themepath . 'scheme/' . $scheme . '.php')) return $info;
$f = file_get_contents($themepath . 'scheme/' . $scheme . '.php');
$r = preg_match('|/\*.*\*/|msU', $f, $m);
if ($r) {
$ll = explode("\n", $m[0]);
foreach ($ll as $l) {
$l = trim($l, "\t\n\r */");
if ($l != '') {
list($k, $v) = array_map('trim', explode(':', $l, 2));
$k = strtolower($k);
if ($k == 'author') {
$r = preg_match('|([^<]+)<([^>]+)>|', $v, $m);
if ($r) {
$info['author'][] = ['name' => $m[1], 'link' => $m[2]];
} else {
$info['author'][] = ['name' => $v];
}
} elseif ($k == 'overwrites') {
$theme_settings = explode(',', str_replace(' ', '', $v));
foreach ($theme_settings as $key => $value) {
$info['overwrites'][$value] = true;
}
} else {
if (array_key_exists($k, $info)) {
$info[$k] = $v;
}
}
}
}
}
return $info;
}

View file

@ -16,7 +16,7 @@
<?php $frio = "view/theme/frio"; ?> <?php $frio = "view/theme/frio"; ?>
<?php if(x($page,'htmlhead')) echo $page['htmlhead']; ?> <?php if(x($page,'htmlhead')) echo $page['htmlhead']; ?>
</head> </head>
<body id=\"top\">"; <body id="top">
<?php if($_SERVER['REQUEST_URI'] == "/"){header('Location: /login');} ?> <?php if($_SERVER['REQUEST_URI'] == "/"){header('Location: /login');} ?>
<a href="#content" class="sr-only sr-only-focusable">Skip to main content</a> <a href="#content" class="sr-only sr-only-focusable">Skip to main content</a>
<?php <?php

View file

@ -0,0 +1,16 @@
<?php
/*
* Name: Blue
* Author: Rabuzarus
*
* List here all variables which will get overwritten through this scheme
* Overwrites: nav_bg, nav_icon_color, link_color, background_color, login_bg_color, contentbg_transp
*/
$nav_bg = "#708fa0";
$nav_icon_color = "#fff";
$link_color = "#6fdbe8";
$background_color = "#ededed";
$login_bg_color = "#ededed";
$contentbg_transp = 100;

View file

@ -1,13 +1,13 @@
<?php <?php
/* Licence: AGP /* Licence: AGP
* Author: rabuzarus * Author: rabuzarus
* Overwrites: nav_bg, nav_icon_color, link_color, bgcolor, contentbg_transp, background_image, bg_image_option, link_hover_color * Overwrites: nav_bg, nav_icon_color, link_color, background_color, contentbg_transp, background_image, bg_image_option, link_hover_color
*/ */
$nav_bg = "#000"; $nav_bg = "#000";
$nav_icon_color = "#e355e0"; $nav_icon_color = "#e355e0";
$link_color = "#e355e0"; $link_color = "#e355e0";
$bgcolor = "#fff"; $background_color = "#fff";
$contentbg_transp = 100; $contentbg_transp = 100;
$background_image = "img/bg_circle.png"; $background_image = "img/bg_circle.png";
$bg_image_option = "repeat"; $bg_image_option = "repeat";

View file

@ -3,13 +3,13 @@
* Name: Red * Name: Red
* Author: Rabuzarus * Author: Rabuzarus
* *
* List here all variables which will get overwritten through this schema * List here all variables which will get overwritten through this scheme
* Overwrites: nav_bg, nav_icon_color, link_color, bgcolor, contentbg_transp * Overwrites: nav_bg, nav_icon_color, link_color, background_color, contentbg_transp
*/ */
$nav_bg = "#870000"; $nav_bg = "#870000";
$nav_icon_color = "#f5f5f5"; $nav_icon_color = "#f5f5f5";
$link_color = "#b50404"; $link_color = "#b50404";
$bgcolor = "#ededed"; $background_color = "#ededed";
$contentbg_transp = 95; $contentbg_transp = 95;

View file

@ -8,7 +8,7 @@ use Friendica\Model\Profile;
require_once 'view/theme/frio/php/PHPColors/Color.php'; require_once 'view/theme/frio/php/PHPColors/Color.php';
$schemecss = ""; $schemecss = '';
$schemecssfile = false; $schemecssfile = false;
$scheme_modified = 0; $scheme_modified = 0;
@ -19,15 +19,15 @@ if ($a->module !== 'install') {
PConfig::load($uid, 'frio'); PConfig::load($uid, 'frio');
// Load the profile owners pconfig. // Load the profile owners pconfig.
$schema = PConfig::get($uid, "frio", "schema"); $scheme = PConfig::get($uid, 'frio', 'scheme', PConfig::get($uid, 'frio', 'schema'));
$nav_bg = PConfig::get($uid, "frio", "nav_bg"); $nav_bg = PConfig::get($uid, 'frio', 'nav_bg');
$nav_icon_color = PConfig::get($uid, "frio", "nav_icon_color"); $nav_icon_color = PConfig::get($uid, 'frio', 'nav_icon_color');
$link_color = PConfig::get($uid, "frio", "link_color"); $link_color = PConfig::get($uid, 'frio', 'link_color');
$bgcolor = PConfig::get($uid, "frio", "background_color"); $background_color = PConfig::get($uid, 'frio', 'background_color');
$contentbg_transp = PConfig::get($uid, "frio", "contentbg_transp"); $contentbg_transp = PConfig::get($uid, 'frio', 'contentbg_transp');
$background_image = PConfig::get($uid, "frio", "background_image"); $background_image = PConfig::get($uid, 'frio', 'background_image');
$bg_image_option = PConfig::get($uid, "frio", "bg_image_option"); $bg_image_option = PConfig::get($uid, 'frio', 'bg_image_option');
$modified = PConfig::get($uid, "frio", "css_modified"); $modified = PConfig::get($uid, 'frio', 'css_modified');
// There is maybe the case that the user did never modify the theme settings. // There is maybe the case that the user did never modify the theme settings.
// In this case we store the present time. // In this case we store the present time.
@ -38,17 +38,17 @@ if ($a->module !== 'install') {
Config::load('frio'); Config::load('frio');
// Load frios system config. // Load frios system config.
$schema = Config::get("frio", "schema"); $scheme = Config::get('frio', 'scheme', Config::get('frio', 'schema'));
$nav_bg = Config::get("frio", "nav_bg"); $nav_bg = Config::get('frio', 'nav_bg');
$nav_icon_color = Config::get("frio", "nav_icon_color"); $nav_icon_color = Config::get('frio', 'nav_icon_color');
$link_color = Config::get("frio", "link_color"); $link_color = Config::get('frio', 'link_color');
$bgcolor = Config::get("frio", "background_color"); $background_color = Config::get('frio', 'background_color');
$contentbg_transp = Config::get("frio", "contentbg_transp"); $contentbg_transp = Config::get('frio', 'contentbg_transp');
$background_image = Config::get("frio", "background_image"); $background_image = Config::get('frio', 'background_image');
$bg_image_option = Config::get("frio", "bg_image_option"); $bg_image_option = Config::get('frio', 'bg_image_option');
$login_bg_image = Config::get("frio", "login_bg_image"); $login_bg_image = Config::get('frio', 'login_bg_image');
$login_bg_color = Config::get("frio", "login_bg_color"); $login_bg_color = Config::get('frio', 'login_bg_color');
$modified = Config::get("frio", "css_modified"); $modified = Config::get('frio', 'css_modified');
// There is maybe the case that the user did never modify the theme settings. // There is maybe the case that the user did never modify the theme settings.
// In this case we store the present time. // In this case we store the present time.
@ -59,47 +59,47 @@ if ($a->module !== 'install') {
} }
// Now load the scheme. If a value is changed above, we'll keep the settings // Now load the scheme. If a value is changed above, we'll keep the settings
// If not, we'll keep those defined by the schema // If not, we'll keep those defined by the scheme
// Setting $schema to '' wasn't working for some reason, so we'll check it's // Setting $scheme to '' wasn't working for some reason, so we'll check it's
// not --- like the mobile theme does instead. // not --- like the mobile theme does instead.
// Allow layouts to over-ride the schema. // Allow layouts to over-ride the scheme.
if (x($_REQUEST, 'schema')) { if (x($_REQUEST, 'scheme')) {
$schema = $_REQUEST['schema']; $scheme = $_REQUEST['scheme'];
} }
// Sanitize the data. // Sanitize the data.
$schema = !empty($schema) ? basename($schema) : ""; $scheme = !empty($scheme) ? basename($scheme) : '';
if (($schema) && ($schema != '---')) { if (($scheme) && ($scheme != '---')) {
if (file_exists('view/theme/frio/schema/' . $schema . '.php')) { if (file_exists('view/theme/frio/scheme/' . $scheme . '.php')) {
$schemefile = 'view/theme/frio/schema/' . $schema . '.php'; $schemefile = 'view/theme/frio/scheme/' . $scheme . '.php';
require_once $schemefile; require_once $schemefile;
} }
if (file_exists('view/theme/frio/schema/' . $schema . '.css')) { if (file_exists('view/theme/frio/scheme/' . $scheme . '.css')) {
$schemecssfile = 'view/theme/frio/schema/' . $schema . '.css'; $schemecssfile = 'view/theme/frio/scheme/' . $scheme . '.css';
} }
} }
// If we haven't got a schema, load the default. We shouldn't touch this - we // If we haven't got a scheme, load the default. We shouldn't touch this - we
// should leave it for admins to define for themselves. // should leave it for admins to define for themselves.
// default.php and default.css MUST be symlinks to existing schema files. // default.php and default.css MUST be symlinks to existing scheme files.
if (! $schema) { if (!$scheme) {
if (file_exists('view/theme/frio/schema/default.php')) { if (file_exists('view/theme/frio/scheme/default.php')) {
$schemefile = 'view/theme/frio/schema/default.php'; $schemefile = 'view/theme/frio/scheme/default.php';
require_once $schemefile; require_once $schemefile;
} }
if (file_exists('view/theme/frio/schema/default.css')) { if (file_exists('view/theme/frio/scheme/default.css')) {
$schemecssfile = 'view/theme/frio/schema/default.css'; $schemecssfile = 'view/theme/frio/scheme/default.css';
} }
} }
//Set some defaults - we have to do this after pulling owner settings, and we have to check for each setting //Set some defaults - we have to do this after pulling owner settings, and we have to check for each setting
//individually. If we don't, we'll have problems if a user has set one, but not all options. //individually. If we don't, we'll have problems if a user has set one, but not all options.
$nav_bg = (empty($nav_bg) ? "#708fa0" : $nav_bg); $nav_bg = (empty($nav_bg) ? '#708fa0' : $nav_bg);
$nav_icon_color = (empty($nav_icon_color) ? "#fff" : $nav_icon_color); $nav_icon_color = (empty($nav_icon_color) ? '#fff' : $nav_icon_color);
$link_color = (empty($link_color) ? "#6fdbe8" : $link_color); $link_color = (empty($link_color) ? '#6fdbe8' : $link_color);
$bgcolor = (empty($bgcolor) ? "#ededed" : $bgcolor); $background_color = (empty($background_color) ? '#ededed' : $background_color);
// The background image can not be empty. So we use a dummy jpg if no image was set. // The background image can not be empty. So we use a dummy jpg if no image was set.
$background_image = (empty($background_image) ? 'img/none.jpg' : $background_image); $background_image = (empty($background_image) ? 'img/none.jpg' : $background_image);
$modified = (empty($modified) ? time() : $modified); $modified = (empty($modified) ? time() : $modified);
@ -107,12 +107,11 @@ $modified = (empty($modified) ? time() :$modified);
// set a default login bg image if no custom image and no custom bg color are set. // set a default login bg image if no custom image and no custom bg color are set.
if (empty($login_bg_image) && empty($login_bg_color)) { if (empty($login_bg_image) && empty($login_bg_color)) {
$login_bg_image = (empty($login_bg_image) ? 'img/login_bg.jpg' : $login_bg_image); $login_bg_image = 'img/login_bg.jpg';
} }
$login_bg_color = (empty($login_bg_color) ? "#ededed" : $login_bg_color); $login_bg_color = (empty($login_bg_color) ? '#ededed' : $login_bg_color);
$contentbg_transp = ((isset($contentbg_transp) && $contentbg_transp != '') ? $contentbg_transp : 100);
$contentbg_transp = ((isset($contentbg_transp) && $contentbg_transp != "") ? $contentbg_transp : 100);
// Calculate some colors in dependance of existing colors. // Calculate some colors in dependance of existing colors.
// Some colors are calculated to don't have too many selection // Some colors are calculated to don't have too many selection
@ -153,22 +152,28 @@ if (!isset($link_hover_color)) {
if (!isset($bg_image_option)) { if (!isset($bg_image_option)) {
$bg_image_option = null; $bg_image_option = null;
} }
switch ($bg_image_option) { switch ($bg_image_option) {
case "stretch": case 'stretch':
$background_size_img = "100%"; $background_size_img = '100%';
$background_repeat = 'no-repeat';
break; break;
case "cover": case 'cover':
$background_size_img ="cover"; $background_size_img = 'cover';
$background_repeat = 'no-repeat';
break; break;
case "repeat": case 'repeat':
$background_size_img = "auto"; $background_size_img = 'auto';
$background_repeat = 'repeat';
break; break;
case "contain": case 'contain':
$background_size_img = "contain"; $background_size_img = 'contain';
$background_repeat = 'repeat';
break; break;
default: default:
$background_size_img = "auto"; $background_size_img = 'auto';
$background_repeat = 'no-repeat';
break; break;
} }
@ -184,10 +189,11 @@ $options = [
'$menu_background_hover_color' => $menu_background_hover_color, '$menu_background_hover_color' => $menu_background_hover_color,
'$btn_primary_color' => $nav_icon_color, '$btn_primary_color' => $nav_icon_color,
'$btn_primary_hover_color' => $menu_background_hover_color, '$btn_primary_hover_color' => $menu_background_hover_color,
'$bgcolor' => $bgcolor, '$background_color' => $background_color,
'$contentbg_transp' => $contentbg_transp, '$contentbg_transp' => $contentbg_transp,
'$background_image' => $background_image, '$background_image' => $background_image,
'$background_size_img' => $background_size_img, '$background_size_img' => $background_size_img,
'$background_repeat' => $background_repeat,
'$login_bg_image' => $login_bg_image, '$login_bg_image' => $login_bg_image,
'$login_bg_color' => $login_bg_color '$login_bg_color' => $login_bg_color
]; ];
@ -220,7 +226,7 @@ header('Last-Modified: '.$modified);
// Only send the CSS file if it was changed. // Only send the CSS file if it was changed.
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) { if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) || isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$cached_modified = gmdate('r', strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])); $cached_modified = gmdate('r', strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']));
$cached_etag = str_replace(['"', "-gzip"], ['', ''], $cached_etag = str_replace(['"', '-gzip'], ['', ''],
stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])); stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));
if (($cached_modified == $modified) && ($cached_etag == $etag)) { if (($cached_modified == $modified) && ($cached_etag == $etag)) {

View file

@ -1,16 +1,55 @@
<script type="text/javascript" src="view/theme/frio/js/mod_admin.js"></script> <script type="text/javascript" src="view/theme/frio/js/mod_admin.js"></script>
<div id="adminpage"> <link rel="stylesheet" href="view/theme/frio/css/mod_admin.css" type="text/css" media="screen"/>
<div id="admin-contactblock" class="adminpage generic-page-wrapper">
<h1>{{$title}} - {{$page}}</h1> <h1>{{$title}} - {{$page}}</h1>
<p>{{$description}}</p> <p>{{$description}}</p>
{{* We organize the settings in collapsable panel-groups *}}
<div class="panel-group panel-group-settings" id="admin-settings" role="tablist" aria-multiselectable="true">
{{* The form for entering user profile which should be blocked *}}
<div class="panel">
<div class="section-subtitle-wrapper" role="tab" id="admin-settings-contactblock-block">
<h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-contactblock-block-collapse" aria-expanded="false" aria-controls="admin-settings-contactblock-block-collapse">
{{$h_newblock}}
</a>
</h4>
</div>
<div id="admin-settings-contactblock-block-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-contactblock-block">
<form action="{{$baseurl}}/admin/contactblock" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
{{include file="field_input.tpl" field=$contacturl}}
<div class="admin-settings-submit-wrapper form-group pull-right">
<button type="submit" class="btn btn-primary" name="page_contactblock_block" value="1">{{$submit|escape:'html'}}</button>
</div>
<div class="clear"></div>
</form>
</div>
</div>
{{* The list of blocked user profiles with the possibility to unblock them *}}
<div class="panel">
<div class="section-subtitle-wrapper" role="tab" id="admin-settings-contactblock-blocked">
<h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-contactblock-blocked-collapse" aria-expanded="{{if count($contacts) > 0}}true{{else}}false{{/if}}" aria-controls="admin-settings-contactblock-blocked-collapse">
{{$h_contacts}} ({{count($contacts)}})
</a>
</h4>
</div>
<div id="admin-settings-contactblock-blocked-collapse" class="panel-collapse collapse {{if count($contacts) > 0}}in{{/if}}" role="tabpanel" aria-labelledby="admin-settings-contactblock-blocked">
<form action="{{$baseurl}}/admin/contactblock" method="post"> <form action="{{$baseurl}}/admin/contactblock" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}"> <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<h3>{{$h_contacts}}</h3>
{{if $contacts}} {{if $contacts}}
<table id="contactblock" class="table table-condensed table-striped"> <table id="contactblock" class="table table-condensed table-striped">
<thead> <thead>
<tr> <tr>
<th><input type="checkbox" class="select contacts_ckbx" data-select-class="contacts_ckbx" data-select-all="{{$select_all}}" data-select-none="{{$select_none}}" title="{{$select_all}}"/></th> <th></th>
{{foreach $th_contacts as $th}} {{foreach $th_contacts as $th}}
<th> <th>
{{$th}} {{$th}}
@ -22,39 +61,45 @@
<tbody> <tbody>
{{foreach $contacts as $contact}} {{foreach $contacts as $contact}}
<tr> <tr>
<td><input type="checkbox" class="contacts_ckbx" id="id_contact_{{$contact.id}}" name="contacts[]" value="{{$contact.id}}"/></td> <td>
<div class="checkbox">
<input type="checkbox" class="contacts_ckbx" id="id_contact_{{$contact.id}}" name="contacts[]" value="{{$contact.id}}"/>
<label for="id_contact_{{$contact.id}}"></label>
</div>
</td>
<td><img class="icon" src="{{$contact.micro}}" alt="{{$contact.nickname}}" title="{{$contact.addr}}"></td> <td><img class="icon" src="{{$contact.micro}}" alt="{{$contact.nickname}}" title="{{$contact.addr}}"></td>
<td class="name">{{$contact.name}}</td> <td class="name">{{$contact.name}}</td>
<td class="addr" colspan="2"><a href="{{$contact.url}}" title="{{$contact.addr}}" >{{$contact.url}}</a></td> <td class="addr" colspan="3"><a href="{{$contact.url}}" title="{{$contact.addr}}" >{{$contact.url}}</a></td>
</tr> </tr>
{{/foreach}} {{/foreach}}
</tbody> </tbody>
<tfoot> <tfoot>
<tr> <tr>
<td><input type="checkbox" class="select contacts_ckbx" data-select-class="contacts_ckbx" data-select-all="{{$select_all}}" data-select-none="{{$select_none}}" title="{{$select_all}}"/></td> <td>
<td colspan="3"> {{* Checkbox to select all blocked contacts *}}
<div class="checkbox">
<input type="checkbox" id="contactblock-select" class="selecttoggle contacts_ckbx" data-select-class="contacts_ckbx" data-select-all="{{$select_all}}" data-select-none="{{$select_none}}" title="{{$select_all}}"/>
<label for="contactblock-select"></label>
</div>
</td>
<td colspan="5">
{{$total_contacts}} {{$total_contacts}}
<div class="admin-settings-submit-wrapper form-group pull-right">
<button type="submit" class="btn btn-small btn-default pull-right" name="page_contactblock_unblock" value="1">{{$unblock|escape:'html'}}</button>
</div>
<div class="clear"></div>
</td> </td>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
<div class="submit"><button type="submit" class="btn btn-small btn-default" name="page_contactblock_unblock" value="1">{{$unblock|escape:'html'}}</button></div>
{{$paginate}} {{$paginate}}
{{else}} {{else}}
<p>{{$no_data|escape:'html'}}</p> <p>{{$no_data|escape:'html'}}</p>
{{/if}} {{/if}}
</form> </form>
</div>
<h3>{{$h_newblock}}</h3> </div>
<form action="{{$baseurl}}/admin/contactblock" method="post"> </div>
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<table id="contactblock">
<tbody>
<tr>
<td>{{include file="field_input.tpl" field=$contacturl}}</td>
</tr>
</tbody>
</table>
<div class="submit"><button type="submit" class="btn btn-primary" name="page_contactblock_block" value="1">{{$submit|escape:'html'}}</button></div>
</form>
</div> </div>

View file

@ -7,6 +7,8 @@
<form action="{{$baseurl}}/admin/users" method="post"> <form action="{{$baseurl}}/admin/users" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}"> <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
{{* We organize the settings in collapsable panel-groups *}}
<div class="panel-group panel-group-settings" id="admin-settings" role="tablist" aria-multiselectable="true">
<!-- <!--
** **
@ -15,9 +17,16 @@
* *
** **
--> -->
<div class="panel panel-default"> <div class="panel">
<div class="panel-heading"><h3 class="panel-title">{{$h_pending}}</h3></div> <div class="section-subtitle-wrapper" role="tab" id="admin-settings-pending">
<h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-pending-collapse" aria-expanded="{{if count($pending) > 0}}true{{else}}false{{/if}}" aria-controls="admin-settings-pending-collapse">
{{$h_pending}} ({{count($pending)}})
</a>
</h4>
</div>
<div id="admin-settings-pending-collapse" class="panel-collapse collapse {{if count($pending) > 0}}in{{/if}}" role="tabpanel" aria-labelledby="admin-settings-pending">
{{if $pending}} {{if $pending}}
<table id="pending" class="table table-hover"> <table id="pending" class="table table-hover">
<thead> <thead>
@ -30,34 +39,45 @@
<tbody> <tbody>
{{foreach $pending as $u}} {{foreach $pending as $u}}
<tr> <tr>
<td><input type="checkbox" class="pending_ckbx" id="id_pending_{{$u.hash}}" name="pending[]" value="{{$u.hash}}" /></td> <td>
<div class="checkbox">
<input type="checkbox" class="pending_ckbx" id="id_pending_{{$u.hash}}" name="pending[]" value="{{$u.hash}}" />
<label for="id_pending_{{$u.hash}}"></label>
</div>
</td>
<td>{{$u.created}}</td> <td>{{$u.created}}</td>
<td>{{$u.name}}</td> <td>{{$u.name}}</td>
<td>{{$u.email}}</td> <td>{{$u.email}}</td>
<td> <td>
<a href="{{$baseurl}}/regmod/allow/{{$u.hash}}" title="{{$approve}}"><i class="fa fa-thumbs-up" aria-hidden="true"></i></a> <a href="{{$baseurl}}/regmod/allow/{{$u.hash}}" class="admin-settings-action-link" title="{{$approve}}"><i class="fa fa-check" aria-hidden="true"></i></a>
<a href="{{$baseurl}}/regmod/deny/{{$u.hash}}" title="{{$deny}}"><i class="fa fa-thumbs-down" aria-hidden="true"></i></a> <a href="{{$baseurl}}/regmod/deny/{{$u.hash}}" class="admin-settings-action-link" title="{{$deny}}"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
</td> </td>
</tr> </tr>
{{if $u.note}}
<tr class="details"> <tr class="details">
<td></td> <td></td>
<th>{{$pendingnotetext}}</th> <th>{{$pendingnotetext}}</th>
<td colspan="4">{{$u.note}}</td> <td colspan="4">{{$u.note}}</td>
</tr> </tr>
{{/if}}
{{/foreach}} {{/foreach}}
</tbody> </tbody>
</table> </table>
<div class="panel-footer"> <div class="panel-footer">
<div class="row"> <div class="row">
<div class="col-xs-3"> <div class="col-xs-3 admin-settings-footer-elements">
<div class="btn-group" role="group"> <div class="checkbox">
<button type="button" class="btn btn-default selectall" data-select-all="pending_ckbx"><i class="fa fa-check-square-o" aria-hidden="true"></i></button> <input type="checkbox" id="admin-settings-pending-select" class="selecttoggle" data-select-class="pending_ckbx"/>
<button type="button" class="btn btn-default selectnone" data-select-none="pending_ckbx"><i class="fa fa-square-o" aria-hidden="true"></i></button> <label for="admin-settings-pending-select"></label>
</div> </div>
</div> </div>
<div class="col-xs-9 text-right"> <div class="col-xs-9 admin-settings-footer-elements text-right">
<button type="submit" name="page_users_deny" class="btn btn-primary"><i class="fa fa-thumbs-down" aria-hidden="true"></i> {{$deny}}</button> <button type="submit" name="page_users_deny" value="1" class="btn btn-primary">
<button type="submit" name="page_users_approve" class="btn btn-warinig"><i class="fa fa-thumbs-up" aria-hidden="true"></i> {{$approve}}</button> <i class="fa fa-trash-o" aria-hidden="true"></i> {{$deny}}
</button>
<button type="submit" name="page_users_approve" value="1" class="btn btn-warinig">
<i class="fa fa-check" aria-hidden="true"></i> {{$approve}}
</button>
</div> </div>
</div> </div>
</div> </div>
@ -65,6 +85,7 @@
<div class="panel-body text-center text-muted">{{$no_pending}}</div> <div class="panel-body text-center text-muted">{{$no_pending}}</div>
{{/if}} {{/if}}
</div> </div>
</div>
<!-- <!--
** **
@ -73,10 +94,18 @@
* *
** **
--> -->
<div class="panel panel-default"> <div class="panel">
<div class="panel-heading"><h3 class="panel-title">{{$h_users}}</h3></div> <div class="section-subtitle-wrapper" role="tab" id="admin-settings-user">
{{if $users}} <h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-user-collapse" aria-expanded="false" aria-controls="admin-settings-user-collapse">
{{$h_users}} ({{count($users)}})
</a>
</h4>
</div>
<div id="admin-settings-user-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-user">
{{if $users}}
<table id="users" class="table table-hover"> <table id="users" class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -85,7 +114,7 @@
{{foreach $th_users as $k=>$th}} {{foreach $th_users as $k=>$th}}
{{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }} {{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }}
<th class="th-{{$k}}"> <th class="th-{{$k}}">
<a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}"> <button type="button" data-order-url="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}" class="btn-link table-order">
{{if $order_users == $th.1}} {{if $order_users == $th.1}}
{{if $order_direction_users == "+"}} {{if $order_direction_users == "+"}}
&#8595; &#8595;
@ -95,7 +124,8 @@
{{else}} {{else}}
&#8597; &#8597;
{{/if}} {{/if}}
{{$th.0}}</a> {{$th.0}}
</button>
</th> </th>
{{/if}} {{/if}}
{{/foreach}} {{/foreach}}
@ -104,10 +134,13 @@
</thead> </thead>
<tbody> <tbody>
{{foreach $users as $u}} {{foreach $users as $u}}
<tr id="user-{{$u.uid}}"> <tr id="user-{{$u.uid}}" class="{{if $u.blocked != 0}}blocked{{/if}}">
<td> <td>
{{if $u.is_deletable}} {{if $u.is_deletable}}
<div class="checkbox">
<input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/> <input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/>
<label for="id_user_{{$u.uid}}"></label>
</div>
{{else}} {{else}}
&nbsp; &nbsp;
{{/if}} {{/if}}
@ -128,7 +161,6 @@
{{/if}} {{/if}}
{{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }} {{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<td> <td>
<i class="fa <i class="fa
{{if $u.page_flags_raw==0}}fa-user{{/if}} {{* PAGE_NORMAL *}} {{if $u.page_flags_raw==0}}fa-user{{/if}} {{* PAGE_NORMAL *}}
@ -137,56 +169,69 @@
{{if $u.page_flags_raw==3}}fa-heart{{/if}} {{* PAGE_FREELOVE *}} {{if $u.page_flags_raw==3}}fa-heart{{/if}} {{* PAGE_FREELOVE *}}
{{if $u.page_flags_raw==4}}fa-rss{{/if}} {{* PAGE_BLOG *}} {{if $u.page_flags_raw==4}}fa-rss{{/if}} {{* PAGE_BLOG *}}
{{if $u.page_flags_raw==5}}fa-user-secret{{/if}} {{* PAGE_PRVGROUP *}} {{if $u.page_flags_raw==5}}fa-user-secret{{/if}} {{* PAGE_PRVGROUP *}}
" title="{{$u.page_flags}}"></i> " title="{{$u.page_flags}}">
</i>
{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}} {{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}
<i class="fa <i class="fa
{{if $u.account_type_raw==1}}fa-sitemap{{/if}} {{* ACCOUNT_TYPE_ORGANISATION *}} {{if $u.account_type_raw==1}}fa-sitemap{{/if}} {{* ACCOUNT_TYPE_ORGANISATION *}}
{{if $u.account_type_raw==2}}fa-newspaper-o{{/if}} {{* ACCOUNT_TYPE_NEWS *}} {{if $u.account_type_raw==2}}fa-newspaper-o{{/if}} {{* ACCOUNT_TYPE_NEWS *}}
{{if $u.account_type_raw==3}}fa-comments{{/if}} {{* ACCOUNT_TYPE_COMMUNITY *}} {{if $u.account_type_raw==3}}fa-comments{{/if}} {{* ACCOUNT_TYPE_COMMUNITY *}}
" title="{{$u.account_type}}"></i> " title="{{$u.account_type}}">
</i>
{{/if}} {{/if}}
{{if $u.is_admin}}<i class="fa fa-user-md text-primary" title="{{$siteadmin}}"></i>{{/if}} {{if $u.is_admin}}<i class="fa fa-user-md text-primary" title="{{$siteadmin}}"></i>{{/if}}
{{if $u.account_expired}}<i class="fa fa-clock-o text-warning" title="{{$accountexpired}}"></i>{{/if}} {{if $u.account_expired}}<i class="fa fa-clock-o text-warning" title="{{$accountexpired}}"></i>{{/if}}
</td> </td>
{{/if}} {{/if}}
<td class="text-right"> <td class="text-right">
<button type="button" class="btn-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button> <button type="button" class="btn-link admin-settings-action-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button>
</td> </td>
</tr> </tr>
<tr id="user-{{$u.uid}}-detail" class="hidden details"> <tr id="user-{{$u.uid}}-detail" class=" details hidden {{if $u.blocked != 0}}blocked{{/if}}">
<td>&nbsp;</td> <td>&nbsp;</td>
<td colspan="4"> <td colspan="4">
{{if $order_users != $th_users.2.1}} {{if $order_users != $th_users.2.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}"> <p>
&#8597; {{$th_users.2.0}}</a> : {{$u.register_date}}</p> <button type="button" data-order-url="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}" class="btn-link table-order">
&#8597; {{$th_users.2.0}}</button> : {{$u.register_date}}
</p>
{{/if}} {{/if}}
{{if $order_users != $th_users.3.1}} {{if $order_users != $th_users.3.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}"> <p>
&#8597; {{$th_users.3.0}}</a> : {{$u.login_date}}</p> <button type="button" data-order-url="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}" class="btn-link table-order">
&#8597; {{$th_users.3.0}}</button> : {{$u.login_date}}
</p>
{{/if}} {{/if}}
{{if $order_users != $th_users.4.1}} {{if $order_users != $th_users.4.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}"> <p>
&#8597; {{$th_users.4.0}}</a> : {{$u.lastitem_date}}</p> <button type="button" data-order-url="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}" class="btn-link table-order">
&#8597; {{$th_users.4.0}}</button> : {{$u.lastitem_date}}
</p>
{{/if}} {{/if}}
{{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }} {{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}"> <p>
&#8597; {{$th_users.5.0}}</a> : {{$u.page_flags}}{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}, {{$u.account_type}}{{/if}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}</p> <button type="button" data-order-url="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}" class="btn-link table-order">
&#8597; {{$th_users.5.0}}</button> : {{$u.page_flags}}{{if $u.page_flags_raw==0 && $u.account_type_raw > 0}}, {{$u.account_type}}{{/if}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}
</p>
{{/if}} {{/if}}
</td> </td>
<td class="text-right"> <td class="text-right">
{{if $u.is_deletable}} {{if $u.is_deletable}}
<a href="{{$baseurl}}/admin/users/block/{{$u.uid}}?t={{$form_security_token}}" title="{{if $u.blocked}}{{$unblock}}{{else}}{{$block}}{{/if}}"> <a href="{{$baseurl}}/admin/users/block/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link"title="{{if $u.blocked}}{{$unblock}}{{else}}{{$block}}{{/if}}">
{{if $u.blocked == 0}} {{if $u.blocked == 0}}
<i class="fa fa-ban" aria-hidden="true"></i> <i class="fa fa-ban" aria-hidden="true"></i>
{{else}} {{else}}
<i class="fa fa-circle-o" aria-hidden="true"></i> <i class="fa fa-circle-o" aria-hidden="true"></i>
{{/if}} {{/if}}
</a> </a>
<a href="{{$baseurl}}/admin/users/delete/{{$u.uid}}?t={{$form_security_token}}" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')"><i class="fa fa-trash" aria-hidden="true"></i></a> <a href="{{$baseurl}}/admin/users/delete/{{$u.uid}}?t={{$form_security_token}}" class="admin-settings-action-link" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')">
<i class="fa fa-trash" aria-hidden="true"></i>
</a>
{{else}} {{else}}
&nbsp; &nbsp;
{{/if}} {{/if}}
@ -197,15 +242,19 @@
</table> </table>
<div class="panel-footer"> <div class="panel-footer">
<div class="row"> <div class="row">
<div class="col-xs-3"> <div class="col-xs-3 admin-settings-footer-elements">
<div class="btn-group" role="group"> <div class="checkbox">
<button type="button" class="btn btn-default selectall" data-select-all="users_ckbx"><i class="fa fa-check-square-o" aria-hidden="true"></i></button> <input type="checkbox" id="admin-settings-users-select" class="selecttoggle" data-select-class="users_ckbx"/>
<button type="button" class="btn btn-default selectnone" data-select-none="users_ckbx"><i class="fa fa-square-o" aria-hidden="true"></i></button> <label for="admin-settings-users-select"></label>
</div> </div>
</div> </div>
<div class="col-xs-9 text-right"> <div class="col-xs-9 admin-settings-footer-elements text-right">
<button type="submit" name="page_users_block" class="btn btn-warning"> <i class="fa fa-ban" aria-hidden="true"></i> {{$block}} / <i class="fa fa-circle-o" aria-hidden="true"></i> {{$unblock}}</button> <button type="submit" name="page_users_block" value="1" class="btn btn-warning">
<button type="submit" name="page_users_delete" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')"><i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}</button> <i class="fa fa-ban" aria-hidden="true"></i> {{$block}} / <i class="fa fa-circle-o" aria-hidden="true"></i> {{$unblock}}
</button>
<button type="submit" name="page_users_delete" value="1" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')">
<i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}
</button>
</div> </div>
</div> </div>
</div> </div>
@ -213,13 +262,7 @@
<div class="panel-body text-center bg-danger">NO USERS?!?</div> <div class="panel-body text-center bg-danger">NO USERS?!?</div>
{{/if}} {{/if}}
</div> </div>
</div>
</form>
<!-- <!--
@ -230,8 +273,16 @@
** **
--> -->
{{if $deleted}} {{if $deleted}}
<div class="panel panel-default"> <div class="panel">
<div class="panel-heading"><h3 class="panel-title">{{$h_deleted}}</h3></div> <div class="section-subtitle-wrapper" role="tab" id="admin-settings-deleted">
<h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-deleted-collapse" aria-expanded="false" aria-controls="admin-settings-deleted-collapse">
{{$h_deleted}} ({{count($deleted)}})
</a>
</h4>
</div>
<div id="admin-settings-deleted-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-deleted">
<table id="deleted" class="table table-hover"> <table id="deleted" class="table table-hover">
<thead> <thead>
<tr> <tr>
@ -255,6 +306,7 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
{{/if}} {{/if}}
@ -266,11 +318,16 @@
* *
** **
--> -->
<form action="{{$baseurl}}/admin/users" method="post"> <div class="panel">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}"> <div class="section-subtitle-wrapper" role="tab" id="admin-settings-new-user">
<h4>
<a class="accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-new-user-collapse" aria-expanded="false" aria-controls="admin-settings-new-user-collapse">
{{$h_newuser}}
</a>
</h4>
</div>
<div class="panel panel-default"> <div id="admin-settings-new-user-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-new-user">
<div class="panel-heading"><h3 class="panel-title">{{$h_newuser}}</h3></div>
<div class="panel-body"> <div class="panel-body">
{{include file="field_input.tpl" field=$newusername}} {{include file="field_input.tpl" field=$newusername}}
{{include file="field_input.tpl" field=$newusernickname}} {{include file="field_input.tpl" field=$newusernickname}}
@ -278,8 +335,11 @@
</div> </div>
<div class="panel-footer text-right"> <div class="panel-footer text-right">
<button type="submit" class="btn btn-primary">{{$submit}}</button> <button type="submit" class="btn btn-primary">{{$submit}}</button>
</form>
</div> </div>
</form>
</div> </div>
</div>
</div>
</form>
</div>

View file

@ -33,7 +33,7 @@
{{/if}} {{/if}}
<ul class="comment-edit-bb-{{$id}} comment-icon-list nav nav-pills pull-right"> <ul class="comment-edit-bb-{{$id}} comment-icon-list nav nav-pills pull-right">
<li> <li>
<button type="button" class="btn-link icon" style="cursor: pointer;" aria-label="{{$edimg}}" title="{{$edimg}}" data-role="insert-formatting" data-bbcode="img" data-id="{{$id}}"> <button type="button" class="btn-link icon bb-img" style="cursor: pointer;" aria-label="{{$edimg}}" title="{{$edimg}}" data-role="insert-formatting" data-bbcode="img" data-id="{{$id}}">
<i class="fa fa-picture-o"></i> <i class="fa fa-picture-o"></i>
</button> </button>
</li> </li>

View file

@ -79,7 +79,7 @@
<ul id="event-desc-text-edit-bb" class="comment-edit-bb comment-icon-list nav nav-pills hidden-xs pull-left"> <ul id="event-desc-text-edit-bb" class="comment-edit-bb comment-icon-list nav nav-pills hidden-xs pull-left">
{{* commented out because it isn't implemented yet {{* commented out because it isn't implemented yet
<li> <li>
<button type="button" class="btn-link icon" style="cursor: pointer;" title="{{$edimg|escape:'html'}}" data-role="insert-formatting" data-comment=" " data-bbcode="img" data-id="desc"> <button type="button" class="btn-link icon bb-img" style="cursor: pointer;" title="{{$edimg|escape:'html'}}" data-role="insert-formatting" data-comment=" " data-bbcode="img" data-id="desc">
<i class="fa fa-picture-o"></i> <i class="fa fa-picture-o"></i>
</button> </button>
</li> </li>
@ -126,7 +126,7 @@
<ul id="comment-tools-loc" class="comment-edit-bb comment-icon-list nav nav-pills hidden-xs pull-left"> <ul id="comment-tools-loc" class="comment-edit-bb comment-icon-list nav nav-pills hidden-xs pull-left">
{{* commented out because it isn't implemented yet {{* commented out because it isn't implemented yet
<li> <li>
<button type="button" class="btn-link icon" style="cursor: pointer;" title="{{$edimg|escape:'html'}}" data-role="insert-formatting" data-comment=" " data-bbcode="img" data-id="loc"> <button type="button" class="btn-link icon bb-img" style="cursor: pointer;" title="{{$edimg|escape:'html'}}" data-role="insert-formatting" data-comment=" " data-bbcode="img" data-id="loc">
<i class="fa fa-picture-o"></i> <i class="fa fa-picture-o"></i>
</button> </button>
</li> </li>

View file

@ -4,6 +4,8 @@
<input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" value="1" {{if $field.2}}checked="checked"{{/if}} aria-checked="{{if $field.2}}true{{else}}false{{/if}}" aria-describedby="{{$field.0}}_tip" {{if $field.4}}{{$field.4}}{{/if}}> <input type="checkbox" name="{{$field.0}}" id="id_{{$field.0}}" value="1" {{if $field.2}}checked="checked"{{/if}} aria-checked="{{if $field.2}}true{{else}}false{{/if}}" aria-describedby="{{$field.0}}_tip" {{if $field.4}}{{$field.4}}{{/if}}>
<label for="id_{{$field.0}}"> <label for="id_{{$field.0}}">
{{$field.1}} {{$field.1}}
{{if $field.3}}
<span class="help-block" id="{{$field.0}}_tip" role="tooltip">{{$field.3}}</span> <span class="help-block" id="{{$field.0}}_tip" role="tooltip">{{$field.3}}</span>
{{/if}}
</label> </label>
</div> </div>

View file

@ -6,6 +6,8 @@
{{if $field.4}}<span class="required">{{$field.4}}</span>{{/if}} {{if $field.4}}<span class="required">{{$field.4}}</span>{{/if}}
<span class="input-group-addon"><i></i></span> <span class="input-group-addon"><i></i></span>
</div> </div>
<span id="{{$field.0}}_tip" class="help-block" role="tooltip">{{$field.3}}</span> {{if $field.3}}
<span class="help-block" id="{{$field.0}}_tip" role="tooltip">{{$field.3}}</span>
{{/if}}
<div id="end_{{$field.0}}" class="field_end"></div> <div id="end_{{$field.0}}" class="field_end"></div>
</div> </div>

View file

@ -2,5 +2,7 @@
<div class="form-group field custom"> <div class="form-group field custom">
<label for="{{$field.0}}">{{$field.1}}</label> <label for="{{$field.0}}">{{$field.1}}</label>
{{$field.2}} {{$field.2}}
<span class="help-block" id="{{$field.0}}_tip">{{$field.3}}</span> {{if $field.3}}
<span class="help-block" id="{{$field.0}}_tip" role="tooltip">{{$field.3}}</span>
{{/if}}
</div> </div>

Some files were not shown because too many files have changed in this diff Show more