Merge branch 'master' into develop

This commit is contained in:
Hypolite Petovan 2018-03-23 06:15:55 -04:00
commit 27d94023ee
70 changed files with 27138 additions and 26508 deletions

128
CHANGELOG
View File

@ -1,3 +1,129 @@
Version 3.6 (2018-03-23)
Friendica Core:
Updates to the translations (DE, EN_GB, EN_US, ES, FR, IT, ZH_CN) [translation teams]
Updates for the Danish and French regions [Alkarex]
Update for the documentation [andyhee, annando, rabuzarus, ratten, rudlof, silke, tobiasd]
Updates to the themes [Andi-K, annando, fabrixxm, hoergen, rebeka-catalina, rabuzarus]
Enhancements to the ARIA support in frio [rabuzarus]
Enhancements to the DB handling and structure [annando]
Enhancements to the API [annando, fabrixxm, MrPetovan, rudlof]
Enhancements to the support of Open Graph with images [hoergen]
Enhancements to the Diaspora federation (participation signal, relay of dislikes, basic forum support for D*, Birthdays) [annando]
Enhancements to the OStatus federation [annando]
Enhancements to the handling of feed contacts [MrPetovan]
Enhancements to the display of threaded discussions (optional) [MrPetovan]
Enhancements to the display of events [hoergen]
Enhancements to the ACL dialog (selection of forums) [rabuzarus]
Enhancements to the handling of new connections [annando]
Enhancements to the vitality check of contacts [annando]
Enhancements to the daemon script [annando]
Enhancements to the federation stats [annando, tobiasd]
Enhancements to the interaction with public posts [annando]
Enhancements to the structure of the admin panel [tobiasd]
Enhancements to the community page [annando]
Enhancements to the delegation of accounts [annando, MrPetovan]
Enhancements to the user import and server relocation functionality [annando]
Enhancements to the menu layout in the admin panel [tobiasd]
Enhancements to the extraction of strings to be translated [fabrixxm, MrPetovan]
Enhancements to the installation wizard [annando, tobias]
Enhancements to the events [annando, hoergen, MrPetovan, rabuzarus]
Enhancements to the handling of email contacts [annando]
Enhancements to the Vagrant configuration of the development VM [tobias]
Enhancements to the probing of pump.io profiles [annando]
Enhancements to the handling of BBCode tags [MrPetovan]
Enhancements to the OEmbed handling [MrPetovan]
Fixed a bug that triggered the display of activities on the cummunity page [annando]
Fixed a bug with personal notes [annando]
Fixed a display issue of long postings when using the showmore option [annando]
Fixed a bug that caused Twidere to crash on reload [annando]
Fixed a bug in the exported data to the-federation.info [annando]
Fixed a bug in URL completion for feed fragments [annando]
Fixed a bug in the notification system about new registrations [annando]
Fixed the display of dislikes [annando]
Fixed the display of orphans childs in threads [MrPetovan]
Fixed some SQL problems [annando]
Fixed the CLI config script [tobiasd]
Fixed the forum selection on the network display [annando]
Fixed a bug during the import of accounts [annando]
Fixed a problem with UTF8 encoding during account export [annando]
Fixed a problem with archiving "self" contacts [annando]
Fixes to file permissions lintian reported [tobiasd]
Fixed a session problem leading to double login problem [MrPetovan]
Fixed a bug that caused code blocks on Diaspora being displayed wrongly [MrPetovan]
Fixed a bug that suggested it was possible to use some bridges without an account on the other side [annando]
Fixed the situation that an OStatus activity was triggered when publishing a image without sending out a posting for it [annando]
Fixed some issues with the display of exported events on GNU social and diaspora [annando]
Fixed the issue that Atom feeds of forums had no postings listed [annando]
Fixed a problem with the expiration of accounts [annando]
Added Atom feed for conversations [annando]
Added the possibility to address forums with !forumname [annando]
Added option to compare version against upstream version [tobiasd]
Added an optional hint that a global community page is global [tobiasd]
Added an option to always display the preview image in shared articles even if larger ones exist [annando]
Added CLI script to silence accounts on the community page [tobiasd]
Added CLI script to block postings to a node from accounts [tobiasd]
Added account block interface to the admin panel [MrPetovan]
Added browser bookmarklet code snippet [hoergen]
Added an additional feature to display a tag cloud on the profile page [rabuzarus]
Added retrieval of Mastodon server statistics [annando]
Added Atom feed that only contains top level postings of a user [annando]
Added tag following via saved search for #hashtag [annando]
Added PHP version information to the admin panel [MrPetovan]
Added the possibility to change relationships between Friendica contacts [annando]
Added the membersince functionality from the addon to the core [rabuzarus]
Added support of nodeinfo 2.0 [annando]
Removed the long deprecated internal templating engine [annando]
Removed the obsolete mysql support, you have to use MySQLI or PDO [annando]
Removed the unused mood module [annando]
Removed connect link from side panel when it should not be there [annando]
Removed very old updating routines [annando]
Dependencies are now (mostly) handled by composer [MrPetovan, zeroadam]
General code refactoring and beautification work [annando, MrPetovan, tobiasd, zeroadam]
ejabberd logs are now handled by syslog [annando]
Moved the poller script to the "scripts" directory and renamed it to worker [annando]
Threaded display of conversations is now always enabled [annando]
Images send to public forums are now always public as well [annando]
The DB cleanup option now includes the conversation table [annando]
Hash tags now always search locally [annando]
Consistent naming of addons (instead of plugins and addons) [zeroadam]
Community page is split between local and global and always visible for local users [annando]
Updated the credits to include new contributors [tobiasd]
Friendica Addons:
Updates to the translations (DE, EN_GB, ES, FR, IT, NL, ZH_CN) [translation teams]
all bridges don't relay postings anymore that are posted to a public forum [annando]
DAV addon marked unsupported [tobiasd]
Current Weather: fixing a problem with the weathermap link [zeroadam]
NSFW added config examples, reworked the description, now ignores the CW from Mastodon [andyhee, annando, rebeka-catalina]
Twitter support 280 chars limit [annando]
OpenWeatherMap fix broken map link [zeroadam]
CommunityHome added settings to admin panel, removed active users feature [annando, fabrixxm]
General code refactoring and beautification work [annando, MrPetovan, tobiasd, zeroadam]
Public Server reworked [annando]
pageheader settings beautifications [tobiasd]
mailstream settings beautifications [tobiasd]
Membersince is now part of the core [rabuzarus]
Forum posts are not transmitted over the connectors anymore [annando]
Friendica Dir:
Fixed a problem with the maintenance cron [MrPetovan]
Fixed a problem with the location widget [MrPetovan]
Work on the UI [MrPetovan]
Closed Issues:
929, 1050, 1056, 1125, 1215, 1251, 1289, 1312, 1429, 1488, 1540,
1610, 1858, 2786, 2845, 3020, 3039, 3337, 3379, 3394, 3396, 3566,
3583, 3661, 3671, 3680, 3801, 3822, 3824, 3828, 3839, 3855, 3857,
3860, 3863, 3867, 3905, 3911, 3916, 3942, 3946, 3999, 4013, 4020,
4023, 4041, 4042, 4061, 4069, 4070, 4071, 4075, 4078, 4082, 4094,
4105, 4115, 4116, 4137, 4141, 4144, 4150, 4155, 4161, 4163, 4173,
4184, 4199, 4200, 4207, 4227, 4228, 4236, 4251, 4272, 4273, 4278,
4279, 4281, 4290, 4294, 4295, 4296, 4304, 4306, 4319, 4348, 4362,
4368, 4369, 4377, 4390, 4395, 4396, 4409, 4412, 4426, 4431, 4445,
4450, 4452, 4458, 4463, 4481, 4482, 4495, 4497, 4498, 4508, 4518,
4520, 4522, 4535, 4543, 4550, 4555, 4556, 4571, 4575, 4610, 4611,
4620
Version 3.5.4 (2017-10-16) Version 3.5.4 (2017-10-16)
Friendica Core: Friendica Core:
Updates to the translations (DE) [translation teams] Updates to the translations (DE) [translation teams]
@ -90,7 +216,7 @@ Version 3.5.2 (2017-06-06)
Updates to the translations (DE, EN-GB, EN-US, ES, IT, PT-BR, RU) [translation teams] Updates to the translations (DE, EN-GB, EN-US, ES, IT, PT-BR, RU) [translation teams]
Updates to the documentation [annando, beardyunixer, rabuzarus, tobiasd] Updates to the documentation [annando, beardyunixer, rabuzarus, tobiasd]
Updated the nginx example configuration [beardyunixer] Updated the nginx example configuration [beardyunixer]
Code revision and refactoring [annando, hypolite, Quix0r, rebeka-catalina] Code revision and refactoring [annando, MrPetovan, Quix0r, rebeka-catalina]
Background process is now done by the new worker process [annando] Background process is now done by the new worker process [annando]
Added support of Composer for dependencies [Hypolite] Added support of Composer for dependencies [Hypolite]
Added support of Web app manifests [Rudloff] Added support of Web app manifests [Rudloff]

View File

@ -37,10 +37,12 @@ local .htaccess file
- PHP *command line* access with register_argc_argv set to true in the - PHP *command line* access with register_argc_argv set to true in the
php.ini file [or see 'poormancron' in section 8] php.ini file [or see 'poormancron' in section 8]
- curl, gd (with at least jpeg support), mysql, mbstring, xml and openssl extensions - curl, gd (with at least jpeg support), mysql, mbstring, xml, zip and openssl extensions
- some form of email server or email gateway such that PHP mail() works - some form of email server or email gateway such that PHP mail() works
- The POSIX module of PHP needs to be activated (e.g. RHEL, CentOS have disabled it)
- Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.) - Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.)
- ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks - ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks
@ -129,24 +131,16 @@ assistance. Friendica will not work correctly if you cannot perform this step.
You should also be sure that $a->config['php_path'] is set correctly, it should You should also be sure that $a->config['php_path'] is set correctly, it should
look like (changing it to the correct PHP location) look like (changing it to the correct PHP location)
$a->config['php_path'] = '/usr/local/php53/bin/php' $a->config['php_path'] = '/usr/local/php56/bin/php'
Alternative: You may be able to use the 'poormancron' addon to perform this
step if you are using a recent Friendica release. 'poormancron' may result in
perfomance and memory issues and is only suitable for small sites with one or
two users and a handful of contacts. To do this, edit the file
".htconfig.php" and look for a line describing your addons. On a fresh
installation, it will look like
$a->config['system']['addon'] = 'js_upload'; Alternative: If you cannot use a cron job as described above, you can use
the frontend worker and an external cron service to trigger the execution
of the worker script. You can enable the frontend worker after the installation
from the admin panel of your node and call
This indicates the "js_upload" addon module is enabled. You may add additional https://example.com/worker
addons using this same line in the configuration file. Change it to
read
$a->config['system']['addon'] = 'js_upload,poormancron'; with the service of your choice.
and save your changes.
9. (Recommended) Set up a backup plan 9. (Recommended) Set up a backup plan

View File

@ -1 +1 @@
3.6-dev 3.6

View File

@ -39,7 +39,7 @@ Config::load();
check_db(true); check_db(true);
// Quit when in maintenance // Quit when in maintenance
if (Config::get('system', 'maintenance', true)) { if (Config::get('system', 'maintenance', false, true)) {
return; return;
} }

View File

@ -38,8 +38,8 @@ use Friendica\Util\DateTimeFormat;
require_once 'include/text.php'; require_once 'include/text.php';
define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_PLATFORM', 'Friendica');
define('FRIENDICA_CODENAME', 'Asparagus'); define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily');
define('FRIENDICA_VERSION', '3.6-dev'); define('FRIENDICA_VERSION', '3.6');
define('DFRN_PROTOCOL_VERSION', '2.23'); define('DFRN_PROTOCOL_VERSION', '2.23');
define('DB_UPDATE_VERSION', 1256); define('DB_UPDATE_VERSION', 1256);
define('NEW_UPDATE_ROUTINE_VERSION', 1170); define('NEW_UPDATE_ROUTINE_VERSION', 1170);

View File

@ -1,5 +1,5 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 3.6-dev (Asparagus) -- Friendica 2018-05-dev (Tazmans Flax-lilly)
-- DB_UPDATE_VERSION 1256 -- DB_UPDATE_VERSION 1256
-- ------------------------------------------ -- ------------------------------------------
@ -466,6 +466,7 @@ CREATE TABLE IF NOT EXISTS `item` (
`author-link` varchar(255) NOT NULL DEFAULT '' COMMENT '', `author-link` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`author-avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '', `author-avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`title` varchar(255) NOT NULL DEFAULT '' COMMENT '', `title` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`content-warning` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`body` mediumtext COMMENT '', `body` mediumtext COMMENT '',
`app` varchar(255) NOT NULL DEFAULT '' COMMENT '', `app` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`verb` varchar(100) NOT NULL DEFAULT '' COMMENT '', `verb` varchar(100) NOT NULL DEFAULT '' COMMENT '',

View File

@ -16,7 +16,6 @@ Whether you feel like an expert or like a newbie - join us with your ideas!
The discussion of Friendica development takes place in the following Friendica forums: The discussion of Friendica development takes place in the following Friendica forums:
* The main [forum for Friendica development](https://forum.friendi.ca/profile/developers) * The main [forum for Friendica development](https://forum.friendi.ca/profile/developers)
* The [forum for Friendica theme development](https://friendica.eu/profile/ftdevs)
## Help other users ## Help other users
@ -141,7 +140,7 @@ If you want to get involved here:
* Look at the first steps that were made (e.g. the clean theme). * Look at the first steps that were made (e.g. the clean theme).
Ask us to find out whom to talk to about their experiences. Ask us to find out whom to talk to about their experiences.
* Talk to design people if you know any. * Talk to design people if you know any.
* Let us know about your plans [in the dev forum](https://forum.friendi.ca/profile/developers) or the [theme developer forum](https://friendica.eu/profile/ftdevs). * Let us know about your plans [in the dev forum](https://forum.friendi.ca/profile/developers)
Do not worry about cross-posting. Do not worry about cross-posting.
### Client software ### Client software

View File

@ -70,7 +70,8 @@ See Wikipedia for more of them ([video](http://en.wikipedia.org/wiki/HTML5_video
<a name="avatars"></a> <a name="avatars"></a>
### Is it possible to have different avatars per profile? ### Is it possible to have different avatars per profile?
Yes. On your Edit/Manage Profiles page, you will find a "change profile photo" link. Yes.
On your Edit/Manage Profiles page, you will find a "change profile photo" link.
Clicking this will take you to a page where you can upload a photograph and select which profile it will be associated with. Clicking this will take you to a page where you can upload a photograph and select which profile it will be associated with.
To avoid privacy leakage, we only display the photograph associated with your default profile as the avatar in your posts. To avoid privacy leakage, we only display the photograph associated with your default profile as the avatar in your posts.
@ -111,15 +112,9 @@ After that, your account is deleted.
<a name="hashtag"></a> <a name="hashtag"></a>
### Can I follow a hashtag? ### Can I follow a hashtag?
No. The act of 'following' a hashtags is an interesting technology, but presents a few issues. Yes. Simply add the hash tag to your saved searches.
The posts will appear on your network page.
1. Posts would have to be copied to all sites on the network that are "listening" to that hashtag. This would increase the storage demands to the detriment of small sites. It would make the use of shared hosting practically impossible. For technical reasons, your answers to such posts won't appear on the "personal" tab in the network page and the whole thread isn't accessible via the API.
2. Making spam easy (tag spam is a serious issue on Twitter for instance)
3. It creates a natural bias towards large sites which hold more tagged content - if your network uses tagging instead of other conversation federation mechanisms such as groups/forums.
Instead, we offer other mechanisms for wide-area conversations while retaining a 'level playing ground' for both large and small sites, such as forums and community pages and shared tags.
<a name="rss"></a> <a name="rss"></a>
### How to create a RSS feed of the stream? ### How to create a RSS feed of the stream?
@ -171,10 +166,15 @@ Depending on the features of the client you might encounter some glitches in usa
### Where I can find help? ### Where I can find help?
If you have problems with your Friendica page, you can ask the community at the [Friendica Support Group](https://forum.friendi.ca/profile/helpers). If you have problems with your Friendica page, you can ask the community at the [Friendica Support Group](https://forum.friendi.ca/profile/helpers).
If you can't use your default profile you can use an account at a public site [list](https://dir.friendica.social/servers) or you can use the Librelist mailing list. If you can't use your default profile you can use an account at a public site [list](https://dir.friendica.social/servers).
If you want to use the mailing list, please just send a mail to friendica AT librelist DOT com.
If you are a theme developer, you will find help at this forum: [Friendica Theme Developers](https://friendica.eu/profile/ftdevs). In case you do not want to set up another account on Friendica, you can also use one of the following channels to reach out for help:
* [Friendica Support Forum](https://forum.friendi.ca/~helpers)
* [Mailing List Archive](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) you can subscribe to the list by sending an email to ``support-request(at)friendi.ca?subject=subscribe``
* XMPP/Jabber MUC: support(at)forum.friendi.ca
* IRC: #friendica at irc.freenode.net
* Matrix: #friendi.ca or #friendica at matrix.org
Admin Admin
-------- --------

View File

@ -28,7 +28,8 @@ Requirements
* Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file * Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file
* PHP 5.6+ (PHP 7 is recommended for performance) * PHP 5.6+ (PHP 7 is recommended for performance)
* PHP *command line* access with register_argc_argv set to true in the php.ini file * PHP *command line* access with register_argc_argv set to true in the php.ini file
* Curl, GD, PDO, MySQLi, hash, xml and OpenSSL extensions * Curl, GD, PDO, MySQLi, hash, xml, zip and OpenSSL extensions
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
* some form of email server or email gateway such that PHP mail() works * some form of email server or email gateway such that PHP mail() works
* Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.) * Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.)
* the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.) * the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.)
@ -38,7 +39,7 @@ Requirements
Installation procedure Installation procedure
--- ---
###Get Friendica ### Get Friendica
Unpack the Friendica files into the root of your web server document area. Unpack the Friendica files into the root of your web server document area.
If you are able to do so, we recommend using git to clone the source repository rather than to use a packaged tar or zip file. If you are able to do so, we recommend using git to clone the source repository rather than to use a packaged tar or zip file.
@ -64,7 +65,7 @@ Clone the addon repository (separately):
If you copy the directory tree to your webserver, make sure that you also copy .htaccess - as "dot" files are often hidden and aren't normally copied. If you copy the directory tree to your webserver, make sure that you also copy .htaccess - as "dot" files are often hidden and aren't normally copied.
###Create a database ### Create a database
Create an empty database and note the access details (hostname, username, password, database name). Create an empty database and note the access details (hostname, username, password, database name).
@ -79,7 +80,7 @@ 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.
###Run the 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.
@ -96,7 +97,7 @@ 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.
###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.
Example: Example:
@ -118,7 +119,8 @@ If it is not possible to set up a cron job then please activate the "frontend wo
Once you have installed Friendica and created an admin account as part of the process, you can access the admin panel of your installation and do most of the server wide configuration from there Once you have installed Friendica and created an admin account as part of the process, you can access the admin panel of your installation and do most of the server wide configuration from there
###Set up a backup plan ### Set up a backup plan
Bad things will happen. Bad things will happen.
Let there be a hardware failure, a corrupted database or whatever you can think of. Let there be a hardware failure, a corrupted database or whatever you can think of.
So once the installation of your Friendica node is done, you should make yourself a backup plan. So once the installation of your Friendica node is done, you should make yourself a backup plan.

View File

@ -57,6 +57,21 @@ The `vier` theme for instance is mobile friendly.
### Registration ### Registration
#### Register policy
With this drop down selector you can set the nodes registration policy.
You can chose between the following modes:
* **open**: Everybody can register a new account and start using it right away.
* **requires approval**: Everybody can register a new account, but the admin has to approve it before it can be used.
* **closed**: No new registrations are possible.
##### Invitation based registry
Additionally to the setting in the admin panel, you can devide if registrations are only possible using an invitation code or not.
To enable invitation based registration, you have to set the `invitation_only` setting in the [.htconfig.php](/help/htconfig) file.
If you want to use this method, the registration policy has to be set to either *open* or *requires approval*.
#### Check Full Names #### Check Full Names
You may find a lot of spammers trying to register on your site. You may find a lot of spammers trying to register on your site.
@ -111,6 +126,14 @@ Unauthorised persons will also not be able to request friendship with site membe
Default is false. Default is false.
Available in version 2.2 or greater. Available in version 2.2 or greater.
#### Community pages for Visitors
The community pages show all public postings, separated by their origin being local or the entire network.
With this setting you can select which community pages will be shown to visitors of your Friendica node.
Your local users will always have access to both pages.
**Note**: Several settings, like users hiding their contacts from the public will prevent the postings to show up on the global community page.
#### Allowed Friend Domains #### Allowed Friend Domains
Comma separated list of domains which are allowed to establish friendships with this site. Comma separated list of domains which are allowed to establish friendships with this site.

View File

@ -10,7 +10,7 @@ Nutzer
* **[Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben?](help/FAQ#avatars)** * **[Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben?](help/FAQ#avatars)**
* **[Was ist der Unterschied zwischen blockierten|ignorierten|archivierten|versteckten Kontakten?](help/FAQ#contacts)** * **[Was ist der Unterschied zwischen blockierten|ignorierten|archivierten|versteckten Kontakten?](help/FAQ#contacts)**
* **[Was passiert, wenn ein Account gelöscht ist? Ist dieser richtig gelöscht?](help/FAQ#removed)** * **[Was passiert, wenn ein Account gelöscht ist? Ist dieser richtig gelöscht?](help/FAQ#removed)**
* **[Kann ich einem hashtag folgen?](help/FAQ#hashtag)** * **[Kann ich einem Hashtag folgen?](help/FAQ#hashtag)**
* **[Wie kann ich einen RSS-Feed meiner Netzwerkseite (Stream) erstellen?](help/FAQ#rss)** * **[Wie kann ich einen RSS-Feed meiner Netzwerkseite (Stream) erstellen?](help/FAQ#rss)**
* **[Gibt es Clients für Friendica?](help/FAQ#clients)** * **[Gibt es Clients für Friendica?](help/FAQ#clients)**
* **[Wo finde ich Hilfe?](help/FAQ#help)** * **[Wo finde ich Hilfe?](help/FAQ#help)**
@ -85,9 +85,9 @@ Zum Konvertieren von Videos in das lizenfreie Videoformat WebM gibt es unter Win
### Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben? ### Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben?
Ja. Ja.
Auf Deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst Du zunächst das gewünschte Profil aus. Auf Deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst Du zunächst das gewünschte Profil aus.
Anschließend siehst Du eine Seite mit allen Infos zu diesem Profil. Anschließend siehst Du eine Seite mit allen Infos zu diesem Profil.
Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von Deinem PC hoch. Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von Deinem PC hoch.
Um Deine privaten Daten zu schützen, wird in Beiträgen nur das Bild aus Deinem öffentlichen Profil angezeigt. Um Deine privaten Daten zu schützen, wird in Beiträgen nur das Bild aus Deinem öffentlichen Profil angezeigt.
<a name="contacts"></a> <a name="contacts"></a>
@ -122,19 +122,11 @@ Dieses Vorgehen setzt voraus, dass Dein Profil für 24 Stunden weiterhin "teilwe
Wir können also Dein Profil blockieren und es so erscheinen lassen, als wären alle Daten sofort gelöscht, allerdings warten wir 24 Stunden (bzw. bis alle Deine Kontakte informiert wurden), bevor wir die Daten auch physikalisch löschen. Wir können also Dein Profil blockieren und es so erscheinen lassen, als wären alle Daten sofort gelöscht, allerdings warten wir 24 Stunden (bzw. bis alle Deine Kontakte informiert wurden), bevor wir die Daten auch physikalisch löschen.
<a name="hashtag"></a> <a name="hashtag"></a>
### Kann ich einem hashtag folgen? ### Kann ich einem Hashtag folgen?
Nein. Ja.
Die Möglichkeit, einem hashtag zu folgen, ist eine interessante Technik, führt aber zu einigen Schwierigkeiten. Füge die Tags zu Deinen gespeicherten Suchen hinzu, sie werden automatisch auf der Netzwerk-Seite auftauchen.
Bitte beachte, dass Deine Antworten auf solche Posts aus technischen Gründen nicht unter dem "Persönlich"-Reiter auf der Netzwerk-Seite und der gesamte Thread nicht per API zu sehen sind.
1.) Alle Beiträge, die diesen tag nutzen, müssten zu allen Seiten im Netzwerk kopiert werden. Das erhöht den Speicherbedarf und beeinträchtigt kleine Seiten. Die Nutzung von geteilten Hosting-Angeboten (Shared Hosting) wäre praktisch unmöglich.
2.) Die Verbreitung von Spam wäre vereinfacht (tag-Spam ist z.B. bei Twitter ein schwerwiegendes Problem)
3.) Der wichtigste Grund der gegen diese Technik spricht ist, dass sie eine natürliche Ausrichtung auf größere Seiten mit mehr getaggten Inhalten zur Folge hat. Dies kann z.B. aufkommen, wenn Dein Netzwerk tags anstelle von anderen Kommunikationsmitteln wie Gruppen oder Foren nutzt.
Stattdessen bieten wir andere Mechanismen, um globale Unterhaltungen zu erreichen, dabei aber eine angemesse Basis für kleine und große Seiten zu bieten.
Hierzu gehören Foren, Gruppen und geteilte tags.
<a name="rss"></a> <a name="rss"></a>
### Wie kann ich einen RSS-Feed meiner Netzwerkseite (Stream) erstellen? ### Wie kann ich einen RSS-Feed meiner Netzwerkseite (Stream) erstellen?
@ -188,11 +180,16 @@ Hier ist eine Liste von Clients bei denen dies möglich ist, bzw. die speziell f
<a name="help"></a> <a name="help"></a>
### Wo finde ich Hilfe? ### Wo finde ich Hilfe?
Wenn Du Probleme mit Deiner Friendica-Seite hast, dann kannst Du die Community in der [Friendica-Support-Gruppe](https://forum.friendi.ca/profile/helpers) oder im [deutschen Friendica-Support-Forum](http://toktan.org/profile/wiki) fragen oder Dir das [deutsche Wiki](http://wiki.toktan.org/doku.php) anschauen. Wenn Du Probleme mit Deiner Friendica-Seite hast, dann kannst Du die Community in der [Friendica-Support-Gruppe](https://forum.friendi.ca/profile/helpers) oder im [deutschen Friendica-Support-Forum](http://toktan.org/profile/wiki) fragen oder Dir das [deutsche Wiki](http://wiki.toktan.org/doku.php) anschauen.
Wenn Du Deinen Account nicht nutzen kannst, kannst Du entweder einen [Testaccount](https://tryfriendica.de) bzw. einen Account auf einer öffentlichen Seite ([Liste](https://dir.friendica.social/servers)) nutzen, oder Du wählst die Librelist-mailing-Liste. Wenn Du Deinen Account nicht nutzen kannst, kannst Du entweder einen [Testaccount](https://tryfriendica.de) bzw. einen Account auf einer öffentlichen Seite ([Liste](https://dir.friendica.social/servers)) nutzen.
Wenn Du die Mailing-Liste nutzen willst, schicke eine Mail an friendica AT librelist PUNKT com.
Wenn Du ein Theme-Entwickler bist, wirst Du in diesem Forum Hilfe finden: [Friendica Theme Developers](https://friendica.eu/profile/ftdevs). Wenn du dir keinen weiteren Friendica Account einrichten willst, kannst du auch gerne über einen der folgenden alternativen Kanäle Hilfe suchen:
* [Friendica Support Forum](https://forum.friendi.ca/~helpers)
* [Mailing List Archive](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) you can subscribe to the list by sending an email to ``support-request(at)friendi.ca?subject=subscribe``
* XMPP/Jabber MUC: support(at)forum.friendi.ca
* IRC: #friendica at irc.freenode.net
* Matrix: #friendi.ca or #friendica at matrix.org.
Admin Admin
-------- --------

View File

@ -24,8 +24,9 @@ Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben.
- Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst - 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 5.6+. Je neuer, desto besser.
- PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei - PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
- Curl, GD, PDO, MySQLi, xml und OpenSSL-Erweiterung - Curl, GD, PDO, MySQLi, xml, zip und OpenSSL-Erweiterung
- etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail() - 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+ - 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] - 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. - 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.

View File

@ -54,6 +54,20 @@ Das `vier` Theme z.B. unterstützt kleine Anzeigen und benötigt kein zusätzlic
### Registrierung ### Registrierung
#### Registrierungsmethode
Diese Einstellung regelt die Art der Registrierung.
Dabei kannst du zwischen den folgenden Optionen wählen:
* **Offen**: Jeder kann ein neues Nutzerkonto anlegen und es sofort benutzen.
* **Bedarf der Zustimmung**: Jeder kann ein Nutzerkonto anlegen. Dieses muss allerdings durch den Admin freigeschaltet werden, bevor es verwendet werden kann.
* **Geschlossen**: Es können keine weiteren Nutzerkonten angelegt werden.
##### Einladungen
Zusätzlich zu den oben genannten Möglichkeiten, kann die Registrierung eines neuen Nutzerkontos an eine Einladung durch einen bestehenden Nutzer gekoppelt werden.
Hierzu muss in der [.htconfig.php](/help/htconfig) Datei die Option `invitation_only` aktiviert und als Registrierungsmethode entweder *Offen* oder *Bedarf der Zustimmung* gewählt werden.
#### Namen auf Vollständigkeit überprüfen #### Namen auf Vollständigkeit überprüfen
Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren. Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren.
@ -108,6 +122,15 @@ Unautorisierte Personen haben ebenfalls nicht die Möglichkeit, Freundschaftsanf
Die Standardeinstellung ist deaktiviert. Die Standardeinstellung ist deaktiviert.
Verfügbar in Version 2.2 und höher. Verfügbar in Version 2.2 und höher.
#### Für Besucher verfügbare Gemeinschaftsseiten
Die Gemeinschaftsseiten zeigen all öffentlichen Beiträge.
Es gibt zwei Gemeinschaftsseiten, eine lokale auf der die Beiträge der Nutzer des Knotens gesammelt werden und eine globale auf der alle bekannten Beiträge aus dem gesamten Netzwerk erscheinen.
Mit dieser Einstellung kann geregelt werden, welche dieser beiden Seiten Besucher aufrufen können.
Angemeldete Nutzer des Knotens können grundsätzlich beide Seiten verwenden.
**Hinweis**: Einige Einstellungen, wie z.B. das Verbergen von Kontakten auf der Profilseite, können die Sichtbarkeit der Beiträge auf der Gemeinschaftsseiten beeinflussen.
#### Erlaubte Domains für Kontakte #### Erlaubte Domains für Kontakte
Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen. Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen.

View File

@ -49,6 +49,7 @@ Example: To set the automatic database cleanup process add this line to your .ht
* **ignore_cache** (Boolean) - For development only. Disables the item cache. * **ignore_cache** (Boolean) - For development only. Disables the item cache.
* **instances_social_key** - Key to the API of https://instances.social which retrieves data about mastodon servers. See https://instances.social/api/token to get an API key. * **instances_social_key** - Key to the API of https://instances.social which retrieves data about mastodon servers. See https://instances.social/api/token to get an API key.
* **ipv4_resolve** (Boolean) - Resolve IPV4 addresses only. Don't resolve to IPV6. Default value is false. * **ipv4_resolve** (Boolean) - Resolve IPV4 addresses only. Don't resolve to IPV6. Default value is false.
* **invitation_only** (Boolean) - If set true registration is only possible after a current member of the node has send an invitation. Default is false.
* **like_no_comment** (Boolean) - Don't update the "commented" value of an item when it is liked. * **like_no_comment** (Boolean) - Don't update the "commented" value of an item when it is liked.
* **local_block** (Boolean) - Used in conjunction with "block_public". * **local_block** (Boolean) - Used in conjunction with "block_public".
* **local_search** (Boolean) - Blocks search for users who are not logged in to prevent crawlers from blocking your system. * **local_search** (Boolean) - Blocks search for users who are not logged in to prevent crawlers from blocking your system.

View File

@ -6,7 +6,6 @@ To change the look of friendica you have to touch the themes.
The current default theme is [Vier](https://github.com/friendica/friendica/tree/master/view/theme/vier) but there are numerous others. The current default theme is [Vier](https://github.com/friendica/friendica/tree/master/view/theme/vier) but there are numerous others.
Have a look at [friendica-themes.com](http://friendica-themes.com) for an overview of the existing themes. Have a look at [friendica-themes.com](http://friendica-themes.com) for an overview of the existing themes.
In case none of them suits your needs, there are several ways to change a theme. In case none of them suits your needs, there are several ways to change a theme.
If you need help theming, there is a forum @[ftdevs@friendica.eu](https://friendica.eu/profile/ftdevs) where you can ask theme specific questions and present your themes.
So, how to work on the UI of friendica. So, how to work on the UI of friendica.
@ -206,7 +205,7 @@ Basically what you have to do is identify which template you have to change so i
Adopt the CSS of the theme accordingly. Adopt the CSS of the theme accordingly.
And iterate the process until you have the theme the way you want it. And iterate the process until you have the theme the way you want it.
*Use the source Luke.* and don't hesitate to ask in @[ftdevs](https://friendica.eu/profile/ftdevs) or @[helpers](https://forum.friendi.ca/profile/helpers). *Use the source Luke.* and don't hesitate to ask in @[developers](https://forum.friendi.ca/profile/developers) or @[helpers](https://forum.friendi.ca/profile/helpers).
## Special Files ## Special Files

View File

@ -5931,10 +5931,12 @@ function api_saved_searches_list($type)
$result = []; $result = [];
while ($term = $terms->fetch()) { while ($term = $terms->fetch()) {
$result[] = [ $result[] = [
'name' => $term['term'], 'created_at' => api_date(time()),
'query' => $term['term'], 'id' => intval($term['id']),
'id_str' => $term['id'], 'id_str' => $term['id'],
'id' => intval($term['id']) 'name' => $term['term'],
'position' => null,
'query' => $term['term']
]; ];
} }

View File

@ -443,7 +443,7 @@ These Fields are not added below (yet). They are here to for bug search.
return "`item`.`author-id`, `item`.`author-link`, `item`.`author-name`, `item`.`author-avatar`, return "`item`.`author-id`, `item`.`author-link`, `item`.`author-name`, `item`.`author-avatar`,
`item`.`owner-id`, `item`.`owner-link`, `item`.`owner-name`, `item`.`owner-avatar`, `item`.`owner-id`, `item`.`owner-link`, `item`.`owner-name`, `item`.`owner-avatar`,
`item`.`contact-id`, `item`.`uid`, `item`.`id`, `item`.`parent`, `item`.`contact-id`, `item`.`uid`, `item`.`id`, `item`.`parent`,
`item`.`uri`, `item`.`thr-parent`, `item`.`parent-uri`, `item`.`uri`, `item`.`thr-parent`, `item`.`parent-uri`, `item`.`content-warning`,
`item`.`commented`, `item`.`created`, `item`.`edited`, `item`.`received`, `item`.`commented`, `item`.`created`, `item`.`edited`, `item`.`received`,
`item`.`verb`, `item`.`object-type`, `item`.`postopts`, `item`.`plink`, `item`.`verb`, `item`.`object-type`, `item`.`postopts`, `item`.`plink`,
`item`.`guid`, `item`.`wall`, `item`.`private`, `item`.`starred`, `item`.`guid`, `item`.`wall`, `item`.`private`, `item`.`starred`,
@ -1245,7 +1245,7 @@ function format_like($cnt, array $arr, $type, $id) {
break; break;
case 'attendmaybe': case 'attendmaybe':
$phrase = L10n::t('<span %1$s>%2$d people</span> attend maybe', $spanatts, $cnt); $phrase = L10n::t('<span %1$s>%2$d people</span> attend maybe', $spanatts, $cnt);
$explikers = L10n::t('%s anttend maybe.', $likers); $explikers = L10n::t('%s attend maybe.', $likers);
break; break;
} }
@ -1643,7 +1643,7 @@ function get_responses($conv_responses, $response_verbs, $ob, $item) {
foreach ($response_verbs as $v) { foreach ($response_verbs as $v) {
$ret[$v] = []; $ret[$v] = [];
$ret[$v]['count'] = defaults($conv_responses[$v], $item['uri'], ''); $ret[$v]['count'] = defaults($conv_responses[$v], $item['uri'], '');
$ret[$v]['list'] = defaults($conv_responses[$v], $item['uri'] . '-l', ''); $ret[$v]['list'] = defaults($conv_responses[$v], $item['uri'] . '-l', []);
$ret[$v]['self'] = defaults($conv_responses[$v], $item['uri'] . '-self', '0'); $ret[$v]['self'] = defaults($conv_responses[$v], $item['uri'] . '-self', '0');
if (count($ret[$v]['list']) > MAX_LIKERS) { if (count($ret[$v]['list']) > MAX_LIKERS) {
$ret[$v]['list_part'] = array_slice($ret[$v]['list'], 0, MAX_LIKERS); $ret[$v]['list_part'] = array_slice($ret[$v]['list'], 0, MAX_LIKERS);

View File

@ -16,10 +16,10 @@ use Friendica\Util\Emailer;
* @brief Creates a notification entry and possibly sends a mail * @brief Creates a notification entry and possibly sends a mail
* *
* @param array $params Array with the elements: * @param array $params Array with the elements:
uid, item, parent, type, otype, verb, event, * uid, item, parent, type, otype, verb, event,
link, subject, body, to_name, to_email, source_name, * link, subject, body, to_name, to_email, source_name,
source_link, activity, preamble, notify_flags, * source_link, activity, preamble, notify_flags,
language, show_in_notification_page * language, show_in_notification_page
*/ */
function notification($params) function notification($params)
{ {
@ -368,7 +368,7 @@ function notification($params)
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]' '[url='.$params['source_link'].']'.$params['source_name'].'[/url]'
); );
$body = L10n::t('Full Name: %1$s\nSite Location: %2$s\nLogin Name: %3$s ' . "\x28" . '%4$s' . "\x28", $body = L10n::t('Full Name: %1$s\nSite Location: %2$s\nLogin Name: %3$s ' . "\x28" . '%4$s' . "\x29",
$params['source_name'], $params['source_name'],
$siteurl, $params['source_mail'], $siteurl, $params['source_mail'],
$params['source_nick'] $params['source_nick']

View File

@ -38,9 +38,9 @@ function format_event_html($ev, $simple = false) {
); );
if ($simple) { if ($simple) {
$o = "<h3>" . BBCode::convert($ev['summary']) . "</h3>"; $o = "<h3>" . BBCode::convert($ev['summary'], false, $simple) . "</h3>";
$o .= "<div>" . BBCode::convert($ev['desc']) . "</div>"; $o .= "<p>" . BBCode::convert($ev['desc'], false, $simple) . "</p>";
$o .= "<h4>" . L10n::t('Starts:') . "</h4><p>" . $event_start . "</p>"; $o .= "<h4>" . L10n::t('Starts:') . "</h4><p>" . $event_start . "</p>";
@ -49,7 +49,7 @@ function format_event_html($ev, $simple = false) {
} }
if (strlen($ev['location'])) { if (strlen($ev['location'])) {
$o .= "<h4>" . L10n::t('Location:') . "</h4><p>" . $ev['location'] . "</p>"; $o .= "<h4>" . L10n::t('Location:') . "</h4><p>" . BBCode::convert($ev['location'], false, $simple) . "</p>";
} }
return $o; return $o;
@ -57,7 +57,7 @@ function format_event_html($ev, $simple = false) {
$o = '<div class="vevent">' . "\r\n"; $o = '<div class="vevent">' . "\r\n";
$o .= '<div class="summary event-summary">' . BBCode::convert($ev['summary']) . '</div>' . "\r\n"; $o .= '<div class="summary event-summary">' . BBCode::convert($ev['summary'], false, $simple) . '</div>' . "\r\n";
$o .= '<div class="event-start"><span class="event-label">' . L10n::t('Starts:') . '</span>&nbsp;<span class="dtstart" title="' $o .= '<div class="event-start"><span class="event-label">' . L10n::t('Starts:') . '</span>&nbsp;<span class="dtstart" title="'
. DateTimeFormat::utc($ev['start'], (($ev['adjust']) ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s' )) . DateTimeFormat::utc($ev['start'], (($ev['adjust']) ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s' ))
@ -71,16 +71,16 @@ function format_event_html($ev, $simple = false) {
. '</span></div>' . "\r\n"; . '</span></div>' . "\r\n";
} }
$o .= '<div class="description event-description">' . BBCode::convert($ev['desc']) . '</div>' . "\r\n"; $o .= '<div class="description event-description">' . BBCode::convert($ev['desc'], false, $simple) . '</div>' . "\r\n";
if (strlen($ev['location'])) { if (strlen($ev['location'])) {
$o .= '<div class="event-location"><span class="event-label">' . L10n::t('Location:') . '</span>&nbsp;<span class="location">' $o .= '<div class="event-location"><span class="event-label">' . L10n::t('Location:') . '</span>&nbsp;<span class="location">'
. BBCode::convert($ev['location']) . BBCode::convert($ev['location'], false, $simple)
. '</span></div>' . "\r\n"; . '</span></div>' . "\r\n";
// Include a map of the location if the [map] BBCode is used. // Include a map of the location if the [map] BBCode is used.
if (strpos($ev['location'], "[map") !== false) { if (strpos($ev['location'], "[map") !== false) {
$map = Map::byLocation($ev['location']); $map = Map::byLocation($ev['location'], $simple);
if ($map !== $ev['location']) { if ($map !== $ev['location']) {
$o.= $map; $o.= $map;
} }
@ -241,14 +241,20 @@ function event_store($arr) {
$a = get_app(); $a = get_app();
$arr['created'] = (($arr['created']) ? $arr['created'] : DateTimeFormat::utcNow()); $arr['created'] = (($arr['created']) ? DateTimeFormat::utc($arr['created']) : DateTimeFormat::utcNow());
$arr['edited'] = (($arr['edited']) ? $arr['edited'] : DateTimeFormat::utcNow()); $arr['edited'] = (($arr['edited']) ? DateTimeFormat::utc($arr['edited']) : DateTimeFormat::utcNow());
$arr['start'] = (($arr['start']) ? DateTimeFormat::utc($arr['start']) : NULL_DATE);
$arr['finish'] = (($arr['finish']) ? DateTimeFormat::utc($arr['finish']) : NULL_DATE);
$arr['type'] = (($arr['type']) ? $arr['type'] : 'event' ); $arr['type'] = (($arr['type']) ? $arr['type'] : 'event' );
$arr['cid'] = ((intval($arr['cid'])) ? intval($arr['cid']) : 0); $arr['cid'] = ((intval($arr['cid'])) ? intval($arr['cid']) : 0);
$arr['uri'] = (x($arr, 'uri') ? $arr['uri'] : item_new_uri($a->get_hostname(), $arr['uid'])); $arr['uri'] = (x($arr, 'uri') ? $arr['uri'] : item_new_uri($a->get_hostname(), $arr['uid']));
$arr['private'] = ((x($arr, 'private')) ? intval($arr['private']) : 0); $arr['private'] = ((x($arr, 'private')) ? intval($arr['private']) : 0);
$arr['guid'] = get_guid(32); $arr['guid'] = get_guid(32);
if ($arr['finish'] < NULL_DATE) {
$arr['finish'] = NULL_DATE;
}
if ($arr['cid']) { if ($arr['cid']) {
$c = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1", $c = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
intval($arr['cid']), intval($arr['cid']),

View File

@ -228,7 +228,7 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) {
* *
* @TODO find proper type-hints * @TODO find proper type-hints
*/ */
function consume_feed($xml, $importer, &$contact, &$hub, $datedir = 0, $pass = 0) { function consume_feed($xml, $importer, $contact, &$hub, $datedir = 0, $pass = 0) {
if ($contact['network'] === NETWORK_OSTATUS) { if ($contact['network'] === NETWORK_OSTATUS) {
if ($pass < 2) { if ($pass < 2) {
// Test - remove before flight // Test - remove before flight
@ -290,7 +290,7 @@ function subscribe_to_hub($url, $importer, $contact, $hubmode = 'subscribe') {
return; return;
} }
$push_url = Config::get('system','url') . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id']; $push_url = System::baseUrl() . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id'];
// Use a single verify token, even if multiple hubs // Use a single verify token, even if multiple hubs
$verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string()); $verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string());

View File

@ -1062,7 +1062,7 @@ function linkify($s) {
* Load poke verbs * Load poke verbs
* *
* @return array index is present tense verb * @return array index is present tense verb
value is array containing past tense verb, translation of present, translation of past * value is array containing past tense verb, translation of present, translation of past
* @hook poke_verbs pokes array * @hook poke_verbs pokes array
*/ */
function get_poke_verbs() { function get_poke_verbs() {
@ -1169,8 +1169,25 @@ function redir_private_images($a, &$item)
} }
} }
/**
* Sets the "rendered-html" field of the provided item
*
* Body is preserved to avoid side-effects as we modify it just-in-time for spoilers and private image links
*
* @param array $item
* @param bool $update
*
* @todo Remove reference, simply return "rendered-html" and "rendered-hash"
*/
function put_item_in_cache(&$item, $update = false) function put_item_in_cache(&$item, $update = false)
{ {
$body = $item["body"];
// Add the content warning
if (!empty($item['content-warning'])) {
$item["body"] = $item['content-warning'] . '[spoiler]' . $item["body"] . '[/spoiler]';
}
$rendered_hash = defaults($item, 'rendered-hash', ''); $rendered_hash = defaults($item, 'rendered-hash', '');
if ($rendered_hash == '' if ($rendered_hash == ''
@ -1178,22 +1195,19 @@ function put_item_in_cache(&$item, $update = false)
|| $rendered_hash != hash("md5", $item["body"]) || $rendered_hash != hash("md5", $item["body"])
|| Config::get("system", "ignore_cache") || Config::get("system", "ignore_cache")
) { ) {
// The function "redir_private_images" changes the body.
// I'm not sure if we should store it permanently, so we save the old value.
$body = $item["body"];
$a = get_app(); $a = get_app();
redir_private_images($a, $item); redir_private_images($a, $item);
$item["rendered-html"] = prepare_text($item["body"]); $item["rendered-html"] = prepare_text($item["body"]);
$item["rendered-hash"] = hash("md5", $item["body"]); $item["rendered-hash"] = hash("md5", $item["body"]);
$item["body"] = $body;
if ($update && ($item["id"] > 0)) { if ($update && ($item["id"] > 0)) {
dba::update('item', ['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]], dba::update('item', ['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]],
['id' => $item["id"]], false); ['id' => $item["id"]], false);
} }
} }
$item["body"] = $body;
} }
/** /**
@ -1281,62 +1295,55 @@ function prepare_body(&$item, $attach = false, $preview = false) {
$as = ''; $as = '';
$vhead = false; $vhead = false;
$arr = explode('[/attach],', $item['attach']); $matches = [];
if (count($arr)) { preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\"(?: title=\"(.*?)\")?|', $item['attach'], $matches, PREG_SET_ORDER);
foreach ($arr as $r) { foreach ($matches as $mtch) {
$matches = false; $mime = $mtch[3];
$icon = '';
$cnt = preg_match_all('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r ,$matches, PREG_SET_ORDER);
if ($cnt) {
foreach ($matches as $mtch) {
$mime = $mtch[3];
if ((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { if ((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
$the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1]; $the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1];
} else { } else {
$the_url = $mtch[1]; $the_url = $mtch[1];
}
if (strpos($mime, 'video') !== false) {
if (!$vhead) {
$vhead = true;
$a->page['htmlhead'] .= replace_macros(get_markup_template('videos_head.tpl'), [
'$baseurl' => System::baseUrl(),
]);
$a->page['end'] .= replace_macros(get_markup_template('videos_end.tpl'), [
'$baseurl' => System::baseUrl(),
]);
}
$id = end(explode('/', $the_url));
$as .= replace_macros(get_markup_template('video_top.tpl'), [
'$video' => [
'id' => $id,
'title' => L10n::t('View Video'),
'src' => $the_url,
'mime' => $mime,
],
]);
}
$filetype = strtolower(substr($mime, 0, strpos($mime, '/')));
if ($filetype) {
$filesubtype = strtolower(substr($mime, strpos($mime, '/') + 1));
$filesubtype = str_replace('.', '-', $filesubtype);
} else {
$filetype = 'unkn';
$filesubtype = 'unkn';
}
$title = ((strlen(trim($mtch[4]))) ? escape_tags(trim($mtch[4])) : escape_tags($mtch[1]));
$title .= ' ' . $mtch[2] . ' ' . L10n::t('bytes');
$icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
$as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
}
}
} }
if (strpos($mime, 'video') !== false) {
if (!$vhead) {
$vhead = true;
$a->page['htmlhead'] .= replace_macros(get_markup_template('videos_head.tpl'), [
'$baseurl' => System::baseUrl(),
]);
$a->page['end'] .= replace_macros(get_markup_template('videos_end.tpl'), [
'$baseurl' => System::baseUrl(),
]);
}
$id = end(explode('/', $the_url));
$as .= replace_macros(get_markup_template('video_top.tpl'), [
'$video' => [
'id' => $id,
'title' => L10n::t('View Video'),
'src' => $the_url,
'mime' => $mime,
],
]);
}
$filetype = strtolower(substr($mime, 0, strpos($mime, '/')));
if ($filetype) {
$filesubtype = strtolower(substr($mime, strpos($mime, '/') + 1));
$filesubtype = str_replace('.', '-', $filesubtype);
} else {
$filetype = 'unkn';
$filesubtype = 'unkn';
}
$title = escape_tags(trim(!empty($mtch[4]) ? $mtch[4] : $mtch[1]));
$title .= ' ' . $mtch[2] . ' ' . L10n::t('bytes');
$icon = '<div class="attachtype icon s22 type-' . $filetype . ' subtype-' . $filesubtype . '"></div>';
$as .= '<a href="' . strip_tags($the_url) . '" title="' . $title . '" class="attachlink" target="_blank" >' . $icon . '</a>';
} }
if ($as != '') { if ($as != '') {
$s .= '<div class="body-attach">'.$as.'<div class="clear"></div></div>'; $s .= '<div class="body-attach">'.$as.'<div class="clear"></div></div>';
} }

View File

@ -74,7 +74,7 @@ if (!$install) {
if (Config::get('system', 'force_ssl') && ($a->get_scheme() == "http") if (Config::get('system', 'force_ssl') && ($a->get_scheme() == "http")
&& (intval(Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL) && (intval(Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL)
&& (substr(System::baseUrl(), 0, 8) == "https://") && (substr(System::baseUrl(), 0, 8) == "https://")
) { && ($_SERVER['REQUEST_METHOD'] == 'GET')) {
header("HTTP/1.1 302 Moved Temporarily"); header("HTTP/1.1 302 Moved Temporarily");
header("Location: " . System::baseUrl() . "/" . $a->query_string); header("Location: " . System::baseUrl() . "/" . $a->query_string);
exit(); exit();

View File

@ -1568,7 +1568,7 @@ function admin_page_users(App $a)
if ($a->argc > 2) { if ($a->argc > 2) {
$uid = $a->argv[3]; $uid = $a->argv[3];
$user = dba::selectFirst('user', ['username', 'blocked'], ['uid' => $uid]); $user = dba::selectFirst('user', ['username', 'blocked'], ['uid' => $uid]);
if (DBM::is_result($user)) { if (!DBM::is_result($user)) {
notice('User not found' . EOL); notice('User not found' . EOL);
goaway('admin/users'); goaway('admin/users');
return ''; // NOTREACHED return ''; // NOTREACHED

View File

@ -45,6 +45,14 @@ function contacts_init(App $a)
} }
if (DBM::is_result($contact)) { if (DBM::is_result($contact)) {
if ($contact['self']) {
if (($a->argc == 3) && intval($a->argv[1]) && ($a->argv[2] == "posts")) {
goaway('profile/' . $contact['nick']);
} else {
goaway('profile/' . $contact['nick'] . '?tab=profile');
}
}
$a->data['contact'] = $contact; $a->data['contact'] = $contact;
if (($a->data['contact']['network'] != "") && ($a->data['contact']['network'] != NETWORK_DFRN)) { if (($a->data['contact']['network'] != "") && ($a->data['contact']['network'] != NETWORK_DFRN)) {
@ -579,9 +587,10 @@ function contacts_content(App $a)
$profile_select = ContactSelector::profileAssign($contact['profile-id'], (($contact['network'] !== NETWORK_DFRN) ? true : false)); $profile_select = ContactSelector::profileAssign($contact['profile-id'], (($contact['network'] !== NETWORK_DFRN) ? true : false));
} }
/// @todo Only show the following link with DFRN when the remote version supports it
$follow = ''; $follow = '';
$follow_text = ''; $follow_text = '';
if (in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS])) { if (in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_DFRN])) {
if ($contact['rel'] == CONTACT_IS_FOLLOWER) { if ($contact['rel'] == CONTACT_IS_FOLLOWER) {
$follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]); $follow = System::baseUrl(true) . "/follow?url=" . urlencode($contact["url"]);
$follow_text = L10n::t("Connect/Follow"); $follow_text = L10n::t("Connect/Follow");
@ -939,6 +948,13 @@ function _contact_detail_for_template($rr)
$sparkle = ''; $sparkle = '';
} }
if ($rr['self']) {
$dir_icon = 'images/larrow.gif';
$alt_text = L10n::t('This is you');
$url = $rr['url'];
$sparkle = '';
}
return [ return [
'img_hover' => L10n::t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']), 'img_hover' => L10n::t('Visit %s\'s profile [%s]', $rr['name'], $rr['url']),
'edit_hover' => L10n::t('Edit contact'), 'edit_hover' => L10n::t('Edit contact'),

View File

@ -6,6 +6,7 @@ use Friendica\App;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Database\DBM; use Friendica\Database\DBM;
use Friendica\Model\User;
require_once 'mod/settings.php'; require_once 'mod/settings.php';
@ -28,6 +29,21 @@ function delegate_post(App $a)
check_form_security_token_redirectOnErr('/delegate', 'delegate'); check_form_security_token_redirectOnErr('/delegate', 'delegate');
$parent_uid = defaults($_POST, 'parent_user', 0); $parent_uid = defaults($_POST, 'parent_user', 0);
$parent_password = defaults($_POST, 'parent_password', '');
if ($parent_uid != 0) {
$user = dba::selectFirst('user', ['nickname'], ['uid' => $parent_uid]);
if (!DBM::is_result($user)) {
notice(L10n::t('Parent user not found.') . EOL);
return;
}
$success = User::authenticate($user['nickname'], trim($parent_password));
if (!$success) {
notice(L10n::t('Permission denied.') . EOL);
return;
}
}
dba::update('user', ['parent-uid' => $parent_uid], ['uid' => local_user()]); dba::update('user', ['parent-uid' => $parent_uid], ['uid' => local_user()]);
} }
@ -70,16 +86,6 @@ function delegate_content(App $a)
goaway(System::baseUrl() . '/delegate'); goaway(System::baseUrl() . '/delegate');
} }
// These people can manage this account/page with full privilege
$full_managers = [];
$r = q("SELECT * FROM `user` WHERE `email` = '%s' AND `password` = '%s' ",
dbesc($a->user['email']),
dbesc($a->user['password'])
);
if (DBM::is_result($r)) {
$full_managers = $r;
}
// find everybody that currently has delegated management to this account/page // find everybody that currently has delegated management to this account/page
$delegates = []; $delegates = [];
$r = q("SELECT * FROM `user` WHERE `uid` IN (SELECT `uid` FROM `manage` WHERE `mid` = %d)", $r = q("SELECT * FROM `user` WHERE `uid` IN (SELECT `uid` FROM `manage` WHERE `mid` = %d)",
@ -90,10 +96,6 @@ function delegate_content(App $a)
} }
$uids = []; $uids = [];
foreach ($full_managers as $rr) {
$uids[] = $rr['uid'];
}
foreach ($delegates as $rr) { foreach ($delegates as $rr) {
$uids[] = $rr['uid']; $uids[] = $rr['uid'];
} }
@ -153,18 +155,21 @@ function delegate_content(App $a)
} }
} }
if (!is_null($parent_user)) {
$parent_password = ['parent_password', L10n::t('Parent Password:'), '', L10n::t('Please enter the password of the parent account to legitimize your request.')];
}
$o = replace_macros(get_markup_template('delegate.tpl'), [ $o = replace_macros(get_markup_template('delegate.tpl'), [
'$form_security_token' => get_form_security_token('delegate'), '$form_security_token' => get_form_security_token('delegate'),
'$parent_header' => L10n::t('Parent User'), '$parent_header' => L10n::t('Parent User'),
'$parent_user' => $parent_user, '$parent_user' => $parent_user,
'$parent_password' => $parent_password,
'$parent_desc' => L10n::t('Parent users have total control about this account, including the account settings. Please double check whom you give this access.'), '$parent_desc' => L10n::t('Parent users have total control about this account, including the account settings. Please double check whom you give this access.'),
'$submit' => L10n::t('Save Settings'), '$submit' => L10n::t('Save Settings'),
'$header' => L10n::t('Delegate Page Management'), '$header' => L10n::t('Delegate Page Management'),
'$delegates_header' => L10n::t('Delegates'), '$delegates_header' => L10n::t('Delegates'),
'$base' => System::baseUrl(), '$base' => System::baseUrl(),
'$desc' => L10n::t('Delegates are able to manage all aspects of this account/page except for basic account settings. Please do not delegate your personal account to anybody that you do not trust completely.'), '$desc' => L10n::t('Delegates are able to manage all aspects of this account/page except for basic account settings. Please do not delegate your personal account to anybody that you do not trust completely.'),
'$head_managers' => L10n::t('Existing Page Managers'),
'$managers' => $full_managers,
'$head_delegates' => L10n::t('Existing Page Delegates'), '$head_delegates' => L10n::t('Existing Page Delegates'),
'$delegates' => $delegates, '$delegates' => $delegates,
'$head_potentials' => L10n::t('Potential Delegates'), '$head_potentials' => L10n::t('Potential Delegates'),

View File

@ -18,6 +18,12 @@ require_once 'include/event.php';
function dfrn_notify_post(App $a) { function dfrn_notify_post(App $a) {
logger(__function__, LOGGER_TRACE); logger(__function__, LOGGER_TRACE);
if (empty($_POST)) {
require_once 'mod/salmon.php';
salmon_post($a);
}
$dfrn_id = ((x($_POST,'dfrn_id')) ? notags(trim($_POST['dfrn_id'])) : ''); $dfrn_id = ((x($_POST,'dfrn_id')) ? notags(trim($_POST['dfrn_id'])) : '');
$dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0); $dfrn_version = ((x($_POST,'dfrn_version')) ? (float) $_POST['dfrn_version'] : 2.0);
$challenge = ((x($_POST,'challenge')) ? notags(trim($_POST['challenge'])) : ''); $challenge = ((x($_POST,'challenge')) ? notags(trim($_POST['challenge'])) : '');

View File

@ -12,12 +12,12 @@ use Friendica\Database\DBM;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
function directory_init(App $a) { function directory_init(App $a)
{
$a->set_pager_itemspage(60); $a->set_pager_itemspage(60);
if(local_user()) { if (local_user()) {
$a->page['aside'] .= Widget::findPeople(); $a->page['aside'] .= Widget::findPeople();
$a->page['aside'] .= Widget::follow(); $a->page['aside'] .= Widget::follow();
} else { } else {
unset($_SESSION['theme']); unset($_SESSION['theme']);
@ -25,16 +25,20 @@ function directory_init(App $a) {
} }
} }
function directory_post(App $a) { function directory_post(App $a)
if(x($_POST,'search')) {
if (x($_POST, 'search')) {
$a->data['search'] = $_POST['search']; $a->data['search'] = $_POST['search'];
}
} }
function directory_content(App $a) { function directory_content(App $a)
{
require_once("mod/proxy.php"); require_once("mod/proxy.php");
if((Config::get('system','block_public')) && (! local_user()) && (! remote_user()) || if ((Config::get('system', 'block_public') && !local_user() && !remote_user())
(Config::get('system','block_local_dir')) && (! local_user()) && (! remote_user())) { || (Config::get('system', 'block_local_dir') && !local_user() && !remote_user())
) {
notice(L10n::t('Public access denied.') . EOL); notice(L10n::t('Public access denied.') . EOL);
return; return;
} }
@ -42,18 +46,19 @@ function directory_content(App $a) {
$o = ''; $o = '';
Nav::setSelected('directory'); Nav::setSelected('directory');
if(x($a->data,'search')) if (x($a->data, 'search')) {
$search = notags(trim($a->data['search'])); $search = notags(trim($a->data['search']));
else } else {
$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : ''); $search = ((x($_GET, 'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
$gdirpath = '';
$dirurl = Config::get('system','directory');
if(strlen($dirurl)) {
$gdirpath = Profile::zrl($dirurl,true);
} }
if($search) { $gdirpath = '';
$dirurl = Config::get('system', 'directory');
if (strlen($dirurl)) {
$gdirpath = Profile::zrl($dirurl, true);
}
if ($search) {
$search = dbesc($search); $search = dbesc($search);
$sql_extra = " AND ((`profile`.`name` LIKE '%$search%') OR $sql_extra = " AND ((`profile`.`name` LIKE '%$search%') OR
@ -73,35 +78,35 @@ function directory_content(App $a) {
(`profile`.`prv_keywords` LIKE '%$search%'))"; (`profile`.`prv_keywords` LIKE '%$search%'))";
} }
$publish = ((Config::get('system','publish_all')) ? '' : " AND `publish` = 1 " ); $publish = ((Config::get('system', 'publish_all')) ? '' : " AND `publish` = 1 " );
$r = q("SELECT COUNT(*) AS `total` FROM `profile` $cnt = dba::selectFirst("SELECT COUNT(*) AS `total` FROM `profile`
LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid`
WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra "); WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra "
if (DBM::is_result($r)) );
$a->set_pager_total($r[0]['total']); if (DBM::is_result($cnt)) {
$a->set_pager_total($cnt['total']);
}
$order = " ORDER BY `name` ASC "; $order = " ORDER BY `name` ASC ";
$limit = intval($a->pager['start']).",".intval($a->pager['itemspage']); $limit = intval($a->pager['start'])."," . intval($a->pager['itemspage']);
$r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, $r = dba::p("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`,
`contact`.`addr`, `contact`.`url` AS profile_url FROM `profile` `contact`.`addr`, `contact`.`url` AS profile_url FROM `profile`
LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid`
LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid`
WHERE `is-default` $publish AND `user`.`blocked` = 0 AND `contact`.`self` $sql_extra $order LIMIT ".$limit); WHERE `is-default` $publish AND `user`.`blocked` = 0 AND `contact`.`self` $sql_extra $order LIMIT ".$limit
);
if (DBM::is_result($r)) { if (DBM::is_result($r)) {
if (in_array('small', $a->argv)) { if (in_array('small', $a->argv)) {
$photo = 'thumb'; $photo = 'thumb';
} } else {
else {
$photo = 'photo'; $photo = 'photo';
} }
foreach ($r as $rr) { while ($rr = dba::fetch($r)) {
$itemurl= ''; $itemurl= '';
$itemurl = (($rr['addr'] != "") ? $rr['addr'] : $rr['profile_url']); $itemurl = (($rr['addr'] != "") ? $rr['addr'] : $rr['profile_url']);
@ -111,16 +116,19 @@ function directory_content(App $a) {
$pdesc = (($rr['pdesc']) ? $rr['pdesc'] . '<br />' : ''); $pdesc = (($rr['pdesc']) ? $rr['pdesc'] . '<br />' : '');
$details = ''; $details = '';
if(strlen($rr['locality'])) if (strlen($rr['locality'])) {
$details .= $rr['locality']; $details .= $rr['locality'];
if(strlen($rr['region'])) { }
if(strlen($rr['locality'])) if (strlen($rr['region'])) {
if (strlen($rr['locality'])) {
$details .= ', '; $details .= ', ';
}
$details .= $rr['region']; $details .= $rr['region'];
} }
if(strlen($rr['country-name'])) { if (strlen($rr['country-name'])) {
if(strlen($details)) if (strlen($details)) {
$details .= ', '; $details .= ', ';
}
$details .= $rr['country-name']; $details .= $rr['country-name'];
} }
// if(strlen($rr['dob'])) { // if(strlen($rr['dob'])) {
@ -132,20 +140,19 @@ function directory_content(App $a) {
$profile = $rr; $profile = $rr;
if((x($profile,'address') == 1) if ((x($profile, 'address') == 1)
|| (x($profile,'locality') == 1) || (x($profile, 'locality') == 1)
|| (x($profile,'region') == 1) || (x($profile, 'region') == 1)
|| (x($profile,'postal-code') == 1) || (x($profile, 'postal-code') == 1)
|| (x($profile,'country-name') == 1)) || (x($profile, 'country-name') == 1)
$location = L10n::t('Location:'); ) {
$location = L10n::t('Location:');
}
$gender = ((x($profile,'gender') == 1) ? L10n::t('Gender:') : False); $gender = ((x($profile, 'gender') == 1) ? L10n::t('Gender:') : false);
$marital = ((x($profile, 'marital') == 1) ? L10n::t('Status:') : false);
$marital = ((x($profile,'marital') == 1) ? L10n::t('Status:') : False); $homepage = ((x($profile, 'homepage') == 1) ? L10n::t('Homepage:') : false);
$about = ((x($profile, 'about') == 1) ? L10n::t('About:') : false);
$homepage = ((x($profile,'homepage') == 1) ? L10n::t('Homepage:') : False);
$about = ((x($profile,'about') == 1) ? L10n::t('About:') : False);
$location_e = $location; $location_e = $location;
@ -154,23 +161,23 @@ function directory_content(App $a) {
]; ];
$entry = [ $entry = [
'id' => $rr['id'], 'id' => $rr['id'],
'url' => $profile_link, 'url' => $profile_link,
'itemurl' => $itemurl, 'itemurl' => $itemurl,
'thumb' => proxy_url($rr[$photo], false, PROXY_SIZE_THUMB), 'thumb' => proxy_url($rr[$photo], false, PROXY_SIZE_THUMB),
'img_hover' => $rr['name'], 'img_hover' => $rr['name'],
'name' => $rr['name'], 'name' => $rr['name'],
'details' => $details, 'details' => $details,
'account_type' => Contact::getAccountType($rr), 'account_type' => Contact::getAccountType($rr),
'profile' => $profile, 'profile' => $profile,
'location' => $location_e, 'location' => $location_e,
'tags' => $rr['pub_keywords'], 'tags' => $rr['pub_keywords'],
'gender' => $gender, 'gender' => $gender,
'pdesc' => $pdesc, 'pdesc' => $pdesc,
'marital' => $marital, 'marital' => $marital,
'homepage' => $homepage, 'homepage' => $homepage,
'about' => $about, 'about' => $about,
'photo_menu' => $photo_menu, 'photo_menu' => $photo_menu,
]; ];
@ -187,20 +194,21 @@ function directory_content(App $a) {
$entries[] = $arr['entry']; $entries[] = $arr['entry'];
} }
dba::close($r);
$tpl = get_markup_template('directory_header.tpl'); $tpl = get_markup_template('directory_header.tpl');
$o .= replace_macros($tpl, [ $o .= replace_macros($tpl, [
'$search' => $search, '$search' => $search,
'$globaldir' => L10n::t('Global Directory'), '$globaldir' => L10n::t('Global Directory'),
'$gdirpath' => $gdirpath, '$gdirpath' => $gdirpath,
'$desc' => L10n::t('Find on this site'), '$desc' => L10n::t('Find on this site'),
'$contacts' => $entries, '$contacts' => $entries,
'$finding' => L10n::t('Results for:'), '$finding' => L10n::t('Results for:'),
'$findterm' => (strlen($search) ? $search : ""), '$findterm' => (strlen($search) ? $search : ""),
'$title' => L10n::t('Site Directory'), '$title' => L10n::t('Site Directory'),
'$submit' => L10n::t('Find'), '$submit' => L10n::t('Find'),
'$paginate' => paginate($a), '$paginate' => paginate($a),
]); ]);
} else { } else {
info(L10n::t("No entries \x28some entries may be hidden\x29.") . EOL); info(L10n::t("No entries \x28some entries may be hidden\x29.") . EOL);

View File

@ -15,6 +15,7 @@ use Friendica\Model\Profile;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Protocol\PortableContact; use Friendica\Protocol\PortableContact;
use Friendica\Util\Network; use Friendica\Util\Network;
use Friendica\Database\DBM;
require_once 'mod/contacts.php'; require_once 'mod/contacts.php';
@ -113,32 +114,22 @@ function dirfind_content(App $a, $prefix = "") {
/// @TODO These 2 SELECTs are not checked on validity with DBM::is_result() /// @TODO These 2 SELECTs are not checked on validity with DBM::is_result()
$count = q("SELECT count(*) AS `total` FROM `gcontact` $count = q("SELECT count(*) AS `total` FROM `gcontact`
LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl` WHERE NOT `hide` AND `network` IN ('%s', '%s', '%s') AND
AND `contact`.`network` = `gcontact`.`network` ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND
AND `contact`.`uid` = %d AND NOT `contact`.`blocked` (`url` LIKE '%s' OR `name` LIKE '%s' OR `location` LIKE '%s' OR
AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s') `addr` LIKE '%s' OR `about` LIKE '%s' OR `keywords` LIKE '%s') $extra_sql",
WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
(`gcontact`.`url` LIKE '%s' OR `gcontact`.`name` LIKE '%s' OR `gcontact`.`location` LIKE '%s' OR
`gcontact`.`addr` LIKE '%s' OR `gcontact`.`about` LIKE '%s' OR `gcontact`.`keywords` LIKE '%s') $extra_sql",
intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND),
dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora), dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora),
dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)),
dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2))); dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)));
$results = q("SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`photo`, `gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr` $results = q("SELECT `nurl`
FROM `gcontact` FROM `gcontact`
LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl` WHERE NOT `hide` AND `network` IN ('%s', '%s', '%s') AND
AND `contact`.`network` = `gcontact`.`network` ((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND
AND `contact`.`uid` = %d AND NOT `contact`.`blocked` (`url` LIKE '%s' OR `name` LIKE '%s' OR `location` LIKE '%s' OR
AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s') `addr` LIKE '%s' OR `about` LIKE '%s' OR `keywords` LIKE '%s') $extra_sql
WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND GROUP BY `nurl`
((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR (`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND ORDER BY `updated` DESC LIMIT %d, %d",
(`gcontact`.`url` LIKE '%s' OR `gcontact`.`name` LIKE '%s' OR `gcontact`.`location` LIKE '%s' OR
`gcontact`.`addr` LIKE '%s' OR `gcontact`.`about` LIKE '%s' OR `gcontact`.`keywords` LIKE '%s') $extra_sql
GROUP BY `gcontact`.`nurl`
ORDER BY `gcontact`.`updated` DESC LIMIT %d, %d",
intval(local_user()), dbesc(CONTACT_IS_SHARING), dbesc(CONTACT_IS_FRIEND),
dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora), dbesc(NETWORK_DFRN), dbesc($ostatus), dbesc($diaspora),
dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)),
dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)), dbesc(escape_tags($search2)),
@ -148,14 +139,21 @@ function dirfind_content(App $a, $prefix = "") {
$j->items_page = $perpage; $j->items_page = $perpage;
$j->page = $a->pager['page']; $j->page = $a->pager['page'];
foreach ($results AS $result) { foreach ($results AS $result) {
if (PortableContact::alternateOStatusUrl($result["url"])) { if (PortableContact::alternateOStatusUrl($result["nurl"])) {
continue; continue;
} }
$result = Contact::getDetailsByURL($result["url"], local_user(), $result); $urlparts = parse_url($result["nurl"]);
// Ignore results that look strange.
// For historic reasons the gcontact table does contain some garbage.
if (!empty($urlparts['query']) || !empty($urlparts['fragment'])) {
continue;
}
$result = Contact::getDetailsByURL($result["nurl"], local_user());
if ($result["name"] == "") { if ($result["name"] == "") {
$urlparts = parse_url($result["url"]);
$result["name"] = end(explode("/", $urlparts["path"])); $result["name"] = end(explode("/", $urlparts["path"]));
} }
@ -204,11 +202,10 @@ function dirfind_content(App $a, $prefix = "") {
if ($jj->cid > 0) { if ($jj->cid > 0) {
$connlnk = ""; $connlnk = "";
$conntxt = ""; $conntxt = "";
$contact = q("SELECT * FROM `contact` WHERE `id` = %d", $contact = dba::selectFirst('contact', [], ['id' => $jj->cid]);
intval($jj->cid)); if (DBM::is_result($contact)) {
if ($contact) { $photo_menu = Contact::photoMenu($contact);
$photo_menu = Contact::photoMenu($contact[0]); $details = _contact_detail_for_template($contact);
$details = _contact_detail_for_template($contact[0]);
$alt_text = $details['alt_text']; $alt_text = $details['alt_text'];
} else { } else {
$photo_menu = []; $photo_menu = [];

View File

@ -212,7 +212,11 @@ function display_content(App $a, $update = false, $update_uid = 0) {
if ($update) { if ($update) {
$item_id = $_REQUEST['item_id']; $item_id = $_REQUEST['item_id'];
$item = dba::selectFirst('item', ['uid', 'parent'], ['id' => $item_id]); $item = dba::selectFirst('item', ['uid', 'parent'], ['id' => $item_id]);
$a->profile = ['uid' => intval($item['uid']), 'profile_uid' => intval($item['uid'])]; if ($item['uid'] != 0) {
$a->profile = ['uid' => intval($item['uid']), 'profile_uid' => intval($item['uid'])];
} else {
$a->profile = ['uid' => intval($update_uid), 'profile_uid' => intval($update_uid)];
}
$item_parent = $item['parent']; $item_parent = $item['parent'];
} else { } else {
$item_id = (($a->argc > 2) ? $a->argv[2] : 0); $item_id = (($a->argc > 2) ? $a->argv[2] : 0);

View File

@ -9,6 +9,7 @@ use Friendica\Core\System;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Network\Probe; use Friendica\Network\Probe;
use Friendica\Database\DBM;
function follow_post(App $a) { function follow_post(App $a) {
@ -60,19 +61,20 @@ function follow_content(App $a) {
$submit = L10n::t('Submit Request'); $submit = L10n::t('Submit Request');
// There is a current issue. It seems as if you can't start following a Friendica that is following you // Don't try to add a pending contact
// With Diaspora this works - but Friendica is special, it seems ... $r = q("SELECT `pending` FROM `contact` WHERE `uid` = %d AND ((`rel` != %d) OR (`network` = '%s')) AND
$r = q("SELECT `url` FROM `contact` WHERE `uid` = %d AND ((`rel` != %d) OR (`network` = '%s')) AND
(`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s') AND
`network` != '%s' LIMIT 1", `network` != '%s' LIMIT 1",
intval(local_user()), dbesc(CONTACT_IS_FOLLOWER), dbesc(NETWORK_DFRN), dbesc(normalise_link($url)), intval(local_user()), dbesc(CONTACT_IS_FOLLOWER), dbesc(NETWORK_DFRN), dbesc(normalise_link($url)),
dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET)); dbesc(normalise_link($url)), dbesc($url), dbesc(NETWORK_STATUSNET));
if ($r) { if ($r) {
notice(L10n::t('You already added this contact.').EOL); if ($r[0]['pending']) {
$submit = ""; notice(L10n::t('You already added this contact.').EOL);
//goaway($_SESSION['return_url']); $submit = "";
// NOTREACHED //goaway($_SESSION['return_url']);
// NOTREACHED
}
} }
$ret = Probe::uri($url); $ret = Probe::uri($url);
@ -102,7 +104,7 @@ function follow_content(App $a) {
$ret["url"] = $ret["addr"]; $ret["url"] = $ret["addr"];
} }
if ($ret['network'] === NETWORK_DFRN) { if (($ret['network'] === NETWORK_DFRN) && !DBM::is_result($r)) {
$request = $ret["request"]; $request = $ret["request"];
$tpl = get_markup_template('dfrn_request.tpl'); $tpl = get_markup_template('dfrn_request.tpl');
} else { } else {

View File

@ -397,7 +397,8 @@ function check_funcs(&$checks) {
check_add($ck_funcs, L10n::t('PDO or MySQLi PHP module'), true, true, ""); check_add($ck_funcs, L10n::t('PDO or MySQLi PHP module'), true, true, "");
check_add($ck_funcs, L10n::t('mb_string PHP module'), true, true, ""); check_add($ck_funcs, L10n::t('mb_string PHP module'), true, true, "");
check_add($ck_funcs, L10n::t('XML PHP module'), true, true, ""); check_add($ck_funcs, L10n::t('XML PHP module'), true, true, "");
check_add($ck_funcs, L10n::t('iconv module'), true, true, ""); check_add($ck_funcs, L10n::t('iconv PHP module'), true, true, "");
check_add($ck_funcs, L10n::t('POSIX PHP module'), true, true, "");
if (function_exists('apache_get_modules')) { if (function_exists('apache_get_modules')) {
if (! in_array('mod_rewrite',apache_get_modules())) { if (! in_array('mod_rewrite',apache_get_modules())) {
@ -432,8 +433,12 @@ function check_funcs(&$checks) {
$ck_funcs[4]['help'] = L10n::t('Error: mb_string PHP module required but not installed.'); $ck_funcs[4]['help'] = L10n::t('Error: mb_string PHP module required but not installed.');
} }
if (! function_exists('iconv_strlen')) { if (! function_exists('iconv_strlen')) {
$ck_funcs[6]['status'] = false;
$ck_funcs[6]['help'] = L10n::t('Error: iconv PHP module required but not installed.');
}
if (! function_exists('posix_kill')) {
$ck_funcs[7]['status'] = false; $ck_funcs[7]['status'] = false;
$ck_funcs[7]['help'] = L10n::t('Error: iconv PHP module required but not installed.'); $ck_funcs[7]['help'] = L10n::t('Error: POSIX PHP module required but not installed.');
} }
$checks = array_merge($checks, $ck_funcs); $checks = array_merge($checks, $ck_funcs);
@ -442,8 +447,8 @@ function check_funcs(&$checks) {
try { try {
$xml = new DOMDocument(); $xml = new DOMDocument();
} catch (Exception $e) { } catch (Exception $e) {
$ck_funcs[6]['status'] = false; $ck_funcs[5]['status'] = false;
$ck_funcs[6]['help'] = L10n::t('Error, XML PHP module required but not installed.'); $ck_funcs[5]['help'] = L10n::t('Error, XML PHP module required but not installed.');
} }
} }
@ -540,7 +545,7 @@ function load_database_rem($v, $i) {
} }
function load_database() { function load_database() {
$errors = DBStructure::update(false, true); $errors = DBStructure::update(false, true, true);
return $errors; return $errors;
} }

View File

@ -29,12 +29,14 @@ function ostatus_subscribe_content(App $a) {
if (PConfig::get($uid, "ostatus", "legacy_friends") == "") { if (PConfig::get($uid, "ostatus", "legacy_friends") == "") {
if ($_REQUEST["url"] == "") { if ($_REQUEST["url"] == "") {
PConfig::delete($uid, "ostatus", "legacy_contact");
return $o.L10n::t("No contact provided."); return $o.L10n::t("No contact provided.");
} }
$contact = Probe::uri($_REQUEST["url"]); $contact = Probe::uri($_REQUEST["url"]);
if (!$contact) { if (!$contact) {
PConfig::delete($uid, "ostatus", "legacy_contact");
return $o.L10n::t("Couldn't fetch information for contact."); return $o.L10n::t("Couldn't fetch information for contact.");
} }
@ -44,6 +46,7 @@ function ostatus_subscribe_content(App $a) {
$data = Network::curl($api."statuses/friends.json?screen_name=".$contact["nick"]); $data = Network::curl($api."statuses/friends.json?screen_name=".$contact["nick"]);
if (!$data["success"]) { if (!$data["success"]) {
PConfig::delete($uid, "ostatus", "legacy_contact");
return $o.L10n::t("Couldn't fetch friends for contact."); return $o.L10n::t("Couldn't fetch friends for contact.");
} }

View File

@ -2,162 +2,136 @@
use Friendica\App; use Friendica\App;
use Friendica\Database\DBM; use Friendica\Database\DBM;
use Friendica\Protocol\OStatus;
function hub_return($valid,$body) { require_once('include/security.php');
require_once('include/items.php');
if($valid) { function hub_return($valid, $body)
header($_SERVER["SERVER_PROTOCOL"] . ' 200 ' . 'OK'); {
if ($valid) {
header($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
echo $body; echo $body;
killme(); } else {
header($_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
} }
else { killme();
header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . 'Not Found');
killme();
}
// NOTREACHED
} }
// when receiving an XML feed, always return OK // when receiving an XML feed, always return OK
function hub_post_return() { function hub_post_return()
{
header($_SERVER["SERVER_PROTOCOL"] . ' 200 ' . 'OK'); header($_SERVER["SERVER_PROTOCOL"] . ' 200 OK');
killme(); killme();
} }
function pubsub_init(App $a)
{
function pubsub_init(App $a) {
$nick = (($a->argc > 1) ? notags(trim($a->argv[1])) : ''); $nick = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
$contact_id = (($a->argc > 2) ? intval($a->argv[2]) : 0 ); $contact_id = (($a->argc > 2) ? intval($a->argv[2]) : 0 );
if($_SERVER['REQUEST_METHOD'] === 'GET') { if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$hub_mode = notags(trim(defaults($_GET, 'hub_mode', '')));
$hub_topic = notags(trim(defaults($_GET, 'hub_topic', '')));
$hub_challenge = notags(trim(defaults($_GET, 'hub_challenge', '')));
$hub_lease = notags(trim(defaults($_GET, 'hub_lease_seconds', '')));
$hub_verify = notags(trim(defaults($_GET, 'hub_verify_token', '')));
$hub_mode = ((x($_GET,'hub_mode')) ? notags(trim($_GET['hub_mode'])) : ''); logger('Subscription from ' . $_SERVER['REMOTE_ADDR'] . ' Mode: ' . $hub_mode . ' Nick: ' . $nick);
$hub_topic = ((x($_GET,'hub_topic')) ? notags(trim($_GET['hub_topic'])) : ''); logger('Data: ' . print_r($_GET,true), LOGGER_DATA);
$hub_challenge = ((x($_GET,'hub_challenge')) ? notags(trim($_GET['hub_challenge'])) : '');
$hub_lease = ((x($_GET,'hub_lease_seconds')) ? notags(trim($_GET['hub_lease_seconds'])) : '');
$hub_verify = ((x($_GET,'hub_verify_token')) ? notags(trim($_GET['hub_verify_token'])) : '');
logger('pubsub: Subscription from ' . $_SERVER['REMOTE_ADDR']);
logger('pubsub: data: ' . print_r($_GET,true), LOGGER_DATA);
$subscribe = (($hub_mode === 'subscribe') ? 1 : 0); $subscribe = (($hub_mode === 'subscribe') ? 1 : 0);
$r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1", $owner = dba::selectFirst('user', ['uid'], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
dbesc($nick) if (!DBM::is_result($owner)) {
); logger('Local account not found: ' . $nick);
if (! DBM::is_result($r)) {
logger('pubsub: local account not found: ' . $nick);
hub_return(false, ''); hub_return(false, '');
} }
$condition = ['uid' => $owner['uid'], 'id' => $contact_id, 'blocked' => false, 'pending' => false];
$owner = $r[0]; if (!empty($hub_verify)) {
$condition['hub-verify'] = $hub_verify;
}
$sql_extra = ((strlen($hub_verify)) ? sprintf(" AND `hub-verify` = '%s' ", dbesc($hub_verify)) : ''); $contact = dba::selectFirst('contact', ['id', 'poll'], $condition);
if (!DBM::is_result($contact)) {
$r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d logger('Contact ' . $contact_id . ' not found.');
AND `blocked` = 0 AND `pending` = 0 $sql_extra LIMIT 1",
intval($contact_id),
intval($owner['uid'])
);
if (! DBM::is_result($r)) {
logger('pubsub: contact '.$contact_id.' not found.');
hub_return(false, ''); hub_return(false, '');
} }
if ($hub_topic) if (!empty($hub_topic) && !link_compare($hub_topic, $contact['poll'])) {
if(! link_compare($hub_topic,$r[0]['poll'])) { logger('Hub topic ' . $hub_topic . ' != ' . $contact['poll']);
logger('pubsub: hub topic ' . $hub_topic . ' != ' . $r[0]['poll']); hub_return(false, '');
// should abort but let's humour them. }
}
$contact = $r[0];
// We must initiate an unsubscribe request with a verify_token. // We must initiate an unsubscribe request with a verify_token.
// Don't allow outsiders to unsubscribe us. // Don't allow outsiders to unsubscribe us.
if($hub_mode === 'unsubscribe') { if (($hub_mode === 'unsubscribe') && empty($hub_verify)) {
if(! strlen($hub_verify)) { logger('Bogus unsubscribe');
logger('pubsub: bogus unsubscribe'); hub_return(false, '');
hub_return(false, '');
}
logger('pubsub: unsubscribe success');
} }
if ($hub_mode) if (!empty($hub_mode)) {
$r = q("UPDATE `contact` SET `subhub` = %d WHERE `id` = %d", dba::update('contact', ['subhub' => $subscribe], ['id' => $contact['id']]);
intval($subscribe), logger($hub_mode . ' success for contact ' . $contact_id . '.');
intval($contact['id']) }
);
hub_return(true, $hub_challenge); hub_return(true, $hub_challenge);
} }
} }
require_once('include/security.php'); function pubsub_post(App $a)
{
function pubsub_post(App $a) {
$xml = file_get_contents('php://input'); $xml = file_get_contents('php://input');
logger('pubsub: feed arrived from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $a->cmd ); logger('Feed arrived from ' . $_SERVER['REMOTE_ADDR'] . ' for ' . $a->cmd . ' with user-agent: ' . $_SERVER['HTTP_USER_AGENT']);
logger('pubsub: user-agent: ' . $_SERVER['HTTP_USER_AGENT'] ); logger('Data: ' . $xml, LOGGER_DATA);
logger('pubsub: data: ' . $xml, LOGGER_DATA);
// if(! stristr($xml,'<?xml')) {
// logger('pubsub_post: bad xml');
// hub_post_return();
// }
$nick = (($a->argc > 1) ? notags(trim($a->argv[1])) : ''); $nick = (($a->argc > 1) ? notags(trim($a->argv[1])) : '');
$contact_id = (($a->argc > 2) ? intval($a->argv[2]) : 0 ); $contact_id = (($a->argc > 2) ? intval($a->argv[2]) : 0 );
$r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1", $importer = dba::selectFirst('user', [], ['nickname' => $nick, 'account_expired' => false, 'account_removed' => false]);
dbesc($nick) if (!DBM::is_result($importer)) {
);
if (! DBM::is_result($r)) {
hub_post_return(); hub_post_return();
} }
$importer = $r[0]; $condition = ['id' => $contact_id, 'uid' => $importer['uid'], 'subhub' => true, 'blocked' => false];
$contact = dba::selectFirst('contact', [], $condition);
$r = q("SELECT * FROM `contact` WHERE `subhub` AND `id` = %d AND `uid` = %d if (!DBM::is_result($contact)) {
AND (`rel` = %d OR `rel` = %d OR network = '%s') AND NOT `blocked` LIMIT 1", $author = OStatus::salmonAuthor($xml, $importer);
intval($contact_id), if (!empty($author['contact-id'])) {
intval($importer['uid']), $condition = ['id' => $author['contact-id'], 'uid' => $importer['uid'], 'subhub' => true, 'blocked' => false];
intval(CONTACT_IS_SHARING), $contact = dba::selectFirst('contact', [], $condition);
intval(CONTACT_IS_FRIEND), logger('No record for ' . $nick .' with contact id ' . $contact_id . ' - using '.$author['contact-id'].' instead.');
dbesc(NETWORK_FEED) }
); if (!DBM::is_result($contact)) {
logger('Contact ' . $author["author-link"] . ' (' . $contact_id . ') for user ' . $nick . " wasn't found - ignored. XML: " . $xml);
hub_post_return();
}
}
if (! DBM::is_result($r)) { if (!in_array($contact['rel'], [CONTACT_IS_SHARING, CONTACT_IS_FRIEND]) && ($contact['network'] != NETWORK_FEED)) {
logger('pubsub: no contact record for "'.$nick.' ('.$contact_id.')" - ignored. '.$xml); logger('Contact ' . $contact['id'] . ' is not expected to share with us - ignored.');
hub_post_return(); hub_post_return();
} }
$contact = $r[0]; // We import feeds from OStatus, Friendica and ATOM/RSS.
/// @todo Check if Friendica posts really arrive here - otherwise we can discard some stuff
// we have no way to match Diaspora guid's with atom post id's and could get duplicates. if (!in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DFRN, NETWORK_FEED])) {
// we'll assume that direct delivery is robust (and this is a bad assumption, but the duplicates are messy).
if($r[0]['network'] === NETWORK_DIASPORA)
hub_post_return(); hub_post_return();
}
logger('Import item for ' . $nick . ' from ' . $contact['nick'] . ' (' . $contact['id'] . ')');
$feedhub = ''; $feedhub = '';
consume_feed($xml, $importer, $contact, $feedhub);
require_once('include/items.php'); // do it a second time for DFRN so that any children find their parents.
if ($contact['network'] === NETWORK_DFRN) {
consume_feed($xml,$importer,$contact,$feedhub,1,1); consume_feed($xml, $importer, $contact, $feedhub);
}
// do it a second time so that any children find their parents.
consume_feed($xml,$importer,$contact,$feedhub,1,2);
hub_post_return(); hub_post_return();
} }

View File

@ -208,8 +208,7 @@ function settings_post(App $a)
return; return;
} }
if (($a->argc > 1) && ($a->argv[1] == 'connectors')) if (($a->argc > 1) && ($a->argv[1] == 'connectors')) {
{
check_form_security_token_redirectOnErr('/settings/connectors', 'settings_connectors'); check_form_security_token_redirectOnErr('/settings/connectors', 'settings_connectors');
if (x($_POST, 'general-submit')) { if (x($_POST, 'general-submit')) {
@ -1116,7 +1115,7 @@ function settings_content(App $a)
if (strlen(Config::get('system', 'directory'))) { if (strlen(Config::get('system', 'directory'))) {
$profile_in_net_dir = replace_macros($opt_tpl, [ $profile_in_net_dir = replace_macros($opt_tpl, [
'$field' => ['profile_in_netdirectory', L10n::t('Publish your default profile in the global social directory?'), $profile['net-publish'], L10n::t('Your profile will be publishedin this node\'s <a href="%s">local directory</a>. Your profile details may be publicly visible depending on the system settings.', System::baseUrl().'/directory'), [L10n::t('No'), L10n::t('Yes')]] '$field' => ['profile_in_netdirectory', L10n::t('Publish your default profile in the global social directory?'), $profile['net-publish'], L10n::t('Your profile will be published in this node\'s <a href="%s">local directory</a>. Your profile details may be publicly visible depending on the system settings.', System::baseUrl().'/directory'), [L10n::t('No'), L10n::t('Yes')]]
]); ]);
} else { } else {
$profile_in_net_dir = ''; $profile_in_net_dir = '';

View File

@ -33,7 +33,7 @@ function unfollow_post(App $a)
if (!DBM::is_result($contact)) { if (!DBM::is_result($contact)) {
notice(L10n::t("Contact wasn't found or can't be unfollowed.")); notice(L10n::t("Contact wasn't found or can't be unfollowed."));
} else { } else {
if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DIASPORA])) { if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_DFRN])) {
$r = q("SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` $r = q("SELECT `contact`.*, `user`.* FROM `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
WHERE `user`.`uid` = %d AND `contact`.`self` LIMIT 1", WHERE `user`.`uid` = %d AND `contact`.`self` LIMIT 1",
intval($uid) intval($uid)
@ -75,7 +75,7 @@ function unfollow_content(App $a)
// NOTREACHED // NOTREACHED
} }
if (!in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS])) { if (!in_array($contact['network'], [NETWORK_DIASPORA, NETWORK_OSTATUS, NETWORK_DFRN])) {
notice(L10n::t("Unfollowing is currently not supported by your network.").EOL); notice(L10n::t("Unfollowing is currently not supported by your network.").EOL);
$submit = ""; $submit = "";
// NOTREACHED // NOTREACHED

View File

@ -7,7 +7,7 @@
namespace Friendica\Content\Text; namespace Friendica\Content\Text;
use DOMDocument; use DOMDocument;
use DomXPath; use DOMXPath;
use Exception; use Exception;
use Friendica\BaseObject; use Friendica\BaseObject;
use Friendica\Content\OEmbed; use Friendica\Content\OEmbed;
@ -680,7 +680,7 @@ class BBCode extends BaseObject
$return = ''; $return = '';
if ($simplehtml == 7) { if ($simplehtml == 7) {
$return = self::convertUrlForMastodon($data["url"]); $return = self::convertUrlForOStatus($data["url"]);
} elseif (($simplehtml != 4) && ($simplehtml != 0)) { } elseif (($simplehtml != 4) && ($simplehtml != 0)) {
$return = sprintf('<a href="%s" target="_blank">%s</a><br>', $data["url"], $data["title"]); $return = sprintf('<a href="%s" target="_blank">%s</a><br>', $data["url"], $data["title"]);
} else { } else {
@ -708,9 +708,10 @@ class BBCode extends BaseObject
} }
if ($data["description"] != "" && $data["description"] != $data["title"]) { if ($data["description"] != "" && $data["description"] != $data["title"]) {
$return .= sprintf('<blockquote>%s</blockquote>', trim(self::convert($data["description"]))); // Sanitize the HTML by converting it to BBCode
$bbcode = HTML::toBBCode($data["description"]);
$return .= sprintf('<blockquote>%s</blockquote>', trim(self::convert($bbcode)));
} }
if ($data["type"] == "link") { if ($data["type"] == "link") {
$return .= sprintf('<sup><a href="%s">%s</a></sup>', $data['url'], parse_url($data['url'], PHP_URL_HOST)); $return .= sprintf('<sup><a href="%s">%s</a></sup>', $data['url'], parse_url($data['url'], PHP_URL_HOST));
} }
@ -757,7 +758,7 @@ class BBCode extends BaseObject
if (($data["url"] != "") && ($data["title"] != "")) { if (($data["url"] != "") && ($data["title"] != "")) {
$text .= "\n[url=" . $data["url"] . "]" . $data["title"] . "[/url]"; $text .= "\n[url=" . $data["url"] . "]" . $data["title"] . "[/url]";
} elseif (($data["url"] != "")) { } elseif (($data["url"] != "")) {
$text .= "\n" . $data["url"]; $text .= "\n[url]" . $data["url"] . "[/url]";
} }
return $text . "\n" . $data["after"]; return $text . "\n" . $data["after"];
@ -770,7 +771,7 @@ class BBCode extends BaseObject
* @param array $match Array with the matching values * @param array $match Array with the matching values
* @return string reformatted link including HTML codes * @return string reformatted link including HTML codes
*/ */
private static function convertUrlForMastodonCallback($match) private static function convertUrlForOStatusCallback($match)
{ {
$url = $match[1]; $url = $match[1];
@ -783,34 +784,27 @@ class BBCode extends BaseObject
return $match[0]; return $match[0];
} }
return self::convertUrlForMastodon($url); return self::convertUrlForOStatus($url);
} }
/** /**
* @brief Converts [url] BBCodes in a format that looks fine on Mastodon and GNU Social. * @brief Converts [url] BBCodes in a format that looks fine on OStatus systems.
* @param string $url URL that is about to be reformatted * @param string $url URL that is about to be reformatted
* @return string reformatted link including HTML codes * @return string reformatted link including HTML codes
*/ */
private static function convertUrlForMastodon($url) private static function convertUrlForOStatus($url)
{ {
$parts = parse_url($url); $parts = parse_url($url);
$scheme = $parts['scheme'] . '://'; $scheme = $parts['scheme'] . '://';
$styled_url = str_replace($scheme, '', $url); $styled_url = str_replace($scheme, '', $url);
$html = '<a href="%s" class="attachment" rel="nofollow noopener" target="_blank">' .
'<span class="invisible">%s</span>';
if (strlen($styled_url) > 30) { if (strlen($styled_url) > 30) {
$html .= '<span class="ellipsis">%s</span>' . $styled_url = substr($styled_url, 0, 30) . "";
'<span class="invisible">%s</span></a>';
$ellipsis = substr($styled_url, 0, 30);
$rest = substr($styled_url, 30);
return sprintf($html, $url, $scheme, $ellipsis, $rest);
} else {
$html .= '%s</a>';
return sprintf($html, $url, $scheme, $styled_url);
} }
$html = '<a href="%s" target="_blank">%s</a>';
return sprintf($html, $url, $styled_url);
} }
/* /*
@ -1105,13 +1099,13 @@ class BBCode extends BaseObject
} }
if (stripos(normalise_link($link), 'http://twitter.com/') === 0) { if (stripos(normalise_link($link), 'http://twitter.com/') === 0) {
$text .= '<br /><a href="' . $link . '">' . $link . '</a>';
} else {
$text .= $headline . '<blockquote>' . trim($share[3]) . "</blockquote><br />"; $text .= $headline . '<blockquote>' . trim($share[3]) . "</blockquote><br />";
if ($link != "") { if ($link != "") {
$text .= '<br /><a href="' . $link . '">[l]</a>'; $text .= '<br /><a href="' . $link . '">[l]</a>';
} }
} else {
$text .= '<br /><a href="' . $link . '">' . $link . '</a>';
} }
break; break;
@ -1207,7 +1201,7 @@ class BBCode extends BaseObject
$doc = new DOMDocument(); $doc = new DOMDocument();
@$doc->loadHTML($body); @$doc->loadHTML($body);
$xpath = new DomXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query("//meta[@name]"); $list = $xpath->query("//meta[@name]");
foreach ($list as $node) { foreach ($list as $node) {
$attr = []; $attr = [];
@ -1439,8 +1433,8 @@ class BBCode extends BaseObject
$autolink_regex = "/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism"; $autolink_regex = "/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism";
$text = preg_replace($autolink_regex, '$1[url]$2[/url]', $text); $text = preg_replace($autolink_regex, '$1[url]$2[/url]', $text);
if ($simple_html == 7) { if ($simple_html == 7) {
$text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForMastodonCallback', $text); $text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForOStatusCallback', $text);
$text = preg_replace_callback("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForMastodonCallback', $text); $text = preg_replace_callback("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", 'self::convertUrlForOStatusCallback', $text);
} }
} else { } else {
$text = preg_replace("(\[url\]([$URLSearchString]*)\[\/url\])ism", " $1 ", $text); $text = preg_replace("(\[url\]([$URLSearchString]*)\[\/url\])ism", " $1 ", $text);
@ -1540,10 +1534,8 @@ class BBCode extends BaseObject
if (strpos($text, '[/map]') !== false) { if (strpos($text, '[/map]') !== false) {
$text = preg_replace_callback( $text = preg_replace_callback(
"/\[map\](.*?)\[\/map\]/ism", "/\[map\](.*?)\[\/map\]/ism",
function ($match) { function ($match) use ($simple_html) {
// the extra space in the following line is intentional return str_replace($match[0], '<p class="map">' . Map::byLocation($match[1], $simple_html) . '</p>', $match[0]);
// Whyyy? - @MrPetovan
return str_replace($match[0], '<div class="map" >' . Map::byLocation($match[1]) . '</div>', $match[0]);
}, },
$text $text
); );
@ -1551,16 +1543,14 @@ class BBCode extends BaseObject
if (strpos($text, '[map=') !== false) { if (strpos($text, '[map=') !== false) {
$text = preg_replace_callback( $text = preg_replace_callback(
"/\[map=(.*?)\]/ism", "/\[map=(.*?)\]/ism",
function ($match) { function ($match) use ($simple_html) {
// the extra space in the following line is intentional return str_replace($match[0], '<p class="map">' . Map::byCoordinates(str_replace('/', ' ', $match[1]), $simple_html) . '</p>', $match[0]);
// Whyyy? - @MrPetovan
return str_replace($match[0], '<div class="map" >' . Map::byCoordinates(str_replace('/', ' ', $match[1])) . '</div>', $match[0]);
}, },
$text $text
); );
} }
if (strpos($text, '[map]') !== false) { if (strpos($text, '[map]') !== false) {
$text = preg_replace("/\[map\]/", '<div class="map"></div>', $text); $text = preg_replace("/\[map\]/", '<p class="map"></p>', $text);
} }
// Check for headers // Check for headers

View File

@ -135,7 +135,7 @@ class NotificationsManager extends BaseObject
public function setSeen($note, $seen = true) public function setSeen($note, $seen = true)
{ {
return q( return q(
"UPDATE `notify` SET `seen` = %d WHERE ( `link` = '%s' OR ( `parent` != 0 AND `parent` = %d AND `otype` = '%s' )) AND `uid` = %d", "UPDATE `notify` SET `seen` = %d WHERE (`link` = '%s' OR (`parent` != 0 AND `parent` = %d AND `otype` = '%s')) AND `uid` = %d",
intval($seen), intval($seen),
dbesc($note['link']), dbesc($note['link']),
intval($note['parent']), intval($note['parent']),
@ -384,16 +384,18 @@ class NotificationsManager extends BaseObject
private function networkTotal($seen = 0) private function networkTotal($seen = 0)
{ {
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT COUNT(*) AS `total` "SELECT COUNT(*) AS `total`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent` FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND WHERE `item`.`visible` AND `pitem`.`parent` != 0 AND
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
$sql_seen", $sql_seen",
intval(local_user()) intval(local_user())
); );
@ -423,18 +425,20 @@ class NotificationsManager extends BaseObject
$total = $this->networkTotal($seen); $total = $this->networkTotal($seen);
$notifs = []; $notifs = [];
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`, "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`, `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid` `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent` FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1 AND `pitem`.`parent` != 0 AND WHERE `item`.`visible` AND `pitem`.`parent` != 0 AND
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
$sql_seen $sql_seen
ORDER BY `item`.`created` DESC LIMIT %d, %d ", ORDER BY `item`.`created` DESC LIMIT %d, %d ",
intval(local_user()), intval(local_user()),
@ -466,7 +470,7 @@ class NotificationsManager extends BaseObject
$sql_seen = ""; $sql_seen = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `seen` = 0 "; $sql_seen = " AND NOT `seen` ";
} }
$r = q( $r = q(
@ -501,7 +505,7 @@ class NotificationsManager extends BaseObject
$sql_seen = ""; $sql_seen = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `seen` = 0 "; $sql_seen = " AND NOT `seen` ";
} }
$r = q( $r = q(
@ -536,7 +540,7 @@ class NotificationsManager extends BaseObject
$myurl = str_replace(['www.', '.'], ['', '\\.'], $myurl); $myurl = str_replace(['www.', '.'], ['', '\\.'], $myurl);
$diasp_url = str_replace('/profile/', '/u/', $myurl); $diasp_url = str_replace('/profile/', '/u/', $myurl);
$sql_extra = sprintf( $sql_extra = sprintf(
" AND ( `item`.`author-link` regexp '%s' OR `item`.`tag` regexp '%s' OR `item`.`tag` regexp '%s' ) ", " AND (`item`.`author-link` REGEXP '%s' OR `item`.`tag` REGEXP '%s' OR `item`.`tag` REGEXP '%s') ",
dbesc($myurl . '$'), dbesc($myurl . '$'),
dbesc($myurl . '\\]'), dbesc($myurl . '\\]'),
dbesc($diasp_url . '\\]') dbesc($diasp_url . '\\]')
@ -555,19 +559,21 @@ class NotificationsManager extends BaseObject
private function personalTotal($seen = 0) private function personalTotal($seen = 0)
{ {
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
$sql_extra = $this->personalSqlExtra(); $sql_extra = $this->personalSqlExtra();
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT COUNT(*) AS `total` "SELECT COUNT(*) AS `total`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent` FROM `item` $index_hint
WHERE `item`.`visible` = 1 WHERE `item`.`visible`
$sql_extra $sql_extra
$sql_seen $sql_seen
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 ", AND NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`",
intval(local_user()) intval(local_user())
); );
if (DBM::is_result($r)) { if (DBM::is_result($r)) {
@ -597,20 +603,22 @@ class NotificationsManager extends BaseObject
$sql_extra = $this->personalSqlExtra(); $sql_extra = $this->personalSqlExtra();
$notifs = []; $notifs = [];
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`, "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`, `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid` `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent` FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1 WHERE `item`.`visible`
$sql_extra $sql_extra
$sql_seen $sql_seen
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0 AND NOT `item`.`deleted` AND `item`.`uid` = %d AND NOT `item`.`wall`
ORDER BY `item`.`created` DESC LIMIT %d, %d ", ORDER BY `item`.`created` DESC LIMIT %d, %d ",
intval(local_user()), intval(local_user()),
intval($start), intval($start),
@ -639,13 +647,15 @@ class NotificationsManager extends BaseObject
private function homeTotal($seen = 0) private function homeTotal($seen = 0)
{ {
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT COUNT(*) AS `total` FROM `item` "SELECT COUNT(*) AS `total` FROM `item` $index_hint
WHERE `item`.`visible` = 1 AND WHERE `item`.`visible` = 1 AND
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1 `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1
$sql_seen", $sql_seen",
@ -677,18 +687,20 @@ class NotificationsManager extends BaseObject
$total = $this->homeTotal($seen); $total = $this->homeTotal($seen);
$notifs = []; $notifs = [];
$sql_seen = ""; $sql_seen = "";
$index_hint = "";
if ($seen === 0) { if ($seen === 0) {
$sql_seen = " AND `item`.`unseen` = 1 "; $sql_seen = " AND `item`.`unseen` ";
$index_hint = "USE INDEX (`uid_unseen_contactid`)";
} }
$r = q( $r = q(
"SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`, "SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`, `item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid` `pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent` FROM `item` $index_hint STRAIGHT_JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1 AND WHERE `item`.`visible` AND
`item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 1 NOT `item`.`deleted` AND `item`.`uid` = %d AND `item`.`wall`
$sql_seen $sql_seen
ORDER BY `item`.`created` DESC LIMIT %d, %d ", ORDER BY `item`.`created` DESC LIMIT %d, %d ",
intval(local_user()), intval(local_user()),

View File

@ -201,7 +201,7 @@ class Worker
$mypid = getmypid(); $mypid = getmypid();
// Quit when in maintenance // Quit when in maintenance
if (Config::get('system', 'maintenance', true)) { if (Config::get('system', 'maintenance', false, true)) {
logger("Maintenance mode - quit process ".$mypid, LOGGER_DEBUG); logger("Maintenance mode - quit process ".$mypid, LOGGER_DEBUG);
return false; return false;
} }

View File

@ -199,12 +199,13 @@ class DBStructure
* *
* @param bool $verbose * @param bool $verbose
* @param bool $action Whether to actually apply the update * @param bool $action Whether to actually apply the update
* @param bool $install Is this the initial update during the installation?
* @param array $tables An array of the database tables * @param array $tables An array of the database tables
* @param array $definition An array of the definition tables * @param array $definition An array of the definition tables
* @return string Empty string if the update is successful, error messages otherwise * @return string Empty string if the update is successful, error messages otherwise
*/ */
public static function update($verbose, $action, array $tables = null, array $definition = null) { public static function update($verbose, $action, $install = false, array $tables = null, array $definition = null) {
if ($action) { if ($action && !$install) {
Config::set('system', 'maintenance', 1); Config::set('system', 'maintenance', 1);
Config::set('system', 'maintenance_reason', L10n::t(': Database update', DBM::date().' '.date('e'))); Config::set('system', 'maintenance_reason', L10n::t(': Database update', DBM::date().' '.date('e')));
} }
@ -455,7 +456,9 @@ class DBStructure
} }
if ($action) { if ($action) {
Config::set('system', 'maintenance_reason', L10n::t('%s: updating %s table.', DBM::date().' '.date('e'), $name)); if (!$install) {
Config::set('system', 'maintenance_reason', L10n::t('%s: updating %s table.', DBM::date().' '.date('e'), $name));
}
// Ensure index conversion to unique removes duplicates // Ensure index conversion to unique removes duplicates
if ($is_unique && ($temp_name != $name)) { if ($is_unique && ($temp_name != $name)) {
@ -505,15 +508,15 @@ class DBStructure
} }
} }
if ($action) { if ($action && !$install) {
Config::set('system', 'maintenance', 0); Config::set('system', 'maintenance', 0);
Config::set('system', 'maintenance_reason', ''); Config::set('system', 'maintenance_reason', '');
}
if ($errors) { if ($errors) {
Config::set('system', 'dbupdate', DB_UPDATE_FAILED); Config::set('system', 'dbupdate', DB_UPDATE_FAILED);
} else { } else {
Config::set('system', 'dbupdate', DB_UPDATE_SUCCESSFUL); Config::set('system', 'dbupdate', DB_UPDATE_SUCCESSFUL);
}
} }
return $errors; return $errors;
@ -1143,6 +1146,7 @@ class DBStructure
"author-link" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "author-link" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"author-avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "author-avatar" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"title" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "title" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"content-warning" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"body" => ["type" => "mediumtext", "comment" => ""], "body" => ["type" => "mediumtext", "comment" => ""],
"app" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""], "app" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
"verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => ""], "verb" => ["type" => "varchar(100)", "not null" => "1", "default" => "", "comment" => ""],

View File

@ -173,20 +173,18 @@ class Contact extends BaseObject
*/ */
public static function terminateFriendship(array $user, array $contact) public static function terminateFriendship(array $user, array $contact)
{ {
if ($contact['network'] === NETWORK_OSTATUS) { if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DFRN])) {
// create an unfollow slap // create an unfollow slap
$item = []; $item = [];
$item['verb'] = NAMESPACE_OSTATUS . "/unfollow"; $item['verb'] = NAMESPACE_OSTATUS . "/unfollow";
$item['follow'] = $contact["url"]; $item['follow'] = $contact["url"];
$slap = OStatus::salmon($item, $user); $slap = OStatus::salmon($item, $user);
if ((x($contact, 'notify')) && (strlen($contact['notify']))) { if (!empty($contact['notify'])) {
Salmon::slapper($user, $contact['notify'], $slap); Salmon::slapper($user, $contact['notify'], $slap);
} }
} elseif ($contact['network'] === NETWORK_DIASPORA) { } elseif ($contact['network'] == NETWORK_DIASPORA) {
Diaspora::sendUnshare($user, $contact); Diaspora::sendUnshare($user, $contact);
} elseif ($contact['network'] === NETWORK_DFRN) {
DFRN::deliver($user, $contact, 'placeholder', 1);
} }
} }
@ -516,7 +514,7 @@ class Contact extends BaseObject
} }
$sparkle = false; $sparkle = false;
if ($contact['network'] === NETWORK_DFRN) { if (($contact['network'] === NETWORK_DFRN) && !$contact['self']) {
$sparkle = true; $sparkle = true;
$profile_link = System::baseUrl() . '/redir/' . $contact['id']; $profile_link = System::baseUrl() . '/redir/' . $contact['id'];
} else { } else {
@ -533,18 +531,21 @@ class Contact extends BaseObject
$profile_link = $profile_link . '?url=profile'; $profile_link = $profile_link . '?url=profile';
} }
if (in_array($contact['network'], [NETWORK_DFRN, NETWORK_DIASPORA])) { if (in_array($contact['network'], [NETWORK_DFRN, NETWORK_DIASPORA]) && !$contact['self']) {
$pm_url = System::baseUrl() . '/message/new/' . $contact['id']; $pm_url = System::baseUrl() . '/message/new/' . $contact['id'];
} }
if ($contact['network'] == NETWORK_DFRN) { if (($contact['network'] == NETWORK_DFRN) && !$contact['self']) {
$poke_link = System::baseUrl() . '/poke/?f=&c=' . $contact['id']; $poke_link = System::baseUrl() . '/poke/?f=&c=' . $contact['id'];
} }
$contact_url = System::baseUrl() . '/contacts/' . $contact['id']; $contact_url = System::baseUrl() . '/contacts/' . $contact['id'];
$posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/posts'; $posts_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/posts';
$contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
if (!$contact['self']) {
$contact_drop_link = System::baseUrl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
}
/** /**
* Menu array: * Menu array:
@ -1168,7 +1169,26 @@ class Contact extends BaseObject
return result; return result;
} }
if ($ret['network'] === NETWORK_DFRN) { // check if we already have a contact
// the poll url is more reliable than the profile url, as we may have
// indirect links or webfinger links
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' AND NOT `pending` LIMIT 1",
intval($uid),
dbesc($ret['poll']),
dbesc(normalise_link($ret['poll'])),
dbesc($ret['network'])
);
if (!DBM::is_result($r)) {
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' AND NOT `pending` LIMIT 1",
intval($uid),
dbesc(normalise_link($url)),
dbesc($ret['network'])
);
}
if (($ret['network'] === NETWORK_DFRN) && !DBM::is_result($r)) {
if ($interactive) { if ($interactive) {
if (strlen($a->path)) { if (strlen($a->path)) {
$myaddr = bin2hex(System::baseUrl() . '/profile/' . $a->user['nickname']); $myaddr = bin2hex(System::baseUrl() . '/profile/' . $a->user['nickname']);
@ -1180,7 +1200,7 @@ class Contact extends BaseObject
// NOTREACHED // NOTREACHED
} }
} elseif (Config::get('system', 'dfrn_only')) { } elseif (Config::get('system', 'dfrn_only') && ($ret['network'] != NETWORK_DFRN)) {
$result['message'] = L10n::t('This site is not configured to allow communications with other networks.') . EOL; $result['message'] = L10n::t('This site is not configured to allow communications with other networks.') . EOL;
$result['message'] != L10n::t('No compatible communication protocols or feeds were discovered.') . EOL; $result['message'] != L10n::t('No compatible communication protocols or feeds were discovered.') . EOL;
return $result; return $result;
@ -1230,25 +1250,6 @@ class Contact extends BaseObject
$writeable = 1; $writeable = 1;
} }
// check if we already have a contact
// the poll url is more reliable than the profile url, as we may have
// indirect links or webfinger links
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `poll` IN ('%s', '%s') AND `network` = '%s' LIMIT 1",
intval($uid),
dbesc($ret['poll']),
dbesc(normalise_link($ret['poll'])),
dbesc($ret['network'])
);
if (!DBM::is_result($r)) {
$r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` = '%s' LIMIT 1",
intval($uid),
dbesc(normalise_link($url)),
dbesc($ret['network'])
);
}
if (DBM::is_result($r)) { if (DBM::is_result($r)) {
// update contact // update contact
$new_relation = (($r[0]['rel'] == CONTACT_IS_FOLLOWER) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING); $new_relation = (($r[0]['rel'] == CONTACT_IS_FOLLOWER) ? CONTACT_IS_FRIEND : CONTACT_IS_SHARING);
@ -1309,16 +1310,16 @@ class Contact extends BaseObject
); );
if (DBM::is_result($r)) { if (DBM::is_result($r)) {
if (($contact['network'] == NETWORK_OSTATUS) && (strlen($contact['notify']))) { if (in_array($contact['network'], [NETWORK_OSTATUS, NETWORK_DFRN])) {
// create a follow slap // create a follow slap
$item = []; $item = [];
$item['verb'] = ACTIVITY_FOLLOW; $item['verb'] = ACTIVITY_FOLLOW;
$item['follow'] = $contact["url"]; $item['follow'] = $contact["url"];
$slap = OStatus::salmon($item, $r[0]); $slap = OStatus::salmon($item, $r[0]);
Salmon::slapper($r[0], $contact['notify'], $slap); if (!empty($contact['notify'])) {
} Salmon::slapper($r[0], $contact['notify'], $slap);
}
if ($contact['network'] == NETWORK_DIASPORA) { } elseif ($contact['network'] == NETWORK_DIASPORA) {
$ret = Diaspora::sendShare($a->user, $contact); $ret = Diaspora::sendShare($a->user, $contact);
logger('share returns: ' . $ret); logger('share returns: ' . $ret);
} }
@ -1377,7 +1378,7 @@ class Contact extends BaseObject
} }
if (is_array($contact)) { if (is_array($contact)) {
if (($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING) if (($contact['rel'] == CONTACT_IS_SHARING)
|| ($sharing && $contact['rel'] == CONTACT_IS_FOLLOWER)) { || ($sharing && $contact['rel'] == CONTACT_IS_FOLLOWER)) {
dba::update('contact', ['rel' => CONTACT_IS_FRIEND, 'writable' => true], dba::update('contact', ['rel' => CONTACT_IS_FRIEND, 'writable' => true],
['id' => $contact['id'], 'uid' => $importer['uid']]); ['id' => $contact['id'], 'uid' => $importer['uid']]);

View File

@ -36,56 +36,53 @@ class GContact
*/ */
public static function searchByName($search, $mode = '') public static function searchByName($search, $mode = '')
{ {
if ($search) { if (empty($search)) {
// check supported networks return [];
if (Config::get('system', 'diaspora_enabled')) {
$diaspora = NETWORK_DIASPORA;
} else {
$diaspora = NETWORK_DFRN;
}
if (!Config::get('system', 'ostatus_disabled')) {
$ostatus = NETWORK_OSTATUS;
} else {
$ostatus = NETWORK_DFRN;
}
// check if we search only communities or every contact
if ($mode === "community") {
$extra_sql = " AND `community`";
} else {
$extra_sql = "";
}
$search .= "%";
$results = q(
"SELECT `contact`.`id` AS `cid`, `gcontact`.`url`, `gcontact`.`name`, `gcontact`.`nick`, `gcontact`.`photo`,
`gcontact`.`network`, `gcontact`.`keywords`, `gcontact`.`addr`, `gcontact`.`community`
FROM `gcontact`
LEFT JOIN `contact` ON `contact`.`nurl` = `gcontact`.`nurl`
AND `contact`.`uid` = %d AND NOT `contact`.`blocked`
AND NOT `contact`.`pending` AND `contact`.`rel` IN ('%s', '%s')
WHERE (`contact`.`id` > 0 OR (NOT `gcontact`.`hide` AND `gcontact`.`network` IN ('%s', '%s', '%s') AND
((`gcontact`.`last_contact` >= `gcontact`.`last_failure`) OR
(`gcontact`.`updated` >= `gcontact`.`last_failure`)))) AND
(`gcontact`.`addr` LIKE '%s' OR `gcontact`.`name` LIKE '%s' OR `gcontact`.`nick` LIKE '%s') $extra_sql
GROUP BY `gcontact`.`nurl`
ORDER BY `gcontact`.`nurl` DESC
LIMIT 1000",
intval(local_user()),
dbesc(CONTACT_IS_SHARING),
dbesc(CONTACT_IS_FRIEND),
dbesc(NETWORK_DFRN),
dbesc($ostatus),
dbesc($diaspora),
dbesc(escape_tags($search)),
dbesc(escape_tags($search)),
dbesc(escape_tags($search))
);
return $results;
} }
// check supported networks
if (Config::get('system', 'diaspora_enabled')) {
$diaspora = NETWORK_DIASPORA;
} else {
$diaspora = NETWORK_DFRN;
}
if (!Config::get('system', 'ostatus_disabled')) {
$ostatus = NETWORK_OSTATUS;
} else {
$ostatus = NETWORK_DFRN;
}
// check if we search only communities or every contact
if ($mode === "community") {
$extra_sql = " AND `community`";
} else {
$extra_sql = "";
}
$search .= "%";
$results = dba::p("SELECT `nurl` FROM `gcontact`
WHERE NOT `hide` AND `network` IN (?, ?, ?) AND
((`last_contact` >= `last_failure`) OR (`updated` >= `last_failure`)) AND
(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql
GROUP BY `nurl` ORDER BY `nurl` DESC LIMIT 1000",
NETWORK_DFRN, $ostatus, $diaspora, $search, $search, $search
);
$gcontacts = [];
while ($result = dba::fetch($results)) {
$urlparts = parse_url($result["nurl"]);
// Ignore results that look strange.
// For historic reasons the gcontact table does contain some garbage.
if (!empty($urlparts['query']) || !empty($urlparts['fragment'])) {
continue;
}
$gcontacts[] = Contact::getDetailsByURL($result["nurl"], local_user());
}
return $gcontacts;
} }
/** /**

View File

@ -22,6 +22,7 @@ use Friendica\Object\Image;
use Friendica\Protocol\Diaspora; use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus; use Friendica\Protocol\OStatus;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\XML;
use dba; use dba;
use Text_LanguageDetect; use Text_LanguageDetect;
@ -355,6 +356,10 @@ class Item extends BaseObject
} }
} }
if (!empty($item['thr-parent'])) {
$item['parent-uri'] = $item['thr-parent'];
}
if (x($item, 'gravity')) { if (x($item, 'gravity')) {
$item['gravity'] = intval($item['gravity']); $item['gravity'] = intval($item['gravity']);
} elseif ($item['parent-uri'] === $item['uri']) { } elseif ($item['parent-uri'] === $item['uri']) {
@ -897,7 +902,11 @@ class Item extends BaseObject
$item['uid'] = 0; $item['uid'] = 0;
$item['origin'] = 0; $item['origin'] = 0;
$item['wall'] = 0; $item['wall'] = 0;
$item['contact-id'] = Contact::getIdForURL($item['author-link']); if ($item['uri'] == $item['parent-uri']) {
$item['contact-id'] = Contact::getIdForURL($item['owner-link']);
} else {
$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"])) {
$item['type'] = 'remote-comment'; $item['type'] = 'remote-comment';
@ -1264,21 +1273,23 @@ class Item extends BaseObject
} }
// now change this copy of the post to a forum head message and deliver to all the tgroup members // now change this copy of the post to a forum head message and deliver to all the tgroup members
$self = dba::selectFirst('contact', ['name', 'url', 'thumb'], ['uid' => $uid, 'self' => true]); $self = dba::selectFirst('contact', ['id', 'name', 'url', 'thumb'], ['uid' => $uid, 'self' => true]);
if (!DBM::is_result($self)) { if (!DBM::is_result($self)) {
return; return;
} }
$owner_id = Contact::getIdForURL($self['url']);
// also reset all the privacy bits to the forum default permissions // also reset all the privacy bits to the forum default permissions
$private = ($user['allow_cid'] || $user['allow_gid'] || $user['deny_cid'] || $user['deny_gid']) ? 1 : 0; $private = ($user['allow_cid'] || $user['allow_gid'] || $user['deny_cid'] || $user['deny_gid']) ? 1 : 0;
$forum_mode = ($prvgroup ? 2 : 1); $forum_mode = ($prvgroup ? 2 : 1);
$fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, $fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $self['id'],
'owner-name' => $self['name'], 'owner-link' => $self['url'], 'owner-avatar' => $self['thumb'], 'owner-id' => $owner_id, 'owner-name' => $self['name'], 'owner-link' => $self['url'],
'private' => $private, 'allow_cid' => $user['allow_cid'], 'allow_gid' => $user['allow_gid'], 'owner-avatar' => $self['thumb'], 'private' => $private, 'allow_cid' => $user['allow_cid'],
'deny_cid' => $user['deny_cid'], 'deny_gid' => $user['deny_gid']]; 'allow_gid' => $user['allow_gid'], 'deny_cid' => $user['deny_cid'], 'deny_gid' => $user['deny_gid']];
dba::update('item', $fields, ['id' => $item_id]); dba::update('item', $fields, ['id' => $item_id]);
self::updateThread($item_id); self::updateThread($item_id);

View File

@ -395,7 +395,7 @@ class User
throw new Exception(L10n::t('Not a valid email address.')); throw new Exception(L10n::t('Not a valid email address.'));
} }
if (dba::exists('user', ['email' => $email])) { if (Config::get('system', 'block_extended_register', false) && dba::exists('user', ['email' => $email])) {
throw new Exception(L10n::t('Cannot use that email.')); throw new Exception(L10n::t('Cannot use that email.'));
} }

View File

@ -2079,8 +2079,8 @@ class DFRN
return false; return false;
} }
$fields = ['title' => $item["title"], 'body' => $item["body"], $fields = ['title' => defaults($item, 'title', ''), 'body' => defaults($item, 'body', ''),
'tag' => $item["tag"], 'changed' => DateTimeFormat::utcNow(), 'tag' => defaults($item, 'tag', ''), 'changed' => DateTimeFormat::utcNow(),
'edited' => DateTimeFormat::utc($item["edited"])]; 'edited' => DateTimeFormat::utc($item["edited"])];
$condition = ["`uri` = ? AND `uid` IN (0, ?)", $item["uri"], $importer["importer_uid"]]; $condition = ["`uri` = ? AND `uid` IN (0, ?)", $item["uri"], $importer["importer_uid"]];

View File

@ -31,6 +31,7 @@ use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network; use Friendica\Util\Network;
use Friendica\Util\XML; use Friendica\Util\XML;
use Friendica\Util\Map;
use dba; use dba;
use SimpleXMLElement; use SimpleXMLElement;
@ -222,11 +223,20 @@ class Diaspora
$signable_data = $msg.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg); $signable_data = $msg.".".base64url_encode($type).".".base64url_encode($encoding).".".base64url_encode($alg);
if ($handle == '') {
logger('No author could be decoded. Discarding. Message: ' . $envelope);
return false;
}
$key = self::key($handle); $key = self::key($handle);
if ($key == '') {
logger("Couldn't get a key for handle " . $handle . ". Discarding.");
return false;
}
$verify = Crypto::rsaVerify($signable_data, $sig, $key); $verify = Crypto::rsaVerify($signable_data, $sig, $key);
if (!$verify) { if (!$verify) {
logger('Message did not verify. Discarding.'); logger('Message from ' . $handle . ' did not verify. Discarding.');
return false; return false;
} }
@ -322,7 +332,16 @@ class Diaspora
// Get the senders' public key // Get the senders' public key
$key_id = $base->sig[0]->attributes()->key_id[0]; $key_id = $base->sig[0]->attributes()->key_id[0];
$author_addr = base64_decode($key_id); $author_addr = base64_decode($key_id);
if ($author_addr == '') {
logger('No author could be decoded. Discarding. Message: ' . $xml);
System::httpExit(400);
}
$key = self::key($author_addr); $key = self::key($author_addr);
if ($key == '') {
logger("Couldn't get a key for handle " . $author_addr . ". Discarding.");
System::httpExit(400);
}
$verify = Crypto::rsaVerify($signed_data, $signature, $key); $verify = Crypto::rsaVerify($signed_data, $signature, $key);
if (!$verify) { if (!$verify) {
@ -2212,7 +2231,10 @@ class Diaspora
} }
logger('Received participation for ID: '.$item['id'].' - Contact: '.$contact_id.' - Server: '.$server, LOGGER_DEBUG); logger('Received participation for ID: '.$item['id'].' - Contact: '.$contact_id.' - Server: '.$server, LOGGER_DEBUG);
dba::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]);
if (!dba::exists('participation', ['iid' => $item['id'], 'server' => $server])) {
dba::insert('participation', ['iid' => $item['id'], 'cid' => $contact_id, 'fid' => $person['id'], 'server' => $server]);
}
// Send all existing comments and likes to the requesting server // Send all existing comments and likes to the requesting server
$comments = dba::p("SELECT `item`.`id`, `item`.`verb`, `contact`.`self` $comments = dba::p("SELECT `item`.`id`, `item`.`verb`, `contact`.`self`
@ -3197,13 +3219,14 @@ class Diaspora
} }
$logid = random_string(4); $logid = random_string(4);
$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
// Fetch the fcontact entry when there is missing data // We always try to use the data from the fcontact table.
// Will possibly happen when data is transmitted to a DFRN contact // This is important for transmitting data to Friendica servers.
if (empty($dest_url) && !empty($contact['addr'])) { if (!empty($contact['addr'])) {
$fcontact = self::personByHandle($contact['addr']); $fcontact = self::personByHandle($contact['addr']);
$dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]); $dest_url = ($public_batch ? $fcontact["batch"] : $fcontact["notify"]);
} else {
$dest_url = ($public_batch ? $contact["batch"] : $contact["notify"]);
} }
if (!$dest_url) { if (!$dest_url) {
@ -3597,10 +3620,18 @@ class Diaspora
$eventdata['description'] = html_entity_decode(BBCode::toMarkdown($event['desc'])); $eventdata['description'] = html_entity_decode(BBCode::toMarkdown($event['desc']));
} }
if ($event['location']) { if ($event['location']) {
$event['location'] = preg_replace("/\[map\](.*?)\[\/map\]/ism", '$1', $event['location']);
$coord = Map::getCoordinates($event['location']);
$location = []; $location = [];
$location["address"] = html_entity_decode(BBCode::toMarkdown($event['location'])); $location["address"] = html_entity_decode(BBCode::toMarkdown($event['location']));
$location["lat"] = 0; if (!empty($coord['lat']) && !empty($coord['lon'])) {
$location["lng"] = 0; $location["lat"] = $coord['lat'];
$location["lng"] = $coord['lon'];
} else {
$location["lat"] = 0;
$location["lng"] = 0;
}
$eventdata['location'] = $location; $eventdata['location'] = $location;
} }
@ -3694,7 +3725,13 @@ class Diaspora
if (count($event)) { if (count($event)) {
$message['event'] = $event; $message['event'] = $event;
/// @todo Once Diaspora supports it, we will remove the body if (!empty($event['location']['address']) &&
!empty($event['location']['lat']) &&
!empty($event['location']['lng'])) {
$message['location'] = $event['location'];
}
/// @todo Once Diaspora supports it, we will remove the body and the location hack above
// $message['text'] = ''; // $message['text'] = '';
} }
} }

View File

@ -86,9 +86,9 @@ class Feed {
if ($xpath->query('/atom:feed')->length > 0) { if ($xpath->query('/atom:feed')->length > 0) {
$alternate = $xpath->query("atom:link[@rel='alternate']")->item(0)->attributes; $alternate = $xpath->query("atom:link[@rel='alternate']")->item(0)->attributes;
if (is_object($alternate)) { if (is_object($alternate)) {
foreach ($alternate AS $attributes) { foreach ($alternate AS $attribute) {
if ($attributes->name == "href") { if ($attribute->name == "href") {
$author["author-link"] = $attributes->textContent; $author["author-link"] = $attribute->textContent;
} }
} }
} }
@ -99,9 +99,9 @@ class Feed {
if ($author["author-link"] == "") { if ($author["author-link"] == "") {
$self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes; $self = $xpath->query("atom:link[@rel='self']")->item(0)->attributes;
if (is_object($self)) { if (is_object($self)) {
foreach ($self AS $attributes) { foreach ($self AS $attribute) {
if ($attributes->name == "href") { if ($attribute->name == "href") {
$author["author-link"] = $attributes->textContent; $author["author-link"] = $attribute->textContent;
} }
} }
} }
@ -141,9 +141,9 @@ class Feed {
} }
$avatar = $xpath->evaluate("atom:author/atom:link[@rel='avatar']")->item(0)->attributes; $avatar = $xpath->evaluate("atom:author/atom:link[@rel='avatar']")->item(0)->attributes;
if (is_object($avatar)) { if (is_object($avatar)) {
foreach ($avatar AS $attributes) { foreach ($avatar AS $attribute) {
if ($attributes->name == "href") { if ($attribute->name == "href") {
$author["author-avatar"] = $attributes->textContent; $author["author-avatar"] = $attribute->textContent;
} }
} }
} }
@ -208,13 +208,10 @@ class Feed {
} }
$items = []; $items = [];
// Importing older entries first
for($i = $entries->length - 1; $i >= 0;--$i) {
$entry = $entries->item($i);
$entrylist = [];
foreach ($entries AS $entry) {
$entrylist[] = $entry;
}
foreach (array_reverse($entrylist) AS $entry) {
$item = array_merge($header, $author); $item = array_merge($header, $author);
$alternate = $xpath->query("atom:link[@rel='alternate']", $entry)->item(0)->attributes; $alternate = $xpath->query("atom:link[@rel='alternate']", $entry)->item(0)->attributes;
@ -222,9 +219,9 @@ class Feed {
$alternate = $xpath->query("atom:link", $entry)->item(0)->attributes; $alternate = $xpath->query("atom:link", $entry)->item(0)->attributes;
} }
if (is_object($alternate)) { if (is_object($alternate)) {
foreach ($alternate AS $attributes) { foreach ($alternate AS $attribute) {
if ($attributes->name == "href") { if ($attribute->name == "href") {
$item["plink"] = $attributes->textContent; $item["plink"] = $attribute->textContent;
} }
} }
} }
@ -310,20 +307,20 @@ class Feed {
$attachments = []; $attachments = [];
$enclosures = $xpath->query("enclosure", $entry); $enclosures = $xpath->query("enclosure|atom:link[@rel='enclosure']", $entry);
foreach ($enclosures AS $enclosure) { foreach ($enclosures AS $enclosure) {
$href = ""; $href = "";
$length = ""; $length = "";
$type = ""; $type = "";
$title = ""; $title = "";
foreach ($enclosure->attributes AS $attributes) { foreach ($enclosure->attributes AS $attribute) {
if ($attributes->name == "url") { if (in_array($attribute->name, ["url", "href"])) {
$href = $attributes->textContent; $href = $attribute->textContent;
} elseif ($attributes->name == "length") { } elseif ($attribute->name == "length") {
$length = $attributes->textContent; $length = $attribute->textContent;
} elseif ($attributes->name == "type") { } elseif ($attribute->name == "type") {
$type = $attributes->textContent; $type = $attribute->textContent;
} }
} }
if (strlen($item["attach"])) { if (strlen($item["attach"])) {

View File

@ -72,8 +72,9 @@ class OStatus
$contact = null; $contact = null;
if ($aliaslink != '') { if ($aliaslink != '') {
$condition = ["`uid` = ? AND `alias` = ? AND `network` != ?", $condition = ["`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer["uid"], $aliaslink, NETWORK_STATUSNET]; $importer["uid"], $aliaslink, NETWORK_STATUSNET,
CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
$contact = dba::selectFirst('contact', [], $condition); $contact = dba::selectFirst('contact', [], $condition);
} }
@ -82,14 +83,16 @@ class OStatus
$aliaslink = $author["author-link"]; $aliaslink = $author["author-link"];
} }
$condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ?", $importer["uid"], $condition = ["`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
normalise_link($author["author-link"]), normalise_link($aliaslink), NETWORK_STATUSNET]; $importer["uid"], normalise_link($author["author-link"]), normalise_link($aliaslink),
NETWORK_STATUSNET, CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
$contact = dba::selectFirst('contact', [], $condition); $contact = dba::selectFirst('contact', [], $condition);
} }
if (!DBM::is_result($contact) && ($addr != '')) { if (!DBM::is_result($contact) && ($addr != '')) {
$condition = ["`uid` = ? AND `addr` = ? AND `network` != ?", $condition = ["`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
$importer["uid"], $addr, NETWORK_STATUSNET]; $importer["uid"], $addr, NETWORK_STATUSNET,
CONTACT_IS_SHARING, CONTACT_IS_FRIEND];
$contact = dba::selectFirst('contact', [], $condition); $contact = dba::selectFirst('contact', [], $condition);
} }
@ -243,13 +246,12 @@ class OStatus
$xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS); $xpath->registerNamespace('ostatus', NAMESPACE_OSTATUS);
$xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET); $xpath->registerNamespace('statusnet', NAMESPACE_STATUSNET);
$entries = $xpath->query('/atom:entry'); $contact = ["id" => 0];
foreach ($entries as $entry) { // Fetch the first author
// fetch the author $authordata = $xpath->query('//author')->item(0);
$author = self::fetchAuthor($xpath, $entry, $importer, $contact, true); $author = self::fetchAuthor($xpath, $authordata, $importer, $contact, true);
return $author; return $author;
}
} }
/** /**
@ -658,8 +660,9 @@ class OStatus
// Mastodon Content Warning // Mastodon Content Warning
if (($item["verb"] == ACTIVITY_POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) { if (($item["verb"] == ACTIVITY_POST) && $xpath->evaluate('boolean(atom:summary)', $entry)) {
$clear_text = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue; $clear_text = $xpath->query('atom:summary/text()', $entry)->item(0)->nodeValue;
if (!empty($clear_text)) {
$item["body"] = HTML::toBBCode($clear_text) . '[spoiler]' . $item["body"] . '[/spoiler]'; $item['content-warning'] = HTML::toBBCode($clear_text);
}
} }
if (($self != '') && empty($item['protocol'])) { if (($self != '') && empty($item['protocol'])) {
@ -670,9 +673,11 @@ class OStatus
self::fetchConversation($item['conversation-href'], $item['conversation-uri']); self::fetchConversation($item['conversation-href'], $item['conversation-uri']);
} }
if (isset($item["parent-uri"]) && ($related != '')) { if (isset($item["parent-uri"])) {
if (!dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item['parent-uri']])) { if (!dba::exists('item', ['uid' => $importer["uid"], 'uri' => $item['parent-uri']])) {
self::fetchRelated($related, $item["parent-uri"], $importer); if ($related != '') {
self::fetchRelated($related, $item["parent-uri"], $importer);
}
} else { } else {
logger('Reply with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.', LOGGER_DEBUG); logger('Reply with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.', LOGGER_DEBUG);
} }
@ -1283,6 +1288,13 @@ class OStatus
"rel" => "self", "type" => "application/atom+xml"]; "rel" => "self", "type" => "application/atom+xml"];
XML::addElement($doc, $root, "link", "", $attributes); XML::addElement($doc, $root, "link", "", $attributes);
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
$condition = ['uid' => $owner['uid'], 'self' => false, 'pending' => false,
'archive' => false, 'hidden' => false, 'blocked' => false];
$members = dba::count('contact', $condition);
XML::addElement($doc, $root, "statusnet:group_info", "", ["member_count" => $members]);
}
return $root; return $root;
} }
@ -1374,16 +1386,22 @@ class OStatus
* *
* @return object author element * @return object author element
*/ */
private static function addAuthor($doc, $owner) private static function addAuthor($doc, $owner, $show_profile = true)
{ {
$profile = dba::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid'], 'is-default' => true]); $profile = dba::selectFirst('profile', ['homepage', 'publish'], ['uid' => $owner['uid'], 'is-default' => true]);
$author = $doc->createElement("author"); $author = $doc->createElement("author");
XML::addElement($doc, $author, "id", $owner["url"]); XML::addElement($doc, $author, "id", $owner["url"]);
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON); if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_GROUP);
} else {
XML::addElement($doc, $author, "activity:object-type", ACTIVITY_OBJ_PERSON);
}
XML::addElement($doc, $author, "uri", $owner["url"]); XML::addElement($doc, $author, "uri", $owner["url"]);
XML::addElement($doc, $author, "name", $owner["nick"]); XML::addElement($doc, $author, "name", $owner["nick"]);
XML::addElement($doc, $author, "email", $owner["addr"]); XML::addElement($doc, $author, "email", $owner["addr"]);
XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, 7)); if ($show_profile) {
XML::addElement($doc, $author, "summary", BBCode::convert($owner["about"], false, 7));
}
$attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]]; $attributes = ["rel" => "alternate", "type" => "text/html", "href" => $owner["url"]];
XML::addElement($doc, $author, "link", "", $attributes); XML::addElement($doc, $author, "link", "", $attributes);
@ -1408,15 +1426,17 @@ class OStatus
XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]); XML::addElement($doc, $author, "poco:preferredUsername", $owner["nick"]);
XML::addElement($doc, $author, "poco:displayName", $owner["name"]); XML::addElement($doc, $author, "poco:displayName", $owner["name"]);
XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, 7)); if ($show_profile) {
XML::addElement($doc, $author, "poco:note", BBCode::convert($owner["about"], false, 7));
if (trim($owner["location"]) != "") { if (trim($owner["location"]) != "") {
$element = $doc->createElement("poco:address"); $element = $doc->createElement("poco:address");
XML::addElement($doc, $element, "poco:formatted", $owner["location"]); XML::addElement($doc, $element, "poco:formatted", $owner["location"]);
$author->appendChild($element); $author->appendChild($element);
}
} }
if (DBM::is_result($profile)) { if (DBM::is_result($profile) && !$show_profile) {
if (trim($profile["homepage"]) != "") { if (trim($profile["homepage"]) != "") {
$urls = $doc->createElement("poco:urls"); $urls = $doc->createElement("poco:urls");
XML::addElement($doc, $urls, "poco:type", "homepage"); XML::addElement($doc, $urls, "poco:type", "homepage");
@ -1427,11 +1447,12 @@ class OStatus
XML::addElement($doc, $author, "followers", "", ["url" => System::baseUrl()."/viewcontacts/".$owner["nick"]]); XML::addElement($doc, $author, "followers", "", ["url" => System::baseUrl()."/viewcontacts/".$owner["nick"]]);
XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]); XML::addElement($doc, $author, "statusnet:profile_info", "", ["local_id" => $owner["uid"]]);
if ($profile["publish"]) {
XML::addElement($doc, $author, "mastodon:scope", "public");
}
} }
if ($profile["publish"]) {
XML::addElement($doc, $author, "mastodon:scope", "public");
}
return $author; return $author;
} }
@ -1593,7 +1614,7 @@ class OStatus
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
} }
$title = self::entryHeader($doc, $entry, $owner, $toplevel); $title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
$r = q( $r = q(
"SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1", "SELECT * FROM `item` WHERE `uid` = %d AND `guid` = '%s' AND NOT `private` AND `network` IN ('%s', '%s', '%s') LIMIT 1",
@ -1622,7 +1643,7 @@ class OStatus
self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false); self::entryContent($doc, $as_object, $repeated_item, $owner, "", "", false);
$author = self::addAuthor($doc, $contact); $author = self::addAuthor($doc, $contact, false);
$as_object->appendChild($author); $as_object->appendChild($author);
$as_object2 = $doc->createElement("activity:object"); $as_object2 = $doc->createElement("activity:object");
@ -1664,7 +1685,7 @@ class OStatus
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
} }
$title = self::entryHeader($doc, $entry, $owner, $toplevel); $title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
$verb = NAMESPACE_ACTIVITY_SCHEMA."favorite"; $verb = NAMESPACE_ACTIVITY_SCHEMA."favorite";
self::entryContent($doc, $entry, $item, $owner, "Favorite", $verb, false); self::entryContent($doc, $entry, $item, $owner, "Favorite", $verb, false);
@ -1787,7 +1808,7 @@ class OStatus
$item["body"] = sprintf($message, $owner["nick"], $contact["nick"]); $item["body"] = sprintf($message, $owner["nick"], $contact["nick"]);
self::entryHeader($doc, $entry, $owner, $toplevel); self::entryHeader($doc, $entry, $owner, $item, $toplevel);
self::entryContent($doc, $entry, $item, $owner, $title); self::entryContent($doc, $entry, $item, $owner, $title);
@ -1815,7 +1836,7 @@ class OStatus
logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG); logger("OStatus entry is from author ".$owner["url"]." - not from ".$item["author-link"].". Quitting.", LOGGER_DEBUG);
} }
$title = self::entryHeader($doc, $entry, $owner, $toplevel); $title = self::entryHeader($doc, $entry, $owner, $item, $toplevel);
XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE); XML::addElement($doc, $entry, "activity:object-type", ACTIVITY_OBJ_NOTE);
@ -1836,12 +1857,18 @@ class OStatus
* *
* @return string The title for the element * @return string The title for the element
*/ */
private static function entryHeader($doc, &$entry, $owner, $toplevel) private static function entryHeader($doc, &$entry, $owner, $item, $toplevel)
{ {
/// @todo Check if this title stuff is really needed (I guess not) /// @todo Check if this title stuff is really needed (I guess not)
if (!$toplevel) { if (!$toplevel) {
$entry = $doc->createElement("entry"); $entry = $doc->createElement("entry");
$title = sprintf("New note by %s", $owner["nick"]); $title = sprintf("New note by %s", $owner["nick"]);
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
$contact = self::contactEntry($item['author-link'], $owner);
$author = self::addAuthor($doc, $contact, false);
$entry->appendChild($author);
}
} else { } else {
$entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry"); $entry = $doc->createElementNS(NAMESPACE_ATOM1, "entry");
@ -1996,12 +2023,10 @@ class OStatus
$mentioned = $newmentions; $mentioned = $newmentions;
foreach ($mentioned as $mention) { foreach ($mentioned as $mention) {
$r = q( $condition = ['uid' => $owner['uid'], 'nurl' => normalise_link($mention)];
"SELECT `forum`, `prv` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s'", $contact = dba::selectFirst('contact', ['forum', 'prv', 'self', 'contact-type'], $condition);
intval($owner["uid"]), if ($contact["forum"] || $contact["prv"] || ($owner['contact-type'] == ACCOUNT_TYPE_COMMUNITY) ||
dbesc(normalise_link($mention)) ($contact['self'] && ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY))) {
);
if ($r[0]["forum"] || $r[0]["prv"]) {
XML::addElement($doc, $entry, "link", "", XML::addElement($doc, $entry, "link", "",
[ [
"rel" => "mentioned", "rel" => "mentioned",
@ -2018,6 +2043,12 @@ class OStatus
} }
} }
if ($owner['account-type'] == ACCOUNT_TYPE_COMMUNITY) {
XML::addElement($doc, $entry, "link", "", ["rel" => "mentioned",
"ostatus:object-type" => "http://activitystrea.ms/schema/1.0/group",
"href" => $owner['url']]);
}
if (!$item["private"]) { if (!$item["private"]) {
XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention", XML::addElement($doc, $entry, "link", "", ["rel" => "ostatus:attention",
"href" => "http://activityschema.org/collection/public"]); "href" => "http://activityschema.org/collection/public"]);
@ -2094,7 +2125,7 @@ class OStatus
} }
$owner = dba::fetch_first( $owner = dba::fetch_first(
"SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` "SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`, `user`.`account-type`
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
WHERE `contact`.`self` AND `user`.`nickname` = ? LIMIT 1", WHERE `contact`.`self` AND `user`.`nickname` = ? LIMIT 1",
$owner_nick $owner_nick
@ -2119,22 +2150,23 @@ class OStatus
$sql_extra .= sprintf(" AND `item`.`object-type` = '%s' ", dbesc(ACTIVITY_OBJ_COMMENT)); $sql_extra .= sprintf(" AND `item`.`object-type` = '%s' ", dbesc(ACTIVITY_OBJ_COMMENT));
} }
if ($owner['account-type'] != ACCOUNT_TYPE_COMMUNITY) {
$sql_extra .= sprintf(" AND `item`.`contact-id` = %d AND `item`.`author-id` = %d ", intval($owner["id"]), intval($authorid));
}
$items = q( $items = q(
"SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`) "SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`)
STRAIGHT_JOIN `thread` ON `thread`.`iid` = `item`.`parent` STRAIGHT_JOIN `thread` ON `thread`.`iid` = `item`.`parent`
WHERE `item`.`uid` = %d WHERE `item`.`uid` = %d
AND `item`.`contact-id` = %d
AND `item`.`author-id` = %d
AND `item`.`created` > '%s' AND `item`.`created` > '%s'
AND NOT `item`.`deleted` AND NOT `item`.`deleted`
AND NOT `item`.`private` AND NOT `item`.`private`
AND `item`.`visible` AND `item`.`visible`
AND `item`.`wall`
AND `thread`.`network` IN ('%s', '%s') AND `thread`.`network` IN ('%s', '%s')
$sql_extra $sql_extra
ORDER BY `item`.`created` DESC LIMIT %d", ORDER BY `item`.`created` DESC LIMIT %d",
intval($owner["uid"]), intval($owner["uid"]),
intval($owner["id"]),
intval($authorid),
dbesc($check_date), dbesc($check_date),
dbesc(NETWORK_OSTATUS), dbesc(NETWORK_OSTATUS),
dbesc(NETWORK_DFRN), dbesc(NETWORK_DFRN),

View File

@ -945,6 +945,15 @@ class PortableContact
$register_policy = $gserver["register_policy"]; $register_policy = $gserver["register_policy"];
$registered_users = $gserver["registered-users"]; $registered_users = $gserver["registered-users"];
// See discussion under https://forum.friendi.ca/display/0b6b25a8135aabc37a5a0f5684081633
// It can happen that a zero date is in the database, but storing it again is forbidden.
if ($last_contact < NULL_DATE) {
$last_contact = NULL_DATE;
}
if ($last_failure < NULL_DATE) {
$last_failure = NULL_DATE;
}
if (!$force && !self::updateNeeded($gserver["created"], "", $last_failure, $last_contact)) { if (!$force && !self::updateNeeded($gserver["created"], "", $last_failure, $last_contact)) {
logger("Use cached data for server ".$server_url, LOGGER_DEBUG); logger("Use cached data for server ".$server_url, LOGGER_DEBUG);
return ($last_contact >= $last_failure); return ($last_contact >= $last_failure);
@ -1302,7 +1311,7 @@ class PortableContact
if (isset($data->version)) { if (isset($data->version)) {
$network = NETWORK_DFRN; $network = NETWORK_DFRN;
$noscrape = $data->no_scrape_url; $noscrape = defaults($data->no_scrape_url, '');
$version = $data->version; $version = $data->version;
$site_name = $data->site_name; $site_name = $data->site_name;
$info = $data->info; $info = $data->info;

View File

@ -10,17 +10,23 @@ use Friendica\Core\Addon;
* Leaflet Map related functions * Leaflet Map related functions
*/ */
class Map { class Map {
public static function byCoordinates($coord) { public static function byCoordinates($coord, $html_mode = 0) {
$coord = trim($coord); $coord = trim($coord);
$coord = str_replace([',','/',' '],[' ',' ',' '],$coord); $coord = str_replace([',','/',' '],[' ',' ',' '],$coord);
$arr = ['lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'html' => '']; $arr = ['lat' => trim(substr($coord,0,strpos($coord,' '))), 'lon' => trim(substr($coord,strpos($coord,' ')+1)), 'mode' => $html_mode, 'html' => ''];
Addon::callHooks('generate_map',$arr); Addon::callHooks('generate_map',$arr);
return ($arr['html']) ? $arr['html'] : $coord; return ($arr['html']) ? $arr['html'] : $coord;
} }
public static function byLocation($location) { public static function byLocation($location, $html_mode = 0) {
$arr = ['location' => $location, 'html' => '']; $arr = ['location' => $location, 'mode' => $html_mode, 'html' => ''];
Addon::callHooks('generate_named_map',$arr); Addon::callHooks('generate_named_map',$arr);
return ($arr['html']) ? $arr['html'] : $location; return ($arr['html']) ? $arr['html'] : $location;
} }
public static function getCoordinates($location) {
$arr = ['location' => $location, 'lat' => false, 'lon' => false];
Addon::callHooks('Map::getCoordinates', $arr);
return $arr;
}
} }

View File

@ -217,7 +217,7 @@ class Network
$newurl = $curl_info['redirect_url']; $newurl = $curl_info['redirect_url'];
if (($new_location_info['path'] == '') && ( $new_location_info['host'] != '')) { if (($new_location_info['path'] == '') && ($new_location_info['host'] != '')) {
$newurl = $new_location_info['scheme'] . '://' . $new_location_info['host'] . $old_location_info['path']; $newurl = $new_location_info['scheme'] . '://' . $new_location_info['host'] . $old_location_info['path'];
} }
@ -229,6 +229,11 @@ class Network
if (strpos($newurl, '/') === 0) { if (strpos($newurl, '/') === 0) {
$newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl; $newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl;
} }
$old_location_query = @parse_url($url, PHP_URL_QUERY);
if ($old_location_query != '') {
$newurl .= '?' . $old_location_query;
}
if (filter_var($newurl, FILTER_VALIDATE_URL)) { if (filter_var($newurl, FILTER_VALIDATE_URL)) {
$redirects++; $redirects++;

View File

@ -582,11 +582,12 @@ class OnePoll
logger("Consume feed of contact ".$contact['id']); logger("Consume feed of contact ".$contact['id']);
consume_feed($xml, $importer, $contact, $hub, 1, 1); consume_feed($xml, $importer, $contact, $hub);
// do it twice. Ensures that children of parents which may be later in the stream aren't tossed // do it a second time for DFRN so that any children find their parents.
if ($contact['network'] === NETWORK_DFRN) {
consume_feed($xml, $importer, $contact, $hub, 1, 2); consume_feed($xml, $importer, $contact, $hub);
}
$hubmode = 'subscribe'; $hubmode = 'subscribe';
if ($contact['network'] === NETWORK_DFRN || $contact['blocked'] || $contact['readonly']) { if ($contact['network'] === NETWORK_DFRN || $contact['blocked'] || $contact['readonly']) {

View File

@ -16,6 +16,7 @@ Andrej Stieben
André Alves André Alves
André Lohan André Lohan
Andy H3 Andy H3
Andy Hee
AndyHee AndyHee
Anthronaut Anthronaut
Arian - Cazare Muncitori Arian - Cazare Muncitori
@ -75,6 +76,7 @@ Frederico Gonçalves Guimarães
Gerhard Seeber Gerhard Seeber
gerhard6380 gerhard6380
Gert Cauwenberg Gert Cauwenberg
GLComo
greeneyedred greeneyedred
Gregory Smith Gregory Smith
Haakon Meland Eriksen Haakon Meland Eriksen
@ -91,7 +93,6 @@ Jak
Jakob Jakob
Jens Tautenhahn Jens Tautenhahn
jensp jensp
Jeroen S
jeroenpraat jeroenpraat
Johannes Schwab Johannes Schwab
John Brazil John Brazil
@ -169,6 +170,7 @@ Silke Meyer
Simon L'nu Simon L'nu
Simó Albert i Beltran Simó Albert i Beltran
soko1 soko1
St John Karp
Stanislav N. Stanislav N.
StefOfficiel StefOfficiel
Sveinn í Felli Sveinn í Felli
@ -206,4 +208,5 @@ Zered
zotlabs zotlabs
zottel zottel
Zvi ben Yaakov (a.k.a rdc) Zvi ben Yaakov (a.k.a rdc)
Михаил Михаил
朱陈锬

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

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

@ -7,6 +7,7 @@
<form action="{{$baseurl}}/delegate" method="post"> <form action="{{$baseurl}}/delegate" method="post">
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'> <input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
{{include file="field_select.tpl" field=$parent_user}} {{include file="field_select.tpl" field=$parent_user}}
{{include file="field_password.tpl" field=$parent_password}}
<div class="submit"><input type="submit" name="delegate" value="{{$submit|escape:'html'}}" /></div> <div class="submit"><input type="submit" name="delegate" value="{{$submit|escape:'html'}}" /></div>
</form> </form>
</div> </div>
@ -16,23 +17,6 @@
<div id="delegate-desc" class="delegate-desc">{{$desc}}</div> <div id="delegate-desc" class="delegate-desc">{{$desc}}</div>
{{if $managers}}
<h4>{{$head_managers}}</h4>
{{foreach $managers as $x}}
<div class="contact-block-div">
<a class="contact-block-link" href="#" >
<img class="contact-block-img" src="{{$base}}/photo/thumb/{{$x.uid}}" title="{{$x.username}} ({{$x.nickname}})" />
</a>
</div>
{{/foreach}}
<div class="clear"></div>
<hr />
{{/if}}
<h4>{{$head_delegates}}</h4> <h4>{{$head_delegates}}</h4>
{{if $delegates}} {{if $delegates}}

View File

@ -1,4 +1,6 @@
{{include file='field_input.tpl' field=$field}}<script type="text/javascript"> {{include file='field_input.tpl' field=$field}}
<script type="text/javascript">
$(function () { $(function () {
$('#id_{{$field.0}}').datetimepicker({ $('#id_{{$field.0}}').datetimepicker({
step: 5, step: 5,
@ -17,6 +19,10 @@
dayOfWeekStart: {{$datetimepicker.firstDay}}, dayOfWeekStart: {{$datetimepicker.firstDay}},
lang: '{{$datetimepicker.lang}}' lang: '{{$datetimepicker.lang}}'
}); });
{{if $datetimepicker.lang}}
jQuery.datetimepicker.setLocale('{{$datetimepicker.lang}}');
{{/if}}
{{if $datetimepicker.minfrom }} {{if $datetimepicker.minfrom }}
$('#id_{{$datetimepicker.minfrom}}').data('xdsoft_datetimepicker').setOptions({ $('#id_{{$datetimepicker.minfrom}}').data('xdsoft_datetimepicker').setOptions({
onChangeDateTime: function (currentDateTime) { onChangeDateTime: function (currentDateTime) {

View File

@ -85,7 +85,7 @@ $a->config['system']['rino_encrypt'] = {{$rino}};
// default system theme // default system theme
$a->config['system']['theme'] = 'vier'; $a->config['system']['theme'] = 'vier';
$a->config['system']['allowed_themes'] = 'vier,quattro,duepuntozero,smoothly'; $a->config['system']['allowed_themes'] = 'vier,quattro,duepuntozero,smoothly,frio';
// By default allow pseudonyms // By default allow pseudonyms

View File

@ -22,7 +22,7 @@
<p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p> <p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p>
<div class="settings-submit-wrapper" ><input type="submit" name="general-submit" class="settings-submit" value="{{$submit}}" /></div> <div class="settings-submit-wrapper" ><input type="submit" id="general-submit" name="general-submit" class="settings-submit" value="{{$submit}}" /></div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>

View File

@ -32,7 +32,7 @@ function get_schema_info($schema){
'description' => "", 'description' => "",
'author' => [], 'author' => [],
'version' => "", 'version' => "",
'overwrites' => "" 'overwrites' => []
]; ];
if (!is_file($themepath . "schema/" . $schema . ".php")) return $info; if (!is_file($themepath . "schema/" . $schema . ".php")) return $info;

View File

@ -28,7 +28,7 @@
<p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p> <p><a href="{{$repair_ostatus_url}}">{{$repair_ostatus_text}}</a></p>
<div class="form-group pull-right settings-submit-wrapper" > <div class="form-group pull-right settings-submit-wrapper" >
<button type="submit" name="submit" class="btn btn-primary" value="{{$submit|escape:'html'}}">{{$submit}}</button> <button type="submit" id="general-submit" name="general-submit" class="btn btn-primary" value="{{$submit|escape:'html'}}">{{$submit}}</button>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
@ -67,4 +67,4 @@
{{/if}} {{/if}}
</form> </form>
</div> </div>

View File

@ -18,32 +18,22 @@ if (empty($style)) {
$style = "plus"; $style = "plus";
} }
if ($style == "flat") { $stylecss = '';
$stylecssfile = 'view/theme/vier/flat.css'; $modified = '';
} else if ($style == "netcolour") {
$stylecssfile = 'view/theme/vier/netcolour.css'; foreach (['style', $style] as $file) {
} else if ($style == "breathe") { $stylecssfile = $THEMEPATH . DIRECTORY_SEPARATOR . $file .'.css';
$stylecssfile = 'view/theme/vier/breathe.css'; if (file_exists($stylecssfile)) {
} else if ($style == "plus") { $stylecss .= file_get_contents($stylecssfile);
$stylecssfile = 'view/theme/vier/plus.css'; $stylemodified = filemtime($stylecssfile);
} else if ($style == "dark") { if ($stylemodified > $modified) {
$stylecssfile = 'view/theme/vier/dark.css'; $modified = $stylemodified;
} else if ($style == "plusminus") { }
$stylecssfile = 'view/theme/vier/plusminus.css'; } else {
//TODO: use LOGGER_ERROR?
logger('Error: missing file: "' . $stylecssfile .'" (userid: '. $uid .')');
}
} }
if (file_exists($THEMEPATH."//style.css")) {
$stylecss = file_get_contents($THEMEPATH."//style.css")."\n";
$modified = filemtime($THEMEPATH."//style.css");
}
$stylemodified = filemtime($stylecssfile);
$stylecss .= file_get_contents($stylecssfile);
if ($stylemodified > $modified) {
$modified = $stylemodified;
}
$modified = gmdate('r', $modified); $modified = gmdate('r', $modified);
$etag = md5($stylecss); $etag = md5($stylecss);
@ -54,7 +44,6 @@ header('ETag: "'.$etag.'"');
header('Last-Modified: '.$modified); header('Last-Modified: '.$modified);
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']));