Browse Source

Merge branch 'develop' of github.com:friendica/friendica into rewrites/coding-convention-split2-4-2

Fixed some stuff:
- converted some files from DOS to Uni* (CRLF -> LF)
- removed trailing white-spaces

Signed-off-by: Roland Häder <roland@mxchange.org>
tags/3.5.2
Roland Häder 3 years ago
parent
commit
7a9456d5ac
No known key found for this signature in database GPG Key ID: B72F8185C6C7BD78
100 changed files with 4713 additions and 3472 deletions
  1. +120
    -1074
      boot.php
  2. +3
    -3
      composer.lock
  3. +31
    -15
      database.sql
  4. +5
    -0
      doc/Composer.md
  5. +108
    -0
      doc/Developer-How-To-Move-Classes-to-src.md
  6. +2
    -0
      doc/Developers-Intro.md
  7. +19
    -15
      doc/Home.md
  8. +1
    -1
      doc/Install.md
  9. +6
    -0
      doc/Settings.md
  10. +1004
    -885
      doc/api.md
  11. +197
    -192
      doc/autoloader.md
  12. +1
    -0
      doc/database.md
  13. +14
    -0
      doc/database/db_conversation.md
  14. +1
    -1
      doc/de/Install.md
  15. +13
    -7
      doc/de/Settings.md
  16. +10
    -10
      include/Contact.php
  17. +0
    -1
      include/Emailer.php
  18. +2
    -0
      include/ForumManager.php
  19. +28
    -28
      include/NotificationsManager.php
  20. +14
    -6
      include/Photo.php
  21. +43
    -41
      include/Smilies.php
  22. +8
    -4
      include/acl_selectors.php
  23. +723
    -25
      include/api.php
  24. +2
    -1
      include/auth.php
  25. +5
    -3
      include/auth_ejabberd.php
  26. +8
    -5
      include/bb2diaspora.php
  27. +60
    -14
      include/bbcode.php
  28. +4
    -4
      include/cache.php
  29. +3
    -2
      include/cli_startup.php
  30. +3
    -3
      include/config.php
  31. +2
    -0
      include/contact_widgets.php
  32. +2
    -1
      include/conversation.php
  33. +0
    -1
      include/create_shadowentry.php
  34. +2
    -1
      include/cron.php
  35. +1
    -1
      include/cronhooks.php
  36. +11
    -9
      include/cronjobs.php
  37. +15
    -15
      include/datetime.php
  38. +674
    -64
      include/dba.php
  39. +37
    -31
      include/dbclean.php
  40. +6
    -1
      include/dbm.php
  41. +145
    -87
      include/dbstructure.php
  42. +1
    -1
      include/dbupdate.php
  43. +13
    -12
      include/delivery.php
  44. +53
    -11
      include/dfrn.php
  45. +241
    -132
      include/diaspora.php
  46. +3
    -3
      include/directory.php
  47. +4
    -3
      include/discover_poco.php
  48. +3
    -0
      include/enotify.php
  49. +3
    -69
      include/event.php
  50. +1
    -1
      include/expire.php
  51. +0
    -1
      include/feed.php
  52. +0
    -1
      include/files.php
  53. +15
    -7
      include/follow.php
  54. +4
    -4
      include/gprobe.php
  55. +0
    -1
      include/html2bbcode.php
  56. +0
    -1
      include/html2plain.php
  57. +2
    -0
      include/identity.php
  58. +72
    -3
      include/items.php
  59. +3
    -0
      include/like.php
  60. +3
    -4
      include/message.php
  61. +1
    -2
      include/msgclean.php
  62. +4
    -2
      include/nav.php
  63. +152
    -103
      include/network.php
  64. +14
    -13
      include/notifier.php
  65. +28
    -26
      include/oauth.php
  66. +3
    -2
      include/oembed.php
  67. +1
    -1
      include/onepoll.php
  68. +130
    -88
      include/ostatus.php
  69. +1
    -1
      include/pgettext.php
  70. +2
    -2
      include/photos.php
  71. +0
    -1
      include/pidfile.php
  72. +2
    -2
      include/plaintext.php
  73. +7
    -6
      include/plugin.php
  74. +15
    -6
      include/poller.php
  75. +0
    -2
      include/post_update.php
  76. +20
    -14
      include/probe.php
  77. +6
    -1
      include/pubsubpublish.php
  78. +1
    -1
      include/queue.php
  79. +0
    -1
      include/quoteconvert.php
  80. +2
    -0
      include/redir.php
  81. +4
    -12
      include/remove_contact.php
  82. +3
    -2
      include/salmon.php
  83. +2
    -0
      include/security.php
  84. +3
    -2
      include/shadowupdate.php
  85. +29
    -33
      include/socgraph.php
  86. +1
    -2
      include/spool_post.php
  87. +11
    -10
      include/tags.php
  88. +68
    -1
      include/text.php
  89. +13
    -15
      include/threads.php
  90. +293
    -291
      include/uimport.php
  91. +2
    -2
      include/update_gcontact.php
  92. +15
    -8
      index.php
  93. +9
    -5
      js/autocomplete.js
  94. +2
    -1
      mod/_well_known.php
  95. +3
    -1
      mod/acctlink.php
  96. +2
    -0
      mod/acl.php
  97. +101
    -2
      mod/admin.php
  98. +2
    -0
      mod/allfriends.php
  99. +5
    -4
      mod/amcd.php
  100. +2
    -2
      mod/api.php

+ 120
- 1074
boot.php
File diff suppressed because it is too large
View File


+ 3
- 3
composer.lock View File

@@ -226,10 +226,10 @@
},
{
"name": "pear-pear.php.net/PEAR",
"version": "1.10.3",
"version": "1.10.4",
"dist": {
"type": "file",
"url": "https://pear.php.net/get/PEAR-1.10.3.tgz",
"url": "https://pear.php.net/get/PEAR-1.10.4.tgz",
"reference": null,
"shasum": null
},
@@ -247,7 +247,7 @@
"pear-pear.php.net/pear_frontend_web": "<=0.4.0.0"
},
"replace": {
"pear-pear/pear": "== 1.10.3.0"
"pear-pear/pear": "== 1.10.4.0"
},
"type": "pear-library",
"autoload": {


+ 31
- 15
database.sql View File

@@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 3.5.2-dev (Asparagus)
-- DB_UPDATE_VERSION 1221
-- ------------------------------------------


@@ -174,13 +174,13 @@ CREATE TABLE IF NOT EXISTS `contact` (
`fetch_further_information` tinyint(1) NOT NULL DEFAULT 0,
`ffi_keyword_blacklist` text,
PRIMARY KEY(`id`),
INDEX `uid_name` (`uid`,`name`),
INDEX `uid_name` (`uid`,`name`(190)),
INDEX `self_uid` (`self`,`uid`),
INDEX `alias_uid` (`alias`(32),`uid`),
INDEX `pending_uid` (`pending`,`uid`),
INDEX `blocked_uid` (`blocked`,`uid`),
INDEX `uid_rel_network_poll` (`uid`,`rel`,`network`,`poll`(64),`archive`),
INDEX `uid_network_batch` (`uid`,`network`,`batch`(64)),
INDEX `uid_rel_network_poll` (`uid`,`rel`,`network`(4),`poll`(64),`archive`),
INDEX `uid_network_batch` (`uid`,`network`(4),`batch`(64)),
INDEX `addr_uid` (`addr`(32),`uid`),
INDEX `nurl_uid` (`nurl`(32),`uid`),
INDEX `nick_uid` (`nick`(32),`uid`),
@@ -204,6 +204,21 @@ CREATE TABLE IF NOT EXISTS `conv` (
INDEX `uid` (`uid`)
) DEFAULT COLLATE utf8mb4_general_ci;

--
-- TABLE conversation
--
CREATE TABLE IF NOT EXISTS `conversation` (
`item-uri` varbinary(255) NOT NULL,
`reply-to-uri` varbinary(255) NOT NULL DEFAULT '',
`conversation-uri` varbinary(255) NOT NULL DEFAULT '',
`conversation-href` varbinary(255) NOT NULL DEFAULT '',
`protocol` tinyint(1) unsigned NOT NULL DEFAULT 0,
`source` mediumtext,
`received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
PRIMARY KEY(`item-uri`),
INDEX `conversation-uri` (`conversation-uri`)
) DEFAULT COLLATE utf8mb4_general_ci;

--
-- TABLE event
--
@@ -344,7 +359,7 @@ CREATE TABLE IF NOT EXISTS `gcontact` (
INDEX `name` (`name`(64)),
INDEX `nick` (`nick`(32)),
INDEX `addr` (`addr`(64)),
INDEX `hide_network_updated` (`hide`,`network`,`updated`),
INDEX `hide_network_updated` (`hide`,`network`(4),`updated`),
INDEX `updated` (`updated`)
) DEFAULT COLLATE utf8mb4_general_ci;

@@ -523,22 +538,22 @@ CREATE TABLE IF NOT EXISTS `item` (
INDEX `uid_contactid_id` (`uid`,`contact-id`,`id`),
INDEX `uid_created` (`uid`,`created`),
INDEX `uid_unseen_contactid` (`uid`,`unseen`,`contact-id`),
INDEX `uid_network_received` (`uid`,`network`,`received`),
INDEX `uid_network_commented` (`uid`,`network`,`commented`),
INDEX `uid_thrparent` (`uid`,`thr-parent`),
INDEX `uid_parenturi` (`uid`,`parent-uri`),
INDEX `uid_network_received` (`uid`,`network`(4),`received`),
INDEX `uid_network_commented` (`uid`,`network`(4),`commented`),
INDEX `uid_thrparent` (`uid`,`thr-parent`(190)),
INDEX `uid_parenturi` (`uid`,`parent-uri`(190)),
INDEX `uid_contactid_created` (`uid`,`contact-id`,`created`),
INDEX `authorid_created` (`author-id`,`created`),
INDEX `uid_uri` (`uid`,`uri`),
INDEX `uid_uri` (`uid`,`uri`(190)),
INDEX `resource-id` (`resource-id`),
INDEX `contactid_allowcid_allowpid_denycid_denygid` (`contact-id`,`allow_cid`(10),`allow_gid`(10),`deny_cid`(10),`deny_gid`(10)),
INDEX `uid_type_changed` (`uid`,`type`,`changed`),
INDEX `contactid_verb` (`contact-id`,`verb`),
INDEX `uid_type_changed` (`uid`,`type`(190),`changed`),
INDEX `contactid_verb` (`contact-id`,`verb`(190)),
INDEX `deleted_changed` (`deleted`,`changed`),
INDEX `uid_wall_changed` (`uid`,`wall`,`changed`),
INDEX `uid_eventid` (`uid`,`event-id`),
INDEX `uid_authorlink` (`uid`,`author-link`),
INDEX `uid_ownerlink` (`uid`,`owner-link`)
INDEX `uid_authorlink` (`uid`,`author-link`(190)),
INDEX `uid_ownerlink` (`uid`,`owner-link`(190))
) DEFAULT COLLATE utf8mb4_general_ci;

--
@@ -652,7 +667,7 @@ CREATE TABLE IF NOT EXISTS `notify` (
INDEX `hash_uid` (`hash`,`uid`),
INDEX `seen_uid_date` (`seen`,`uid`,`date`),
INDEX `uid_date` (`uid`,`date`),
INDEX `uid_type_link` (`uid`,`type`,`link`)
INDEX `uid_type_link` (`uid`,`type`,`link`(190))
) DEFAULT COLLATE utf8mb4_general_ci;

--
@@ -963,7 +978,7 @@ CREATE TABLE IF NOT EXISTS `term` (
`aid` int(10) unsigned NOT NULL DEFAULT 0,
`uid` int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY(`tid`),
INDEX `oid_otype_type_term` (`oid`,`otype`,`type`,`term`),
INDEX `oid_otype_type_term` (`oid`,`otype`,`type`,`term`(32)),
INDEX `uid_otype_type_term_global_created` (`uid`,`otype`,`type`,`term`(32),`global`,`created`),
INDEX `uid_otype_type_url` (`uid`,`otype`,`type`,`url`(64)),
INDEX `guid` (`guid`(64))


+ 5
- 0
doc/Composer.md View File

@@ -113,3 +113,8 @@ For Composer, this would be:
````
$> COMPOSER_HOME=/var/tmp/composer sudo -u [web user] util/composer.phar [mode]
````

## Related

* [Class autoloading](help/autoloader)
* [How To Move Classes to `src`](help/Developer-How-To-Move-Classes-to-src)

+ 108
- 0
doc/Developer-How-To-Move-Classes-to-src.md View File

@@ -0,0 +1,108 @@
How To Move Classes to `src`
==============

* [Home](help)
* [Developer Intro](help/Developers-Intro)

Friendica uses [Composer](help/Composer) to manage autoloading.
This means that all the PHP class files moved to the `src` folder will be [automatically included](help/autoloader) when the class it defines is first used in the flow.
This is an improvement over the current `require` usage since files will be included on an actual usage basis instead of the presence of a `require` call.

However, there are a significant number of items to check when moving a class file from the `include` folder to the `src` folder, and this page is there to list them.

## Decide the namespace

This isn't the most technical decision of them all, but it has long lasting consequences as it will be the name that will be used to refer to this class from now on.
There is [a shared Ethercalc sheet](https://ethercalc.org/friendica_classes) to suggest namespace/class names that lists all the already moved class files for inspiration.

A few pointers though:
* `Friendica` is the base namespace for all classes in the `src` folder
* Namespaces match the directory structure, with `Friendica` namespace being the base `src` directory. The `Config` class set in the `Friendica\Core` namespace is expected to be found at `src/Core/Config.php`.
* Namespaces can help group classes with a similar purpose or relevant to a particular feature

When you're done deciding the namespace, it's time to use it.
Let's say we choose `Friendica\Core` for the `Config` class.

## Use the namespace

To declare the namespace, the file `src/Core/Config.php` must start with the following statement:

````php
namespace Friendica\Core;
````

From now on, the `Config` class can be referred to as `Friendica\Core\Config`, however it isn't very practical, especially when the class was previously used as `Config`.
Thankfully, PHP provides namespace shortcuts through `use`.

This language construct just provides a different naming scheme for a namespace or a class, but doesn't trigger the autoload mechanism on its own.
Here are the different ways you can use `use`:

````php
// No use
$config = new Friendica\Core\Config();
````
````php
// Namespace shortcut
use Friendica\Core;

$config = new Core\Config();
````
````php
// Class name shortcut
use Friendica\Core\Config;

$config = new Config();
````
````php
// Aliasing
use Friendica\Core\Config as Cfg;

$config = new Cfg();
````

Whatever the style chosen, a repository-wide search has to be done to find all the class name usage and either use the fully-qualified class name (including the namespace) or add a `use` statement at the start of each relevant file.

## Escape non-namespace classes

The class file you just moved is now in the `Friendica` namespace, but it probably isn't the case for all the classes referenced in this file.
Since we added a `namespace Friendica\Core;` to the file, all the class names still declared in `include` will be implicitly understood as `Friendica\Core\ClassName`, which is rarely what we expect.

To avoid `Class Friendica\Core\ClassName not found` errors, all the `include`-declared class names have to be prepended with a `\`, it tells the autoloader not to look for the class in the namespace but in the global space where non-namespaced classes are set.
If there are only a handful of references to a single non-namespaced class, just prepending `\` is enough. However, if there are many instance, we can use `use` again.

````php
namespace Friendica\Core;
...
if (\dbm::is_result($r)) {
...
}
````
````php
namespace Friendica\Core;

use \dbm;

if (dbm::is_result($r)) {
...
}
````

## Remove any useless `require`

Now that you successfully moved your class to the autoloaded `src` folder, there's no need to include this file anywhere in the app ever again.
Please remove all the `require_once` mentions of the former file, as they will provoke a Fatal Error even if the class isn't used.

## Miscellaneous tips

When you are done with moving the class, please run `php util/typo.php` from the Friendica base directory to check for obvious mistakes.
Howevever, this tool isn't bullet-proof, and a staging install of Friendica is recommended to test your class move without impairing your production server if you host one.

Most of Friendica processes are run in the background, so make sure to turn on your debug log to check for errors that wouldn't show up while simply browsing Friendica.

Check the class file for any magic constant `__FILE__` or `__DIR__`, as their value changed since you moved the class in the file tree.
Most of the time it's used for debugging purposes but there can be instances where it's used to create cache folders for example.

## Related

* [Class autoloading](help/autoloader)
* [Using Composer](help/Composer)

+ 2
- 0
doc/Developers-Intro.md View File

@@ -52,7 +52,9 @@ Friendica uses [Composer](https://getcomposer.org) to manage dependencies librar

It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.

* [Class autoloading](help/autoloader)
* [Using Composer](help/Composer)
* [How To Move Classes to `src`](help/Developer-How-To-Move-Classes-to-src)

###Coding standards



+ 19
- 15
doc/Home.md View File

@@ -36,21 +36,25 @@ Friendica Documentation and Resources

**Developer Manual**

* [Where to get started?](help/Developers-Intro)
* [Help on Github](help/Github)
* [Help on Vagrant](help/Vagrant)
* [How to translate Friendica](help/translations)
* [Bugs and Issues](help/Bugs-and-Issues)
* [Plugin Development](help/Plugins)
* [Theme Development](help/themes)
* [Smarty 3 Templates](help/smarty3-templates)
* [Protocol Documentation](help/Protocol)
* [Database schema documantation](help/database)
* [Class Autoloading](help/autoloader)
* [Using Composer](help/Composer)
* [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
* [Twitter/GNU Social API Functions](help/api)

* [Get started](help/Developers-Intro)
* Set up development environment
* [Help on Github](help/Github)
* [Help on Vagrant](help/Vagrant)
* [Bugs and Issues](help/Bugs-and-Issues)
* Code structure
* [Plugin Development](help/Plugins)
* [Theme Development](help/themes)
* [Smarty 3 Templates](help/smarty3-templates)
* How To
* [Translate Friendica](help/translations)
* [Use Composer](help/Composer)
* [Move classes to `src`](help/Developer-How-To-Move-Classes-to-src)
* Reference
* [Twitter/GNU Social API Functions](help/api)
* [Code (Doxygen generated - sets cookies)](doc/html/)
* [Protocol Documentation](help/Protocol)
* [Database schema documantation](help/database)
* [Class Autoloading](help/autoloader)

**External Resources**



+ 1
- 1
doc/Install.md View File

@@ -28,7 +28,7 @@ Requirements
* Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file
* PHP 5.4+.
* PHP *command line* access with register_argc_argv set to true in the php.ini file
* curl, gd, mysql, hash and openssl extensions
* Curl, GD, PDO, MySQLi, hash and OpenSSL extensions
* 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.)
* the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.)


+ 6
- 0
doc/Settings.md View File

@@ -242,6 +242,12 @@ The receiving end might be off-line, there might be a high system load and so on
Don't panic!
Friendica will not queue messages for all time but will sort out *dead* nodes automatically after a while and remove messages from the queue then.

## Server Blocklist

This page allows to block all communications (inbound and outbound) with a specific domain name.
Each blocked domain entry requires a reason that will be displayed on the [friendica](/friendica) page.
Matching is exact, blocking a domain doesn't block subdomains.

## Federation Statistics

The federation statistics page gives you a short summery of the nodes/servers/pods of the decentralized social network federation your node knows.


+ 1004
- 885
doc/api.md
File diff suppressed because it is too large
View File


+ 197
- 192
doc/autoloader.md View File

@@ -1,192 +1,197 @@
Autoloader with Composer
==========
* [Home](help)
* [Developer Intro](help/Developers-Intro)
Friendica uses [Composer](https://getcomposer.org) to manage dependencies libraries and the class autoloader both for libraries and namespaced Friendica classes.
It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.
* [Using Composer](help/Composer)
## A quick introduction to class autoloading
The autoloader dynamically includes the file defining a class when it is first referenced, either by instantiating an object or simply making sure that it is available, without the need to explicitly use "require_once".
Once it is set up you don't have to directly use it, you can directly use any class that is covered by the autoloader (currently `vendor` and `src`)
Under the hood, Composer registers a callback with [`spl_autoload_register()`](http://php.net/manual/en/function.spl-autoload-register.php) that receives a class name as an argument and includes the corresponding class definition file.
For more info about PHP autoloading, please refer to the [official PHP documentation](http://php.net/manual/en/language.oop5.autoload.php).
### Example
Let's say you have a PHP file in `src/` that define a very useful class:
```php
// src/ItemsManager.php
<?php
namespace \Friendica;
class ItemsManager {
public function getAll() { ... }
public function getByID($id) { ... }
}
```
The class `ItemsManager` has been declared in the `Friendica` namespace.
Namespaces are useful to keep classes separated and avoid names conflicts (could be that a library you want to use also defines a class named `ItemsManager`, but as long as it is in another namespace, you don't have any problem)
Let's say now that you need to load some items in a view, maybe in a fictional `mod/network.php`.
In order for the Composer autoloader to work, it must first be included. In Friendica this is already done at the top of `boot.php`, with `require_once('vendor/autoload.php');`.
The code will be something like:
```php
// mod/network.php
<?php
function network_content(App $a) {
$itemsmanager = new \Friendica\ItemsManager();
$items = $itemsmanager->getAll();
// pass $items to template
// return result
}
```
That's a quite simple example, but look: no `require()`!
If you need to use a class, you can simply use it and you don't need to do anything else.
Going further: now we have a bunch of `*Manager` classes that cause some code duplication, let's define a `BaseManager` class, where we move all common code between all managers:
```php
// src/BaseManager.php
<?php
namespace \Friendica;
class BaseManager {
public function thatFunctionEveryManagerUses() { ... }
}
```
and then let's change the ItemsManager class to use this code
```php
// src/ItemsManager.php
<?php
namespace \Friendica;
class ItemsManager extends BaseManager {
public function getAll() { ... }
public function getByID($id) { ... }
}
```
Even though we didn't explicitly include the `src/BaseManager.php` file, the autoloader will when this class is first defined, because it is referenced as a parent class.
It works with the "BaseManager" example here and it works when we need to call static methods:
```php
// src/Dfrn.php
<?php
namespace \Friendica;
class Dfrn {
public static function mail($item, $owner) { ... }
}
```
```php
// mod/mail.php
<?php
mail_post($a){
...
\Friendica\dfrn::mail($item, $owner);
...
}
```
If your code is in same namespace as the class you need, you don't need to prepend it:
```php
// include/delivery.php
<?php
namespace \Friendica;
// this is the same content of current include/delivery.php,
// but has been declared to be in "Friendica" namespace
[...]
switch($contact['network']) {
case NETWORK_DFRN:
if ($mail) {
$item['body'] = ...
$atom = Dfrn::mail($item, $owner);
} elseif ($fsuggest) {
$atom = Dfrn::fsuggest($item, $owner);
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
} elseif ($relocate)
$atom = Dfrn::relocate($owner, $uid);
[...]
```
This is the current code of `include/delivery.php`, and since the code is declared to be in the "Friendica" namespace, you don't need to write it when you need to use the "Dfrn" class.
But if you want to use classes from another library, you need to use the full namespace, e.g.
```php
// src/Diaspora.php
<?php
namespace \Friendica;
class Diaspora {
public function md2bbcode() {
$html = \Michelf\MarkdownExtra::defaultTransform($text);
}
}
```
if you use that class in many places of the code and you don't want to write the full path to the class every time, you can use the "use" PHP keyword
```php
// src/Diaspora.php
<?php
namespace \Friendica;
use \Michelf\MarkdownExtra;
class Diaspora {
public function md2bbcode() {
$html = MarkdownExtra::defaultTransform($text);
}
}
```
Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
You can go deeper if you want to, like:
```
// src/Network/Dfrn.php
<?php
namespace \Friendica\Network;
class Dfrn {
}
```
Please note that the location of the file defining the class must be placed in the appropriate sub-folders of `src` if the namespace isn't plain `\Friendica`.
or
```
// src/Dba/Mysql
<?php
namespace \Friendica\Dba;
class Mysql {
}
```
So you can think of namespaces as folders in a Unix file system, with global scope as the root ("\").
Autoloader with Composer
==========

* [Home](help)
* [Developer Intro](help/Developers-Intro)

Friendica uses [Composer](https://getcomposer.org) to manage dependencies libraries and the class autoloader both for libraries and namespaced Friendica classes.

It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.

* [Using Composer](help/Composer)

## A quick introduction to class autoloading

The autoloader dynamically includes the file defining a class when it is first referenced, either by instantiating an object or simply making sure that it is available, without the need to explicitly use "require_once".

Once it is set up you don't have to directly use it, you can directly use any class that is covered by the autoloader (currently `vendor` and `src`)

Under the hood, Composer registers a callback with [`spl_autoload_register()`](http://php.net/manual/en/function.spl-autoload-register.php) that receives a class name as an argument and includes the corresponding class definition file.
For more info about PHP autoloading, please refer to the [official PHP documentation](http://php.net/manual/en/language.oop5.autoload.php).

### Example

Let's say you have a PHP file in `src/` that define a very useful class:

```php
// src/ItemsManager.php
<?php
namespace Friendica;

class ItemsManager {
public function getAll() { ... }
public function getByID($id) { ... }
}
```

The class `ItemsManager` has been declared in the `Friendica` namespace.
Namespaces are useful to keep classes separated and avoid names conflicts (could be that a library you want to use also defines a class named `ItemsManager`, but as long as it is in another namespace, you don't have any problem)

Let's say now that you need to load some items in a view, maybe in a fictional `mod/network.php`.
In order for the Composer autoloader to work, it must first be included. In Friendica this is already done at the top of `boot.php`, with `require_once('vendor/autoload.php');`.

The code will be something like:

```php
// mod/network.php
<?php

function network_content(App $a) {
$itemsmanager = new Friendica\ItemsManager();
$items = $itemsmanager->getAll();

// pass $items to template
// return result
}
```

That's a quite simple example, but look: no `require()`!
If you need to use a class, you can simply use it and you don't need to do anything else.

Going further: now we have a bunch of `*Manager` classes that cause some code duplication, let's define a `BaseManager` class, where we move all common code between all managers:

```php
// src/BaseManager.php
<?php
namespace Friendica;

class BaseManager {
public function thatFunctionEveryManagerUses() { ... }
}
```

and then let's change the ItemsManager class to use this code

```php
// src/ItemsManager.php
<?php
namespace Friendica;

class ItemsManager extends BaseManager {
public function getAll() { ... }
public function getByID($id) { ... }
}
```

Even though we didn't explicitly include the `src/BaseManager.php` file, the autoloader will when this class is first defined, because it is referenced as a parent class.
It works with the "BaseManager" example here and it works when we need to call static methods:

```php
// src/Dfrn.php
<?php
namespace Friendica;

class Dfrn {
public static function mail($item, $owner) { ... }
}
```

```php
// mod/mail.php
<?php

mail_post($a){
...
Friendica\dfrn::mail($item, $owner);
...
}
```

If your code is in same namespace as the class you need, you don't need to prepend it:

```php
// include/delivery.php
<?php

namespace Friendica;

// this is the same content of current include/delivery.php,
// but has been declared to be in "Friendica" namespace

[...]
switch($contact['network']) {
case NETWORK_DFRN:
if ($mail) {
$item['body'] = ...
$atom = Dfrn::mail($item, $owner);
} elseif ($fsuggest) {
$atom = Dfrn::fsuggest($item, $owner);
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
} elseif ($relocate)
$atom = Dfrn::relocate($owner, $uid);
[...]
```

This is the current code of `include/delivery.php`, and since the code is declared to be in the "Friendica" namespace, you don't need to write it when you need to use the "Dfrn" class.
But if you want to use classes from another library, you need to use the full namespace, e.g.

```php
// src/Diaspora.php
<?php

namespace Friendica;

class Diaspora {
public function md2bbcode() {
$html = \Michelf\MarkdownExtra::defaultTransform($text);
}
}
```

if you use that class in many places of the code and you don't want to write the full path to the class every time, you can use the "use" PHP keyword

```php
// src/Diaspora.php
<?php
namespace Friendica;

use \Michelf\MarkdownExtra;

class Diaspora {
public function md2bbcode() {
$html = MarkdownExtra::defaultTransform($text);
}
}
```

Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
You can go deeper if you want to, like:

```
// src/Network/Dfrn.php
<?php
namespace Friendica\Network;

class Dfrn {
}
```

Please note that the location of the file defining the class must be placed in the appropriate sub-folders of `src` if the namespace isn't plain `Friendica`.

or

```
// src/Dba/Mysql
<?php
namespace Friendica\Dba;

class Mysql {
}
```

So you can think of namespaces as folders in a Unix file system, with global scope as the root ("\").

## Related

* [Using Composer](help/Composer)
* [How To Move Classes to `src`](help/Developer-How-To-Move-Classes-to-src)

+ 1
- 0
doc/database.md View File

@@ -14,6 +14,7 @@ Database Tables
| [config](help/database/db_config) | main configuration storage |
| [contact](help/database/db_contact) | contact table |
| [conv](help/database/db_conv) | private messages |
| [conversation](help/database/db_conversation) | Raw data and structure information for messages |
| [event](help/database/db_event) | Events |
| [fcontact](help/database/db_fcontact) | friend suggestion stuff |
| [ffinder](help/database/db_ffinder) | friend suggestion stuff |


+ 14
- 0
doc/database/db_conversation.md View File

@@ -0,0 +1,14 @@
Table conversation
==================

| Field | Description | Type | Null | Key | Default | Extra |
|-------------------| ---------------------------------- |---------------------|------|-----|---------------------|----------------|
| item-uri | URI of the item | varbinary(255) | NO | PRI | NULL | |
| reply-to-uri | URI to which this item is a reply | varbinary(255) | NO | | | |
| conversation-uri | GNU Social conversation URI | varbinary(255) | NO | | | |
| conversation-href | GNU Social conversation link | varbinary(255) | NO | | | |
| protocol | The protocol of the item | tinyint(1) unsigned | NO | | 0 | |
| source | Original source | mediumtext | NO | | | |
| received | Receiving date | datetime | NO | | 0001-01-01 | |

Return to [database documentation](help/database)

+ 1
- 1
doc/de/Install.md View File

@@ -24,7 +24,7 @@ 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
- PHP 5.2+. Je neuer, desto besser. Du benötigst 5.3 für die Authentifizierung untereinander. In einer Windows-Umgebung arbeitet die Version 5.2+ möglicherweise nicht, da die Funktion dns_get_record() erst ab Version 5.3 verfügbar ist.
- PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
- curl, gd, mysql und openssl-Erweiterung
- Curl, GD, PDO, MySQLi und OpenSSL-Erweiterung
- etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail()
- Mysql 5.x
- 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]


+ 13
- 7
doc/de/Settings.md View File

@@ -2,7 +2,7 @@

* [Zur Startseite der Hilfe](help)

Wenn du der Administrator einer Friendica Instanz bist, hast du Zugriff auf das so genannte **Admin Panel** in dem du die Friendica Instanz konfigurieren kannst,
Wenn du der Administrator einer Friendica-Instanz bist, hast du Zugriff auf das so genannte **Admin Panel** in dem du die Friendica-Instanz konfigurieren kannst,

Auf der Startseite des Admin Panels werden die Informationen zu der Instanz zusammengefasst.
Die erste Zahl gibt die Anzahl von Nachrichten an, die nicht zugestellt werden konnten.
@@ -22,7 +22,7 @@ Die Unterabschnitte des Admin Panels kannst du in der Seitenleiste auswählen.

## Seite

In diesem Bereich des Admin Panels findest du die Hauptkonfiguration deiner Friendica Instanz.
In diesem Bereich des Admin Panels findest du die Hauptkonfiguration deiner Friendica-Instanz.
Er ist in mehrere Unterabschnitte aufgeteilt, wobei die Grundeinstellungen oben auf der Seite zu finden sind.

Da die meisten Konfigurationsoptionen einen Hilfstext im Admin Panel haben, kann und will dieser Artikel nicht alle Einstellungen abdecken.
@@ -130,7 +130,7 @@ Dieses Feature kann z.B. dafür genutzt werden Blogbeiträge zu spiegeln.
In der Grundeinstellung ist es nicht aktiviert, da es zusätzliche Last auf dem Server verursachen kann.
Außerdem könnte es durch Nutzer als Spam Verteiler missbraucht werden.

Als Administrator der Friendica Instanz kannst du diese Einstellungen ansonsten nur direkt in der Datenbank vornehmen.
Als Administrator der Friendica-Instanz kannst du diese Einstellungen ansonsten nur direkt in der Datenbank vornehmen.
Bevor du das tust solltest du sicherstellen, dass du ein Backup der Datenbank hast und genau weißt was die Änderungen an der Datenbank bewirken, die du vornehmen willst.

### Erweitert
@@ -183,7 +183,7 @@ Dadurch werden dann die Aufgaben aktiviert, die der cron Job sonst aktivieren w

## Nutzer

In diesem Abschnitt des Admin Panels kannst du die Nutzer deiner Friendica Instanz moderieren.
In diesem Abschnitt des Admin Panels kannst du die Nutzer deiner Friendica-Instanz moderieren.

Solltest du für **Registrierungsmethode** die Einstellung "Bedarf Zustimmung" gewählt haben, werden hier zu Beginn der Seite neue Registrationen aufgelistet.
Als Administrator kannst du hier die Registration akzeptieren oder ablehnen.
@@ -202,12 +202,12 @@ Sie müssen in das `/addon` Verzeichnis kopiert werden.
Auf der Seite wird eine Liste der verfügbaren Erweiterungen angezeigt.
Neben den Namen der Erweiterungen wird ein Indikator angezeigt, der anzeigt ob das Addon gerade aktiviert ist oder nicht.

Wenn du die Erweiterungen aktualisiert die du auf deiner Friendica Instanz nutzt könnte es sein, dass sie neu geladen werden müssen, damit die Änderungen aktiviert werden.
Wenn du die Erweiterungen aktualisiert die du auf deiner Friendica-Instanz nutzt könnte es sein, dass sie neu geladen werden müssen, damit die Änderungen aktiviert werden.
Um diesen Prozess zu vereinfachen gibt es am Anfang der Seite einen Button um alle aktiven Plugins neu zu laden.

## Themen

Der Bereich zur Kontrolle der auf der Friendica Instanz verfügbaren Themen funktioniert analog zum Plugins Bereich.
Der Bereich zur Kontrolle der auf der Friendica-Instanz verfügbaren Themen funktioniert analog zum Plugins Bereich.
Jedes Theme hat eine extra Seite auf der der aktuelle Status, ein Bildschirmfoto des Themes, zusätzliche Informationen und eventuelle Einstellungen des Themes zu finden sind.
Genau wie Erweiterungen können Themes in der Übersichtsliste oder der Theme-Seite aktiviert bzw. deaktiviert werden.
Um ein Standardtheme für die Instanz zu wählen, benutze bitte die *Seiten* Bereich des Admin Panels.
@@ -238,6 +238,12 @@ Aber keine Panik!
Friendica wird die Beiträge nicht für alle Zeiten in der Warteschlange behalten.
Nach einiger Zeit werden Knoten als inaktiv identifiziert und Nachrichten an Nutzer dieser Knoten aus der Warteschlange gelöscht.

## Server Blockliste

Auf dieser Seite des Admin Panels können Administratoren einer Friendica-Instanz die komplette Kommunikation (eingehend und ausgehend) mit bestimmten Domains unterbinden.
Für jede dieser Blockierungen muss ein Grund angegeben werden, welcher auf der Informationsseite [friendica](/friendica) angezeigt wird.
Der Abgleich der Domainnamen ist exakt, Subdomains werden nicht automatisch ebenfalls blockiert.

## Federation Statistik

Deine Instanz ist ein Teil eines Netzwerks von Servern dezentraler sozialer Netzwerke, der sogenannten **Federation**.
@@ -258,7 +264,7 @@ Wenn du das dennoch tun musste und die Standardeinstellungen des Apache Servers
Solltest du einen anderen Webserver verwenden, solltest du sicherstellen, dass der Zugrif zu Dateien mit diesen Endungen nicht möglich ist.

Es gibt fünf Level der Ausführlichkeit mit denen Friendica arbeitet: Normal, Trace, Debug, Data und All.
Normalerweise solltest du für den Betrieb deiner Friendica Instanz keine Logs benötigen.
Normalerweise solltest du für den Betrieb deiner Friendica-Instanz keine Logs benötigen.
Wenn du versuchst einem Problem auf den Grund zu gehen, solltest du das "DEBUG" Level wählen.
Mit dem "All" Level schreibt Friendica alles in die Logdatei.
Die Datenmenge der geloggten Daten kann relativ schnell anwachsen, deshalb empfehlen wir das Anlegen von Protokollen nur zu aktivieren wenn es unbedingt nötig ist.


+ 10
- 10
include/Contact.php View File

@@ -1,5 +1,8 @@
<?php

use Friendica\App;
use Friendica\Network\Probe;

// Included here for completeness, but this is a very dangerous operation.
// It is the caller's responsibility to confirm the requestor's intent and
// authorisation to do this.
@@ -89,11 +92,11 @@ function terminate_friendship($user,$self,$contact) {
/// @TODO Get rid of this, include/datetime.php should care about it by itself
$a = get_app();

require_once('include/datetime.php');
require_once 'include/datetime.php';

if ($contact['network'] === NETWORK_OSTATUS) {

require_once('include/ostatus.php');
require_once 'include/ostatus.php';

// create an unfollow slap
$item = array();
@@ -102,14 +105,14 @@ function terminate_friendship($user,$self,$contact) {
$slap = ostatus::salmon($item, $user);

if ((x($contact,'notify')) && (strlen($contact['notify']))) {
require_once('include/salmon.php');
require_once 'include/salmon.php';
slapper($user,$contact['notify'],$slap);
}
} elseif ($contact['network'] === NETWORK_DIASPORA) {
require_once('include/diaspora.php');
require_once 'include/diaspora.php';
Diaspora::send_unshare($user,$contact);
} elseif ($contact['network'] === NETWORK_DFRN) {
require_once('include/dfrn.php');
require_once 'include/dfrn.php';
dfrn::deliver($user,$contact,'placeholder', 1);
}

@@ -361,7 +364,6 @@ function get_contact_details_by_addr($addr, $uid = -1) {
dbesc($addr));

if (!dbm::is_result($r)) {
require_once('include/Probe.php');
$data = Probe::uri($addr);

$profile = get_contact_details_by_url($data['url'], $uid);
@@ -587,7 +589,6 @@ function get_contact($url, $uid = 0, $no_update = false) {
return 0;
}

require_once('include/Probe.php');
$data = Probe::uri($url);

// Last try in gcontact for unsupported networks
@@ -704,7 +705,7 @@ function get_contact($url, $uid = 0, $no_update = false) {
*/
function posts_from_gcontact(App $a, $gcontact_id) {

require_once('include/conversation.php');
require_once 'include/conversation.php';

// There are no posts with "uid = 0" with connector networks
// This speeds up the query a lot
@@ -743,7 +744,7 @@ function posts_from_gcontact(App $a, $gcontact_id) {
*/
function posts_from_contact_url(App $a, $contact_url) {

require_once('include/conversation.php');
require_once 'include/conversation.php';

// There are no posts with "uid = 0" with connector networks
// This speeds up the query a lot
@@ -852,4 +853,3 @@ function account_type($contact) {

return $account_type;
}
?>

+ 0
- 1
include/Emailer.php View File

@@ -81,4 +81,3 @@ class Emailer {
return $res;
}
}
?>

+ 2
- 0
include/ForumManager.php View File

@@ -1,5 +1,7 @@
<?php

use Friendica\App;

/**
* @file include/ForumManager.php
* @brief ForumManager class with its methods related to forum functionality *


+ 28
- 28
include/NotificationsManager.php View File

@@ -4,9 +4,10 @@
* @brief Methods for read and write notifications from/to database
* or for formatting notifications
*/
require_once('include/html2plain.php');
require_once("include/datetime.php");
require_once("include/bbcode.php");
require_once 'include/html2plain.php';
require_once 'include/probe.php';
require_once 'include/datetime.php';
require_once 'include/bbcode.php';

/**
* @brief Methods for read and write notifications from/to database
@@ -24,7 +25,7 @@ class NotificationsManager {
*
* @param array $notes array of note arrays from db
* @return array Copy of input array with added properties
*
*
* Set some extra properties to note array from db:
* - timestamp as int in default TZ
* - date_rel : relative date string
@@ -143,8 +144,7 @@ class NotificationsManager {

/**
* @brief List of pages for the Notifications TabBar
*
* @param app $a The
*
* @return array with with notifications TabBar data
*/
public function getTabs() {
@@ -191,7 +191,7 @@ class NotificationsManager {

/**
* @brief Format the notification query in an usable array
*
*
* @param array $notifs The array from the db query
* @param string $ident The notifications identifier (e.g. network)
* @return array
@@ -360,7 +360,7 @@ class NotificationsManager {
}

/**
* @brief Total number of network notifications
* @brief Total number of network notifications
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
@@ -388,13 +388,13 @@ class NotificationsManager {

/**
* @brief Get network notifications
*
*
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
* @param int $start Start the query at this point
* @param int $limit Maximum number of query results
*
*
* @return array with
* string 'ident' => Notification identifier
* int 'total' => Total number of available network notifications
@@ -436,7 +436,7 @@ class NotificationsManager {
}

/**
* @brief Total number of system notifications
* @brief Total number of system notifications
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
@@ -460,13 +460,13 @@ class NotificationsManager {

/**
* @brief Get system notifications
*
*
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
* @param int $start Start the query at this point
* @param int $limit Maximum number of query results
*
*
* @return array with
* string 'ident' => Notification identifier
* int 'total' => Total number of available system notifications
@@ -502,7 +502,7 @@ class NotificationsManager {

/**
* @brief Addional SQL query string for the personal notifications
*
*
* @return string The additional sql query
*/
private function _personal_sql_extra() {
@@ -520,7 +520,7 @@ class NotificationsManager {
}

/**
* @brief Total number of personal notifications
* @brief Total number of personal notifications
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
@@ -550,13 +550,13 @@ class NotificationsManager {

/**
* @brief Get personal notifications
*
*
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
* @param int $start Start the query at this point
* @param int $limit Maximum number of query results
*
*
* @return array with
* string 'ident' => Notification identifier
* int 'total' => Total number of available personal notifications
@@ -573,13 +573,13 @@ class NotificationsManager {
$sql_seen = " AND `item`.`unseen` = 1 ";

$r = q("SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
`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`
`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`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1
$sql_extra
$sql_seen
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
AND `item`.`deleted` = 0 AND `item`.`uid` = %d AND `item`.`wall` = 0
ORDER BY `item`.`created` DESC LIMIT %d, %d " ,
intval(local_user()),
intval($start),
@@ -588,7 +588,7 @@ class NotificationsManager {

if (dbm::is_result($r))
$notifs = $this->formatNotifs($r, $ident);
$arr = array (
'notifications' => $notifs,
'ident' => $ident,
@@ -599,7 +599,7 @@ class NotificationsManager {
}

/**
* @brief Total number of home notifications
* @brief Total number of home notifications
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
@@ -626,13 +626,13 @@ class NotificationsManager {

/**
* @brief Get home notifications
*
*
* @param int|string $seen
* If 0 only include notifications into the query
* which aren't marked as "seen"
* @param int $start Start the query at this point
* @param int $limit Maximum number of query results
*
*
* @return array with
* string 'ident' => Notification identifier
* int 'total' => Total number of available home notifications
@@ -673,7 +673,7 @@ class NotificationsManager {
}

/**
* @brief Total number of introductions
* @brief Total number of introductions
* @param bool $all
* If false only include introductions into the query
* which aren't marked as ignored
@@ -698,13 +698,13 @@ class NotificationsManager {

/**
* @brief Get introductions
*
*
* @param bool $all
* If false only include introductions into the query
* which aren't marked as ignored
* @param int $start Start the query at this point
* @param int $limit Maximum number of query results
*
*
* @return array with
* string 'ident' => Notification identifier
* int 'total' => Total number of available introductions
@@ -749,7 +749,7 @@ class NotificationsManager {

/**
* @brief Format the notification query in an usable array
*
*
* @param array $intros The array from the db query
* @return array with the introductions
*/


+ 14
- 6
include/Photo.php View File

@@ -4,6 +4,8 @@
* @brief This file contains the Photo class for image processing
*/

use Friendica\App;

require_once("include/photos.php");

class Photo {
@@ -68,7 +70,9 @@ class Photo {
$this->image->destroy();
return;
}
imagedestroy($this->image);
if (is_resource($this->image)) {
imagedestroy($this->image);
}
}
}

@@ -324,6 +328,7 @@ class Photo {
return;
}

// if script dies at this point check memory_limit setting in php.ini
$this->image = imagerotate($this->image,$degrees,0);
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
@@ -620,7 +625,7 @@ class Photo {



public function store($uid, $cid, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') {
public function store($uid, $cid, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '', $desc = '') {

$r = q("SELECT `guid` FROM `photo` WHERE `resource-id` = '%s' AND `guid` != '' LIMIT 1",
dbesc($rid)
@@ -657,7 +662,8 @@ class Photo {
`allow_cid` = '%s',
`allow_gid` = '%s',
`deny_cid` = '%s',
`deny_gid` = '%s'
`deny_gid` = '%s',
`desc` = '%s'
WHERE `id` = %d",

intval($uid),
@@ -679,12 +685,13 @@ class Photo {
dbesc($allow_gid),
dbesc($deny_cid),
dbesc($deny_gid),
dbesc($desc),
intval($x[0]['id'])
);
} else {
$r = q("INSERT INTO `photo`
(`uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `datasize`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`)
VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s')",
(`uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `datasize`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`, `desc`)
VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s')",
intval($uid),
intval($cid),
dbesc($guid),
@@ -703,7 +710,8 @@ class Photo {
dbesc($allow_cid),
dbesc($allow_gid),
dbesc($deny_cid),
dbesc($deny_gid)
dbesc($deny_gid),
dbesc($desc)
);
}



+ 43
- 41
include/Smilies.php View File

@@ -5,6 +5,8 @@
* @brief This file contains the Smilies class which contains functions to handle smiles
*/

use Friendica\App;

/**
* This class contains functions to handle smiles
*/
@@ -13,13 +15,13 @@ class Smilies {

/**
* @brief Function to list all smilies
*
*
* Get an array of all smilies, both internal and from addons.
*
*
* @return array
* 'texts' => smilie shortcut
* 'icons' => icon in html
*
*
* @hook smilie ('texts' => smilies texts array, 'icons' => smilies html array)
*/
public static function get_list() {
@@ -64,41 +66,41 @@ class Smilies {
);

$icons = array(
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" title="&lt;3" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;/3" title="&lt;/3" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" title="&lt;\\3" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-smile.gif" alt=":-)" title=":-)" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-wink.gif" alt=";-)" title=";-)" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-frown.gif" alt=":-(" title=":-(" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" title=":-P" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" title=":-P" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" title=":-x" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" title=":-X" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" title=":-D" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" title="8-|" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" title="8-O" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" title="8-O" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" title="\\o/" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" title="o.O" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" title="O.o" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" title="o_O" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" title="O_o" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" title=":\'("/>',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" title=":-!" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" title=":-/" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" title=":-[" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-cool.gif" alt="8-)" title="8-)" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/beer_mug.gif" alt=":beer" title=":beer" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" title=":homebrew" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/coffee.gif" alt=":coffee" title=":coffee" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" title=":facepalm" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/like.gif" alt=":like" title=":like" />',
'<img class="smiley" src="' . app::get_baseurl() . '/images/dislike.gif" alt=":dislike" title=":dislike" />',
'<a href="http://friendica.com">~friendica <img class="smiley" src="' . app::get_baseurl() . '/images/friendica-16.png" alt="~friendica" title="~friendica" /></a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . app::get_baseurl() . '/images/rm-16.png" alt="red#" title="red#" />matrix</a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . app::get_baseurl() . '/images/rm-16.png" alt="red#matrix" title="red#matrix" />matrix</a>'
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" title="&lt;3" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;/3" title="&lt;/3" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-brokenheart.gif" alt="&lt;\\3" title="&lt;\\3" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-smile.gif" alt=":-)" title=":-)" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-wink.gif" alt=";-)" title=";-)" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-frown.gif" alt=":-(" title=":-(" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" title=":-P" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-p" title=":-P" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-kiss.gif" alt=":-\" title=":-\" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" title=":-x" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" title=":-X" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" title=":-D" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" title="8-|" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" title="8-O" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-surprised.gif" alt=":-O" title="8-O" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-thumbsup.gif" alt="\\o/" title="\\o/" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-Oo.gif" alt="o.O" title="o.O" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-Oo.gif" alt="O.o" title="O.o" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-Oo.gif" alt="o_O" title="o_O" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-Oo.gif" alt="O_o" title="O_o" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-cry.gif" alt=":\'(" title=":\'("/>',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-foot-in-mouth.gif" alt=":-!" title=":-!" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-undecided.gif" alt=":-/" title=":-/" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-embarassed.gif" alt=":-[" title=":-[" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-cool.gif" alt="8-)" title="8-)" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/beer_mug.gif" alt=":beer" title=":beer" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/beer_mug.gif" alt=":homebrew" title=":homebrew" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/coffee.gif" alt=":coffee" title=":coffee" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" title=":facepalm" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/like.gif" alt=":like" title=":like" />',
'<img class="smiley" src="' . App::get_baseurl() . '/images/dislike.gif" alt=":dislike" title=":dislike" />',
'<a href="http://friendica.com">~friendica <img class="smiley" src="' . App::get_baseurl() . '/images/friendica-16.png" alt="~friendica" title="~friendica" /></a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . App::get_baseurl() . '/images/rm-16.png" alt="red#" title="red#" />matrix</a>',
'<a href="http://redmatrix.me/">red<img class="smiley" src="' . App::get_baseurl() . '/images/rm-16.png" alt="red#matrix" title="red#matrix" />matrix</a>'
);

$params = array('texts' => $texts, 'icons' => $icons);
@@ -121,7 +123,7 @@ class Smilies {
*
* @param string $s
* @param boolean $sample
*
*
* @return string HML Output of the Smilie
*/
public static function replace($s, $sample = false) {
@@ -166,7 +168,7 @@ class Smilies {
*
* @param string $x
* @return string HTML Output
*
*
* @todo: Rework because it doesn't work correctly
*/
private function preg_heart($x) {
@@ -174,7 +176,7 @@ class Smilies {
return $x[0];
$t = '';
for($cnt = 0; $cnt < strlen($x[1]); $cnt ++)
$t .= '<img class="smiley" src="' . app::get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" />';
$t .= '<img class="smiley" src="' . App::get_baseurl() . '/images/smiley-heart.gif" alt="&lt;3" />';
$r = str_replace($x[0],$t,$x[0]);
return $r;
}


+ 8
- 4
include/acl_selectors.php View File

@@ -4,6 +4,8 @@
* @file include/acl_selectors.php
*/

use Friendica\App;

require_once "include/contact_selectors.php";
require_once "include/contact_widgets.php";
require_once "include/DirSearch.php";
@@ -543,7 +545,7 @@ function acl_lookup(App $a, $out_type = 'json') {

if ($type == '') {

$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
AND NOT (`network` IN ('%s', '%s'))
$sql_extra2
@@ -552,7 +554,7 @@ function acl_lookup(App $a, $out_type = 'json') {
dbesc(NETWORK_OSTATUS), dbesc(NETWORK_STATUSNET)
);
} elseif ($type == 'c') {
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive` AND `notify` != ''
AND NOT (`network` IN ('%s'))
$sql_extra2
@@ -562,7 +564,7 @@ function acl_lookup(App $a, $out_type = 'json') {
);
}
elseif ($type == 'm') {
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag` FROM `contact`
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr` FROM `contact`
WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` AND NOT `archive`
AND `network` IN ('%s','%s','%s')
$sql_extra2
@@ -573,7 +575,7 @@ function acl_lookup(App $a, $out_type = 'json') {
dbesc(NETWORK_DIASPORA)
);
} elseif ($type == 'a') {
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `forum`, `prv` FROM `contact`
$r = q("SELECT `id`, `name`, `nick`, `micro`, `network`, `url`, `attag`, `addr`, `forum`, `prv` FROM `contact`
WHERE `uid` = %d AND `pending` = 0
$sql_extra2
ORDER BY `name` ASC ",
@@ -617,6 +619,7 @@ function acl_lookup(App $a, $out_type = 'json') {
'network' => $g['network'],
'link' => $g['url'],
'nick' => htmlentities(($g['attag']) ? $g['attag'] : $g['nick']),
'addr' => htmlentities(($g['addr']) ? $g['addr'] : $g['url']),
'forum' => ((x($g, 'forum') || x($g, 'prv')) ? 1 : 0),
);
}
@@ -661,6 +664,7 @@ function acl_lookup(App $a, $out_type = 'json') {
'network' => $contact['network'],
'link' => $contact['url'],
'nick' => htmlentities($contact['nick'] ? : $contact['addr']),
'addr' => htmlentities(($contact['addr']) ? $contact['addr'] : $contact['url']),
'forum' => $contact['forum']
);
}


+ 723
- 25
include/api.php View File

@@ -6,7 +6,8 @@
* @todo Automatically detect if incoming data is HTML or BBCode
*/

use \Friendica\Core\Config;
use Friendica\App;
use Friendica\Core\Config;

require_once 'include/HTTPExceptions.php';
require_once 'include/bbcode.php';
@@ -525,6 +526,15 @@ $called_api = null;
}
}

if (is_null($user) && x($_GET, 'profileurl')) {
$user = dbesc(normalise_link($_GET['profileurl']));
$nick = $user;
$extra_query = "AND `contact`.`nurl` = '%s' ";
if (api_user() !== false) {
$extra_query .= "AND `contact`.`uid`=".intval(api_user());
}
}

if (is_null($user) AND ($a->argc > (count($called_api) - 1)) AND (count($called_api) > 0)) {
$argid = count($called_api);
list($user, $null) = explode(".", $a->argv[$argid]);
@@ -1400,6 +1410,7 @@ $called_api = null;

/// @TODO move to top of file or somewhere better
api_register_func('api/users/show','api_users_show');
api_register_func('api/externalprofile/show','api_users_show');

function api_users_search($type) {

@@ -3276,14 +3287,117 @@ $called_api = null;
api_register_func('api/oauth/request_token', 'api_oauth_request_token', false);
api_register_func('api/oauth/access_token', 'api_oauth_access_token', false);

function api_fr_photos_list($type) {

/**
* @brief delete a complete photoalbum with all containing photos from database through api
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photoalbum_delete($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
// input params
$album = (x($_REQUEST,'album') ? $_REQUEST['album'] : "");

// we do not allow calls without album string
if ($album == "") {
throw new BadRequestException("no albumname specified");
}
// check if album is existing
$r = q("SELECT DISTINCT `resource-id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'",
intval(api_user()),
dbesc($album));
if (!dbm::is_result($r))
throw new BadRequestException("album not available");

// function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore
// to the user and the contacts of the users (drop_items() performs the federation of the deletion to other networks
foreach ($r as $rr) {
$photo_item = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `resource-id` = '%s' AND `type` = 'photo'",
intval(local_user()),
dbesc($rr['resource-id'])
);

if (!dbm::is_result($photo_item)) {
throw new InternalServerErrorException("problem with deleting items occured");
}
drop_item($photo_item[0]['id'],false);
}

// now let's delete all photos from the album
$result = q("DELETE FROM `photo` WHERE `uid` = %d AND `album` = '%s'",
intval(api_user()),
dbesc($album));

// return success of deletion or error message
if ($result) {
$answer = array('result' => 'deleted', 'message' => 'album `' . $album . '` with all containing photos has been deleted.');
return api_format_data("photoalbum_delete", $type, array('$result' => $answer));
} else {
throw new InternalServerErrorException("unknown error - deleting from database failed");
}

}

/**
* @brief update the name of the album for all photos of an album
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photoalbum_update($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
// input params
$album = (x($_REQUEST,'album') ? $_REQUEST['album'] : "");
$album_new = (x($_REQUEST,'album_new') ? $_REQUEST['album_new'] : "");

// we do not allow calls without album string
if ($album == "") {
throw new BadRequestException("no albumname specified");
}
if ($album_new == "") {
throw new BadRequestException("no new albumname specified");
}
// check if album is existing
$r = q("SELECT `id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'",
intval(api_user()),
dbesc($album));
if (!dbm::is_result($r)) {
throw new BadRequestException("album not available");
}
// now let's update all photos to the albumname
$result = q("UPDATE `photo` SET `album` = '%s' WHERE `uid` = %d AND `album` = '%s'",
dbesc($album_new),
intval(api_user()),
dbesc($album));

// return success of updating or error message
if ($result) {
$answer = array('result' => 'updated', 'message' => 'album `' . $album . '` with all containing photos has been renamed to `' . $album_new . '`.');
return api_format_data("photoalbum_update", $type, array('$result' => $answer));
} else {
throw new InternalServerErrorException("unknown error - updating in database failed");
}
}


$r = q("SELECT `resource-id`, MAX(`scale`) AS `scale`, `album`, `filename`, `type`
FROM `photo`
WHERE `uid` = %d AND `album` != 'Contact Photos' GROUP BY `resource-id`, `album`, `filename`, `type`",
/**
* @brief list all photos of the authenticated user
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photos_list($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
$r = q("SELECT `resource-id`, MAX(scale) AS `scale`, `album`, `filename`, `type`, MAX(`created`) AS `created`,
MAX(`edited`) AS `edited`, MAX(`desc`) AS `desc` FROM `photo`
WHERE `uid` = %d AND `album` != 'Contact Photos' GROUP BY `resource-id`",
intval(local_user())
);
$typetoext = array(
@@ -3291,7 +3405,7 @@ $called_api = null;
'image/png' => 'png',
'image/gif' => 'gif'
);
$data = array('photo' => array());
$data = array('photo'=>array());
if (dbm::is_result($r)) {
foreach ($r as $rr) {
$photo = array();
@@ -3300,6 +3414,9 @@ $called_api = null;
$photo['filename'] = $rr['filename'];
$photo['type'] = $rr['type'];
$thumb = App::get_baseurl() . "/photo/" . $rr['resource-id'] . "-" . $rr['scale'] . "." . $typetoext[$rr['type']];
$photo['created'] = $rr['created'];
$photo['edited'] = $rr['edited'];
$photo['desc'] = $rr['desc'];

if ($type == "xml") {
$data['photo'][] = array("@attributes" => $photo, "1" => $thumb);
@@ -3312,26 +3429,563 @@ $called_api = null;
return api_format_data("photos", $type, $data);
}

/**
* @brief upload a new photo or change an existing photo
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photo_create_update($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
// input params
$photo_id = (x($_REQUEST, 'photo_id') ? $_REQUEST['photo_id'] : null);
$desc = (x($_REQUEST, 'desc') ? $_REQUEST['desc'] : (array_key_exists('desc', $_REQUEST) ? "" : null)); // extra check necessary to distinguish between 'not provided' and 'empty string'
$album = (x($_REQUEST,'album') ? $_REQUEST['album'] : null);
$album_new = (x($_REQUEST,'album_new') ? $_REQUEST['album_new'] : null);
$allow_cid = (x($_REQUEST, 'allow_cid') ? $_REQUEST['allow_cid'] : (array_key_exists('allow_cid', $_REQUEST) ? " " : null));
$deny_cid = (x($_REQUEST, 'deny_cid') ? $_REQUEST['deny_cid'] : (array_key_exists('deny_cid', $_REQUEST) ? " " : null));
$allow_gid = (x($_REQUEST, 'allow_gid') ? $_REQUEST['allow_gid'] : (array_key_exists('allow_gid', $_REQUEST) ? " " : null));
$deny_gid = (x($_REQUEST, 'deny_gid') ? $_REQUEST['deny_gid'] : (array_key_exists('deny_gid', $_REQUEST) ? " " : null));
$visibility = (x($_REQUEST, 'visibility') ? (($_REQUEST['visibility'] == "true" || $_REQUEST['visibility'] == 1) ? true : false) : false);

// do several checks on input parameters
// we do not allow calls without album string
if ($album == null) {
throw new BadRequestException("no albumname specified");
}
// if photo_id == null --> we are uploading a new photo
if ($photo_id == null) {
$mode = "create";

// error if no media posted in create-mode
if (!x($_FILES,'media')) {
// Output error
throw new BadRequestException("no media data submitted");
}

// album_new will be ignored in create-mode
$album_new = "";
} else {
$mode = "update";

// check if photo is existing in database
$r = q("SELECT `id` FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' AND `album` = '%s'",
intval(api_user()),
dbesc($photo_id),
dbesc($album));
if (!dbm::is_result($r)) {
throw new BadRequestException("photo not available");
}
}

// checks on acl strings provided by clients
$acl_input_error = false;
$acl_input_error |= check_acl_input($allow_cid);
$acl_input_error |= check_acl_input($deny_cid);
$acl_input_error |= check_acl_input($allow_gid);
$acl_input_error |= check_acl_input($deny_gid);
if ($acl_input_error) {
throw new BadRequestException("acl data invalid");
}
// now let's upload the new media in create-mode
if ($mode == "create") {
$media = $_FILES['media'];
$data = save_media_to_database("photo", $media, $type, $album, trim($allow_cid), trim($deny_cid), trim($allow_gid), trim($deny_gid), $desc, $visibility);

// return success of updating or error message
if (!is_null($data)) {
return api_format_data("photo_create", $type, $data);
} else {
throw new InternalServerErrorException("unknown error - uploading photo failed, see Friendica log for more information");
}
}

// now let's do the changes in update-mode
if ($mode == "update") {
$sql_extra = "";

if (!is_null($desc)) {
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`desc` = '$desc'";
}

if (!is_null($album_new)) {
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`album` = '$album_new'";
}

if (!is_null($allow_cid)) {
$allow_cid = trim($allow_cid);
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`allow_cid` = '$allow_cid'";
}

if (!is_null($deny_cid)) {
$deny_cid = trim($deny_cid);
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`deny_cid` = '$deny_cid'";
}

if (!is_null($allow_gid)) {
$allow_gid = trim($allow_gid);
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`allow_gid` = '$allow_gid'";
}

if (!is_null($deny_gid)) {
$deny_gid = trim($deny_gid);
$sql_extra .= (($sql_extra != "") ? " ," : "") . "`deny_gid` = '$deny_gid'";
}

$result = false;
if ($sql_extra != "") {
$nothingtodo = false;
$result = q("UPDATE `photo` SET %s, `edited`='%s' WHERE `uid` = %d AND `resource-id` = '%s' AND `album` = '%s'",
$sql_extra,
datetime_convert(), // update edited timestamp
intval(api_user()),
dbesc($photo_id),
dbesc($album));
} else {
$nothingtodo = true;
}

if (x($_FILES,'media')) {
$nothingtodo = false;
$media = $_FILES['media'];
$data = save_media_to_database("photo", $media, $type, $album, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $desc, 0, $visibility, $photo_id);
if (!is_null($data)) {
return api_format_data("photo_update", $type, $data);
}
}

// return success of updating or error message
if ($result) {
$answer = array('result' => 'updated', 'message' => 'Image id `' . $photo_id . '` has been updated.');
return api_format_data("photo_update", $type, array('$result' => $answer));
} else {
if ($nothingtodo) {
$answer = array('result' => 'cancelled', 'message' => 'Nothing to update for image id `' . $photo_id . '`.');
return api_format_data("photo_update", $type, array('$result' => $answer));
}
throw new InternalServerErrorException("unknown error - update photo entry in database failed");
}
}
throw new InternalServerErrorException("unknown error - this error on uploading or updating a photo should never happen");
}


/**
* @brief delete a single photo from the database through api
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photo_delete($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
// input params
$photo_id = (x($_REQUEST, 'photo_id') ? $_REQUEST['photo_id'] : null);

// do several checks on input parameters
// we do not allow calls without photo id
if ($photo_id == null) {
throw new BadRequestException("no photo_id specified");
}
// check if photo is existing in database
$r = q("SELECT `id` FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s'",
intval(api_user()),
dbesc($photo_id)
);
if (!dbm::is_result($r)) {
throw new BadRequestException("photo not available");
}
// now we can perform on the deletion of the photo
$result = q("DELETE FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s'",
intval(api_user()),
dbesc($photo_id));

// return success of deletion or error message
if ($result) {
// retrieve the id of the parent element (the photo element)
$photo_item = q("SELECT `id` FROM `item` WHERE `uid` = %d AND `resource-id` = '%s' AND `type` = 'photo'",
intval(local_user()),
dbesc($photo_id)
);

if (!dbm::is_result($photo_item)) {
throw new InternalServerErrorException("problem with deleting items occured");
}
// function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore
// to the user and the contacts of the users (drop_items() do all the necessary magic to avoid orphans in database and federate deletion)
drop_item($photo_item[0]['id'], false);

$answer = array('result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.');
return api_format_data("photo_delete", $type, array('$result' => $answer));
} else {
throw new InternalServerErrorException("unknown error on deleting photo from database table");
}
}


/**
* @brief returns the details of a specified photo id, if scale is given, returns the photo data in base 64
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_fr_photo_detail($type) {
if (api_user() === false) {
throw new ForbiddenException();
} elseif (!x($_REQUEST, 'photo_id')) {
}
if (!x($_REQUEST, 'photo_id')) {
throw new BadRequestException("No photo id.");
}

$scale = (x($_REQUEST, 'scale') ? intval($_REQUEST['scale']) : false);
$scale_sql = ($scale === false ? "" : sprintf("AND `scale`=%d",intval($scale)));
$data_sql = ($scale === false ? "" : "ANY_VALUE(`data`) AS data`,");

$r = q("SELECT %s ANY_VALUE(`resource-id`) AS `resource-id`, ANY_VALUE(`created`) AS `created`,
ANY_VALUE(`edited`) AS `edited`, ANY_VALUE(`title`) AS `title`, ANY_VALUE(`desc`) AS `desc`,
ANY_VALUE(`album`) AS `album`, ANY_VALUE(`filename`) AS `filename`, ANY_VALUE(`type`) AS `type`,
ANY_VALUE(`height`) AS `height`, ANY_VALUE(`width`) AS `width`, ANY_VALUE(`datasize`) AS `datasize`,
ANY_VALUE(`profile`) AS `profile`, min(`scale`) as minscale, max(`scale`) as maxscale
FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s",
$photo_id = $_REQUEST['photo_id'];

// prepare json/xml output with data from database for the requested photo
$data = prepare_photo_data($type, $scale, $photo_id);

return api_format_data("photo_detail", $type, $data);
}


/**
* @brief updates the profile image for the user (either a specified profile or the default profile)
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_account_update_profile_image($type) {
if (api_user() === false) {
throw new ForbiddenException();
}
// input params
$profileid = (x($_REQUEST, 'profile_id') ? $_REQUEST['profile_id'] : 0);

// error if image data is missing
if (!x($_FILES, 'image')) {
throw new BadRequestException("no media data submitted");
}

// check if specified profile id is valid
if ($profileid != 0) {
$r = q("SELECT `id` FROM `profile` WHERE `uid` = %d AND `id` = %d",